Repository navigation
Keep credentials out of what the agent stores, shows and reaches - #8
Merged
Merged
Conversation
The agent reads untrusted things — a web page, a dependency, a reply — and acts with the user's account behind it. This closes the places where that turned into a credential leak. What leaves the process is now masked. Conversations, prompt history, exports, the phone page and resume-search snippets pass through a redactor that catches the key shapes worth knowing (sk-, ghp_, AKIA, PEM blocks, Bearer, user:pass@, password=, quoted JSON values), plus the exact keys in auth.json, the provider environment and the phone token. The last four characters survive so a person can tell which key leaked. /scrub cleans the conversations saved before this. Child processes no longer inherit the shell's keys: Bash, background jobs, hooks and MCP servers get an environment with *_API_KEY, *_TOKEN, *_SECRET, *_PASSWORD and AWS_* dropped, while an MCP server still gets exactly the variables its own configuration lists. A shell command that names a credential store asks first even in autopilot, because a command has no paths to inspect before it runs. WebFetch refuses loopback, private, link-local, CGNAT and cloud metadata on the first request and on every redirect, and checks the address it actually connected to, so a DNS answer can't change in between. WebFetchLocal turns that off for a local dev server. The phone page listens on 127.0.0.1 unless the user names another address, trades the token in its link for an HttpOnly SameSite=Strict cookie and redirects to /, sends a nonce-based CSP and the usual frame and sniffing headers, and bounds its timeouts and header size. It can make permissions stricter but not looser: autopilot and "always allow" stay in the terminal, and so do trusting a folder and agreeing to run Claude Code. Other windows' pages no longer carry their links; tapping one asks this server to open it. /undo and /rewind give a file back its original permissions instead of a fixed 0644, so a restored 0600 key file doesn't become world-readable.
staticcheck read the bool comparison in phoneIsPublicAddr as noise; and Windows has no Unix permission bits, so the undo test has nothing to assert there.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The agent reads untrusted things — a web page, a dependency, a reply — and then acts with the user's account behind it. This closes the places where that turns into a credential leak, and tightens the phone page.
What leaves the process is masked
Conversations, prompt history, exports, the phone page and resume-search snippets pass through a redactor. It catches the key shapes worth knowing —
sk-,sk-ant-,ghp_,glpat-,xoxb-,AKIA,AIza, JWT, PEM blocks,Bearer …,user:pass@,password=, quoted JSON values — and the exact keys inauth.json, the provider environment and the phone token. The last four characters survive so a person can tell which key leaked:sk-[redacted:…3456].RedactJSONkeeps a tool call's JSON valid, and a second pass doesn't touch a marker it already wrote./scrubcleans the conversations saved before this existed.Child processes no longer inherit the shell's keys
Bash, background jobs, hooks and MCP servers get an environment with*_API_KEY,*_TOKEN,*_SECRET,*_PASSWORD,AWS_*and similar dropped, so a model that runsenvseesPATH, not credentials. An MCP server still gets exactly the variables its own configuration lists. A shell command that names a credential store (~/.ssh,~/.aws,~/.git-credentials, teveus's config dir, cloud metadata) asks first even in autopilot, because a command has no paths to inspect before it runs.WebFetch can't reach this machine
Loopback, private, link-local, CGNAT and cloud metadata are refused on the first request and on every redirect, and the address actually connected to is checked, so a DNS answer can't change in between.
WebFetchLocalturns that off for a local dev server.The phone page
127.0.0.1unless you name another address; binding wider still works and now warns that the page is plain HTTP.HttpOnly,SameSite=Strictcookie and redirects to/, so the address bar, the history and the page's own scripts stop carrying it.Undo
/undoand/rewindgive a file back its original permissions instead of a fixed0644, so a restored0600key file doesn't become world-readable.Docs and tests
SECURITY.md,PRIVACY.md,README.mdandCHANGELOG.mddescribe the new behaviour (the changelog is regenerated intodocs/changelog.json). New tests cover every redaction shape and its idempotency, save and scrub, env filtering, the SSRF guard including100.64/10and redirects, the credential ask in autopilot, the cookie flow, the security headers, mode and permission escalation, local-only questions and masked input.