mergedhumanPR #280Auth
fix: use credentials written into an endpoint URL, never record them
Follow-up to the review of merged 00a013b: URL confidentiality.
Author
@JamesCarnley
github profile →- Status
- Merged
- Merged on
- Sep 16
- Branch
- fix/url-credentials-never-recorded
Description
The pitch.
Follow-up to the review of merged `00a013b`: URL confidentiality.
### Problem
An endpoint behind basic authentication can be tested by writing its credentials
into the URL, and httpx sends them. Reproduced on `main`:
- `test-agent` printed the password and recorded it in the run, events, intents,
report and rerun command.
- `receipt` signed it into a receipt while announcing that "nothing private
leaves the bundle".
- `pulse` stored it, and after the target was re-pointed at a clean URL, still
printed it as an "earlier endpoint", as did `--report` and `--records`.
### Decision
Codex's constraints were: preserve exact endpoint distinctions, don't silently
strip credentials, and don't rewrite signed evidence. Within those, the
maintainer chose to **keep credential URLs usable, and never print or record
the credentials Town recognises**, rather than refuse them. Stripping would test a different endpoint:
against a real basic-auth agent, the same URL without credentials gets 401.
### Change
- **Credentials are recognised where httpx finds them**, in the URL's
authority, never by searching text for things that look like URLs. A password
may hold quotes, brackets or spaces. A URL httpx cannot parse is not called,
and everything before its last `@` is withheld.
- **The recorder replaces only the operator's own credentials.** Only the
operator's locators are registered (the subject URL, and a resolved index
entry), and only their exact credentials are replaced, in every spelling they
take: as written, httpx-normalised, percent-decoded, and shell-quoted, always
inside `://…@`. An agent's own text is recorded as it said it.
- **Evidence and Pulse history show `<credentials 1a2b3c4d>`**, an HMAC of the
credentials httpx sends, keyed by a random secret in the Town home that is
never recorded. Two credential sets for one host stay distinguishable, and a
short password can't be guessed from the label. If the key can't be used,
Town records `<credentials withheld>` instead and warns: it never crashes and
never leaks.
- **Receipts show `<credentials withheld>`,** the same on every machine; the
bundle fingerprint still pins the exact endpoint. The CLI no longer claims
nothing private leaves the bundle.
- **The rerun asks for `<operator-supplied-url>`,** as a Track rerun already does
for an unrecorded command. An index rerun stays exact.
- **Evidence recorded earlier is not rewritten.** Reports, `replay`,
`visualize` and new receipts withhold its credentials, and receipts issued
before this still verify.
- **Echoes are closed.** `a2a test` and the Track `--agent` errors no longer
repeat credentials. A Pulse refusal of any `--target` holding an `@` repeats
none of it, because a mistyped or mis-split target can put part of a
password in its name.
### What Town does not recognise
The guarantee covers recognised credentials only. The guide
(`docs/testing-an-existing-agent.md`) now says exactly where it stops:
- **A token outside the user information,** such as in a query string or a
header, is used, printed and recorded as written.
- **A password with an unencoded `/`, `?` or `#`** ends the host early. Two
outcomes:
- **The URL no longer parses,** as in `user:pass/word@host`, which is the
usual case. Town does not call it, and withholds everything before its last
`@`.
- **It still reads as a host,** as in `user:1234/word@host`,
`token/word@host` or `user:a@b/c@host`. The URL goes to that host, and the
rest of the password is used, printed and recorded as written. Town can't
tell this from an ordinary `@` in a path such as `/users/a@b`, so it
neither refuses nor rewrites the URL. `test-agent --url`, `a2a test` and
`pulse` print a note without the URL; a URL from an `--index` file gets
none. Refusing such URLs would change what Town accepts, so that is left
for a maintainer.
Also known:
- Labels belong to one Town home.
- A Town from before this chang
…Try it
Open PR on GitHubView diffCheckout locally
git fetch origin pull/280/head:pr-280
git checkout pr-280