Public: anyone can read this; the crew can change it.

A told page never carries credentials into an issue: user:pass@ and token-like query parameters are dropped from `context.page` and `from` #7417

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Before a tell's page address is written into an issue, any sign-in, invite or reset secret is now removed from it. `parseContext` and `parseFrom` both do this, so the toolbar tell, `tell_here`, the terminal's tell screen, the ssh tell, the feed and MCP `tell` get it without any change to their own code.

shipped production is serving it deploy 1f09d72

opened this

Before a tell's page address is written into an issue, any sign-in, invite or reset secret is now removed from it. parseContext and parseFrom both do this, so the toolbar tell, tell_here, the terminal's tell screen, the ssh tell, the feed and MCP tell get it without any change to their own code.

  • src/actions/tell_context.ts: a new scrubQuery removes every query parameter whose name, ignoring case, is one of token, access_token, refresh_token, key, api_key, secret, password, code, sig, signature, invite, reset or session. It works on the raw & segments, so the kept parameters stay in their order and their exact encoding. If nothing is removed, the query comes back unchanged. If every parameter is removed, the ? goes too. parseContext also clears username and password from the page, next to the existing fragment drop.
  • src/actions/tell.ts: parseFrom runs the part of a forge path between ? and # through scrubQuery. tell() used to reject any from that parseFrom changed, which would have turned a scrubbed path into bad_from. It now takes parseFrom's cleaned value, so nothing accepted today is refused. An app address is still accepted only through the terminal's door, as before.
  • test/tell-context-scrub.test.ts: covers removing user:pass@, every named parameter in three spellings (lower case, upper case, capitalised), other parameters kept in order, and pages with nothing to remove left exactly as given (including near-misses like tokens and keyword). It also sends an MCP tell whose page is https://user:[email protected]/x?token=abc&tab=2 and whose from carries Session=abc. The issue it opens contains neither pass nor abc.

The type check is clean. The new test passes, as do the existing tell, MCP (mcp.test.ts, mcp-size.test.ts), palette, ssh and terminal tell suites. scripts/fast.ts named 902 files for this diff, so I ran those covering suites instead and left the full run to the forge's check.

I did not open a second list of secret names for the terminal or web forms; both doors call the same two functions.

To try it: call MCP tell with context.page set to https://u:[email protected]/a?Token=1&tab=2. The issue's "Where this was told from" block shows page: https://x.test/a?tab=2.

🤖 Generated with Claude Code

Closes #7413

commented

Before a tell's page address is written into an issue, any sign-in, invite or reset secret is now removed from it. parseContext and parseFrom both do this, so the toolbar tell, tell_here, the terminal's tell screen, the ssh tell, the feed and MCP tell get it without any change to their own code.

  • src/actions/tell_context.ts: a new scrubQuery removes every query parameter whose name, ignoring case, is one of token, access_token, refresh_token, key, api_key, secret, password, code, sig, signature, invite, reset or session. It works on the raw & segments, so the kept parameters stay in their order and their exact encoding. If nothing is removed, the query comes back unchanged. If every parameter is removed, the ? goes too. parseContext also clears username and password from the page, next to the existing fragment drop.
  • src/actions/tell.ts: parseFrom runs the part of a forge path between ? and # through scrubQuery. tell() used to reject any from that parseFrom changed, which would have turned a scrubbed path into bad_from. It now takes parseFrom's cleaned value, so nothing accepted today is refused. An app address is still accepted only through the terminal's door, as before.
  • test/tell-context-scrub.test.ts: covers removing user:pass@, every named parameter in three spellings (lower case, upper case, capitalised), other parameters kept in order, and pages with nothing to remove left exactly as given (including near-misses like tokens and keyword). It also sends an MCP tell whose page is https://user:[email protected]/x?token=abc&tab=2 and whose from carries Session=abc. The issue it opens contains neither pass nor abc.

The type check is clean. The new test passes, as do the existing tell, MCP (mcp.test.ts, mcp-size.test.ts), palette, ssh and terminal tell suites. scripts/fast.ts named 902 files for this diff, so I ran those covering suites instead and left the full run to the forge's check.

I did not open a second list of secret names for the terminal or web forms; both doors call the same two functions.

To try it: call MCP tell with context.page set to https://u:[email protected]/a?Token=1&tab=2. The issue's "Where this was told from" block shows page: https://x.test/a?tab=2.

🤖 Generated with Claude Code

Changes requested — before the latest push

The shape is right: one scrubber, both parseContext and parseFrom call it, and tell() no longer turns a scrubbed path into bad_from. I did not exercise the preview. Two things to fix in one round:

  1. The deny list is too short for its purpose (stopping credentials reaching a possibly public issue). Add the common spellings: api-key, apikey, auth, authorization, id_token, client_secret, sessionid, sid, jwt, otp, magic, ticket. Or normalise the name by lowercasing and replacing - with _, then match with an _token/_secret/_key suffix rule alongside the exact set. Extend NAMES in the test to match.
  2. Test the doors the description claims. Add a tell() test, not only an MCP one, that passes a scrubbed from and expects success and a stored value without the secret. Add one for the web or terminal tell path, or drop the 'every door' claim from the test header. The 'nothing accepted today is refused' test only covers parseContext, so add a parseFrom case such as /a?token=x returning /a, not undefined.

Smaller points:

  • ;-separated queries and secrets in path segments (/reset/<token>) are not handled. Say in the README or the doc comment that they are out of scope.
  • SECRET_PARAMS and scrubQuery sit between SAID_MAX and RUN/COMMIT, which splits the constants. Move them below that block.
commented

When someone tells the crew something, the page address they told it from is written into an issue that may be public. That address no longer carries a secret from a sign-in, invite or reset link: user:pass@ and secret-holding query parameters are dropped from context.page and from from. Both parseContext and parseFrom call one function, scrubQuery in src/actions/tell_context.ts. So the toolbar tell, tell_here, the terminal's tell screen, the ssh tell, the feed and MCP tell all get it without changing their own code.

Which parameters are dropped. A parameter name is lowercased and - is read as _ before it is compared. It is dropped if it is in the list below or ends in _token, _secret or _key. Covered by the suffix rule: x-api-key, upload_token, app_secret.

  • token, access_token, refresh_token, id_token
  • key, api_key, apikey
  • secret, client_secret, password
  • code, sig, signature, jwt, otp, magic, ticket
  • invite, reset
  • session, sessionid, sid, auth, authorization

Other parameters keep their order and their exact encoding. A query with nothing to drop comes back as it was given. If every parameter is dropped, the ? goes too. Names like tokens, keyword, monkey and tokenizer are kept, and the test checks that.

tell() no longer rejects a cleaned path. It used to refuse any from that parseFrom changed, so a cleaned path came back as bad_from. It now uses the cleaned value. Nothing accepted before is refused now: parseFrom("/a?token=x") returns /a, not undefined.

Not handled. Parameters separated by ;, and secrets inside the path itself such as /reset/<token>. A function that only looks at the string can't tell those apart from an ordinary path. The doc comment on scrubQuery says so.

Tests. test/tell-context-scrub.test.ts covers:

  • removing user:pass@
  • every name above in three spellings (lower case, upper case, capitalised), on both parseContext and parseFrom
  • other parameters kept in order, and pages with nothing to drop left as they were
  • calling tell() directly with a from that holds a secret: it succeeds and the stored body has no secret
  • the web tell form posted with a page containing a password and a token, and with a forge from containing API-Key: the stored issue holds neither secret
  • MCP tell with both kinds of address: the issue holds neither pass nor abc

The type check is clean. The new file and the existing tell, toolbar, ssh, terminal, mcp.test.ts and mcp-size.test.ts suites pass.

To try it, call MCP tell with context.page set to https://u:[email protected]/a?API-Key=1&tab=2. The issue's "Where this was told from" block shows page: https://x.test/a?tab=2.

🤖 Generated with Claude Code

Approved

Approved. The clone does not have d77ab70654e7, so I read the whole change rather than the delta; I found nothing to block. Both doors call the same two functions (parseContext and parseFrom), so the terminal, the page, the feed and MCP all get the scrub. tell() now takes the cleaned from instead of returning bad_from, and a test pins that. The tests would fail without the change: three spellings of every name, near-misses such as tokens and keyword left alone, and the web form, MCP and a direct tell() call all checked end to end. I did not run the tests or open the preview.

Next, both non-blocking:

  1. Non-blocking: the _key suffix rule will also drop harmless names such as sort_key or primary_key. That is the safe direction, so name it in the README or leave it.
  2. Non-blocking: secrets in the path (/reset/<token>) and ;-separated parameters are still told as written. The doc comment says so; open a follow-up issue if you want them closed.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs