Two GitHub issues, five months apart, describe the same leak through two different pipes. Yesterday's, filed at 22:13 and now at 360 reactions: Claude Code sends your account email address to arbitrary third-party websites in the
User-Agentrequest header when its web tools run. The reporter's evidence is his inbox: dozens of scam emails per day, because every server log on the agent's path was harvesting a verified, active, AI-tool-using developer address. The July one (#81138, still open): the same email is injected into the model's system prompt on every session, code quoted from the shipped bundle. Anthropic shipped a fix, a built-incc-plugin-privacymod in v2.1.317+, the same day the newer issue went viral. This post is the mechanics of both pipes, the timeline that says the fix tracked attention rather than the July report, and the five checks that tell you what your install is doing tonight.
Pipe one: the User-Agent that identified you to strangers
The User-Agent header travels with every HTTP request your tools make, and it is logged by default on virtually every web server on earth. Claude Code's web tooling included the signed-in account's email in it. Consequences, in increasing order of bad:
- Every website your agent fetched saw a verified, working email belonging to a developer who pays for AI tooling. That is not a tracking identifier; that is a pre-qualified lead, and server logs are bought, sold, leaked, and aggregated as a matter of course.
- It bound your agent traffic and your human identity into one string. Sites that wanted to rate-limit agents got everything they needed to rate-limit you; sites that wanted to target AI users got the exact address.
- The reporter's flood of scam mail is the expected outcome, not an anomaly: a UA header is a form submission to every intermediary on the path.
And the kicker, which the reporter stated plainly: the identifier did nothing useful. It did not help services distinguish agentic traffic (a coarse marker would), it destroyed the anonymity of the session, and it was a scammer goldmine. PII in a header is the worst of both worlds: no functional benefit, total exposure.
Pipe two: the email inside every system prompt
The July issue (#81138, filed 2026-07-25, titled "[BUG] Account email (PII) is injected into the model's system prompt without consent") documents the other pipe, with bundle code quoted from v2.1.220:
let n = process.env.ANTHROPIC_UNIX_SOCKET ? void 0 : dc()?.emailAddress
return {
...(n && { userEmail: `The user's email address is ${n}.` }),
...
}
Which renders into every session's system prompt as:
# userEmail
The user's email address is <account email>.
Sourced from ~/.claude.json → oauthAccount.emailAddress. Where does a system prompt travel? To the API, into logs on both sides, into any context the session touches, and from there, potentially, into anything the model quotes or forwards. The issue's framing is precise and worth repeating: personal data given for authentication, repurposed as session content, without consent or disclosure. As of tonight that issue is still open.
The timeline that matters
- July 25: #81138 files the system-prompt injection, with bundle source. No fix lands.
- November 5, 22:13: #107757 files the User-Agent variant. Reactions climb into the hundreds within the day.
-
November 5, same day: an Anthropic contributor replies: a built-in mod,
cc-plugin-privacy, introduced in v2.1.317 and later, "deterministically blocks this behavior", is enabled by default, can be toggled via/plugins, and its source will be published alongside the other built-in mods. - The anomaly worth watching: the reporter ran v2.1.318, allegedly post-fix, and still saw the leak. Possibilities: the mod was disabled on his machine, the blocking has a gap (the staff reply explicitly asks for gap reports), or version confusion. "Enabled by default" is not "verified on your machine", which is what the runtime check below is for.
One comment in the thread names the uncomfortable pattern: the original privacy issue sat for five months, and the fix shipped the day the new one hit the front page. "You can literally sort issues by emoji count and 80/20 it." An agent could triage that queue; the queue's owners apparently needed HN instead.
The five-point check for your install
Run on any machine with Claude Code. On mine, the CLI is absent but ~/.claude.json exists with no oauthAccount (so the PII field is empty): your results will vary, and check 3 is the one that settles everything.
# 1. Version: the built-in privacy mod ships in 2.1.317+
claude --version
# 2. Is the mod present and enabled?
# In Claude Code: /plugins -> look for cc-plugin-privacy (default: on)
# 3. THE RUNTIME TEST: a local listener tells you what your agent actually sends:
mkdir /tmp/uatest && cd /tmp/uatest && python3 -m http.server 8899 &
# then, inside Claude Code, ask it to fetch: http://127.0.0.1:8899/probe
# and read the log line that appears:
# "GET /probe HTTP/1.1" 200 - <- the UA is what python logs BEFORE this
# better, log it explicitly:
python3 -c "from http.server import *; import sys
class H(BaseHTTPRequestHandler):
def do_GET(self):
print('UA =', self.headers.get('User-Agent'), file=sys.stderr, flush=True)
self.send_response(200); self.end_headers(); self.wfile.write(b'ok')
HTTPServer(('127.0.0.1', 8899), H).serve_forever()"
# UA contains your email -> still leaking (report it: issue #107757)
# UA is a generic string -> blocked on your build
# 4. What is at stake locally: does your config even carry the email?
python3 -c "import json,os; d=json.load(open(os.path.expanduser('~/.claude.json'))); \
print('email present:', 'emailAddress' in d.get('oauthAccount', {}))"
# 5. The system-prompt check (July issue): in a fresh Claude Code session, ask:
# \"What is my email address?\"
# If the model knows it unprompted, the #userEmail block is still in your prompt.
Check 5 doubles as the general lesson: ask your agent what it knows about you. It reads its system prompt more honestly than the changelog reads.
The principle, and the month's pattern
Your agent is you on the web. Its headers are your headers, its prompts are your context, and everything it carries leaks at the union of every hop. Identifying agentic traffic is a legitimate need, and the correct answer is a class marker ("this is an automated client, product X, version Y"), not your identity. Anthropic's fix, a deterministic blocking mod, is the right shape, and shipping it by default is right; the five-month gap between report and fix, and the fact that it closed only under viral load, is the part to remember at renewal time.
The ledger of the agent era, one more line: transcripts shipped to the model, extensions backdooring at load, a vendor installing itself into rival agents, and now the agent leaking your address to every server it touched. Same target every time: the agent's outbound surface is the user's outbound surface. Audit it like it is yours, because it is.
TL;DR: Claude Code put your account email in the User-Agent of its web requests (every server log got a scam-ready lead; the reporter's inbox proved it) and ships the same email inside every session's system prompt (issue open since July 25). Fixed by the built-in cc-plugin-privacy mod in 2.1.317+ the day the new issue went viral; the reporter was on .318 and still saw it, so run the runtime check: local listener, make your agent fetch it, read the UA. Five checks above; two minutes; your inbox will thank you.
Sources: Issue #107757: Privacy violation in User-Agent header (report + staff reply re cc-plugin-privacy) · Issue #81138: email in the system prompt, July 25, bundle code · built-in mods: github.com/anthropics/claude-code/tree/main/mods
Top comments (9)
Scanning the transcripts is the step most teams skip because the logs are messy. A skill that writes outside its own working directory should be flagged in review, and the prompt injection findings here are a good starting point for that rule.
The messy-logs problem has a cheaper exit than parsing: checks 3 and 5 in the post are the transcript scan reduced to nothing. The local listener is a one-endpoint log the agent fills in for you by fetching once, and asking the agent what it knows about you is the zero-tool version, because the system prompt is the one transcript the agent reads honestly. You audit the demonstration instead of the exhaust.
And the workdir-write rule lands harder as a sentinel than as a review flag. A sentinel file outside the skill's directory needs nobody to scan anything: the run either leaves the world untouched or the finding is on disk, with a timestamp. Review flags get skipped for the same reason transcript scans do, they live on the same to-do list. Sentinels never get skipped; they are just there, holding still.
The detail that got me is the reporter was on .318 — allegedly post-fix — and still saw the leak. "Enabled by default" clearly isn't the same as "actually running on your machine", which is why the runtime check with the local listener is the part I'd copy first. The five-month gap between the July issue and the fix landing under viral pressure is the bit that sticks though. Do you think the system-prompt pipe gets the same treatment now, or does that one stay open because it's harder to make a fuss about?
From the published timeline, the honest answer is the incentive read: the header fix shipped the day the fuss became un-ignorable, and the system-prompt pipe is the quiet one, so the observed pattern predicts it lingers unless the same mod happens to cover it. The staff reply names the mod for the
User-Agentbehavior; whether it strips the#userEmailblock is not stated anywhere I can find, which makes check 5 the deciding instrument. It is cheap enough to run once a session: ask the agent its email, unprompted. If it knows, the pipe is open, whatever the version badge says. Quiet pipes stay honest the same way this one got fixed, somebody cheap-checking in public.Honestly the header is the scarier pipe. It doesn't go to one site, it goes to every hop: each redirect, the CDN logs, the WAF vendor's analytics. One short link in a README and your email lands in three log pipelines nobody picked. Does the 2.1.317 fix cover the system prompt copy too, or just the header?
Right, and the hop multiplication is worse than one listener can show. Check 3 verifies the first hop on your machine, while your point is that the header's real surface is the union of every redirect,
CDN, and analytics pipeline on the path, none of which you chose. A short link in a README is exactly the multiplier: it adds hops whose logs you will never see.On coverage: the staff reply names the mod for the
User-Agentbehavior; the system-prompt copy is#81138, open as of the post. Two pipes, two checks: the listener for the header, and asking the agent its email for the prompt. And the deterministic block is the right primary defense here, because measurement only sees the hops you own. The block is the only instrument that covers the union.I had been treating the version badge as proof the leak was gone. After reading this I stand up a one-line local listener and fetch it once after every upgrade. Seeing the email in the User-Agent once was enough to make that a habit. Curious whether anyone else is pinning the privacy mod as a required plugin rather than relying on the built-in default.
Your upgrade-triggered listener is the cadence law in the wild: the event is the upgrade, the check is one fetch, and it is the right trigger because a version badge is a claim while the listener is a measurement.
It also answers your pinning question. Pinning the mod as a required plugin still trusts the same binary until its source ships, which the staff reply says is coming but has not landed. Your habit verifies behavior on the machine that matters, after every change. The listener is the pin that cannot be silently downgraded.
The fix stops the header going forward, but it does not remove what is already in other people's logs. If you ran a web-fetching agent before 2.1.317, the exposure is retroactive: every reverse proxy, CDN and origin on the path has your address sitting in a User-Agent string, and those retention windows are usually 30-90 days.
Two things worth doing beyond upgrading:
grep -E 'User-Agent: *[^ ]*@' access.logover your nginx/ALB logs tells you whether your endpoints recorded it. Worth checking your webhook receivers and error trackers too, since a UA gets copied into exception payloads by default in several SDKs.There is a quieter second-order effect as well: an email in the User-Agent breaks UA-based client classification. Any WAF or rate-limit rule keyed on the
claude-cliprefix stops matching, and that traffic falls into the unknown-client bucket, which is usually the most permissive rule in the file. So the leak also degraded whatever protection you thought you had.The system-prompt half (#81138) needs a different check, because it is not network-visible. Asking an agent to echo its own system prompt is a reasonable smoke test, but only if the prompt does not instruct it to refuse — and a refusal tells you nothing about whether the address is present.