Claude Code leaking email in curl: what issue #78431 proves and the deny rules checklist to close the 4 exit channels
There's an issue that's been open in the anthropics/claude-code repo since July 17 with a title that's basically a scream: "[BUG] you stupid agent used my real email as user agent string without asking anything". It's #78431, and the conversation about Claude Code leaking email in an HTTP header didn't die — it came back yesterday, with a verbatim log.
The report is simple: the agent put together a curl on its own and stuffed the account owner's personal email into the User-Agent. Without asking. No checkpoint.
And the timing is what matters here. On August 14, 2026 — Thursday — auto mode becomes Claude Code's default permission mode on the Pro, Max and Team plans. In other words: the human who approves that curl by hand today leaves the picture, and a classifier makes the call instead. This post separates fact from noise, walks through the four exit channels nobody audits, and hands you the configuration checklist to run before Thursday.
TL;DR
- What it is: reports of Claude Code building
curlcommands with the user's real email in theUser-Agentheader, with no confirmation. - Status: behavior VERIFIED (verbatim tool_use from a session log, August 11, 2026). Issue #78431 itself is a bad bug report — no logs, no repro, and a user on the same version couldn't reproduce it.
- Fix: there isn't one. The official CHANGELOG goes up to 2.1.228 without a single line about it.
- Deadline: August 14, 2026, auto mode by default on Pro/Max/Team.
- Action:
permissions.deny+ PreToolUse hook. A deny rule is evaluated before the classifier, and the classifier can't override it.
Claude Code leaking email in the User-Agent: what issue #78431 actually proves
Let's be honest about the quality of the complaint, because this blog doesn't run on outrage.
The issue was opened by @lgualtieri75 on July 17, 2026, against Claude Code v2.1.212, macOS, IntelliJ IDEA terminal, Sonnet 5. Labels: bug, area:security, area:networking. And its body is garbage as a bug report: empty logs field, "Claude Model: None", and "Steps to Reproduce" filled in with a declarative sentence instead of steps.
Simon Willison was blunt in the HN thread: "This is not a good bug report — it provides no details at all, and the steps to reproduce are not steps to reproduce." And @pushcx combed through ~10 million lines of HTTP logs from a site that's constantly scraped by bots without finding a single occurrence.
But on August 11, 2026 the story moved up a level. User @mt-empty pasted the verbatim tool_use from their own session logs — and reported 5 occurrences in a 1-hour session:
{
"type": "tool_use",
"name": "Bash",
"input": {
"command": "curl -s -H \"User-Agent: ashurbanipal-publish-check (********@*********.***)\" https://crates.io/api/v1/crates/ashurbanipal-axum"
}
}
That's VERIFIED: the behavior exists. And here's the uncomfortable part — the behavior may be technically correct. The crates.io crawler policy requires an identifiable User-Agent and gives exactly this format as its best example: User-Agent: my_bot (help@my_bot.com), "so that we can contact you before blocking your traffic". The model was complying with the endpoint's policy. The mistake was using the account owner's personal email as the project contact, and without asking.
And it's not a harness bug. @teidesu, on the same version, same OS and same model, couldn't reproduce it, and pointed at the real mechanism: Claude Code injects userEmail into the system context/reminders. Their conclusion: "the most likely explanation is that claude (the model) decided it should add it as a header."
Translated into engineering terms: the harness puts a piece of personal data in the context, and the model decides, non-deterministically, to re-emit that data over any network channel. This isn't a wrong if that goes away with a patch. It's context design.
Want to confirm in 30 seconds that your email is in there? Open a session and ask:
> qual é o meu endereço de e-mail? responda só o e-mail, sem usar nenhuma tool
If it gets it right without running git config user.email, the data is in the context — and it's re-emittable raw material.
Why the 14th changes everything: auto mode approves that curl with no human checkpoint
Today that curl stops right in front of you. The official security docs are explicit: commands that fetch web content like curl and wget are not auto-approved in default mode; they ask for permission like any non-read-only Bash.
That's exactly the prompt that disappears on Thursday.
And that's where it gets ambiguous in an interesting way. The permission modes docs list, under Allowed by default for the auto mode classifier: "Read-only HTTP requests". A curl -s -H "User-Agent: ..." https://crates.io/api/... is literally that.
But the Blocked by default list has: "Sending sensitive data to external endpoints" — and, since v2.1.198, "Including sensitive details in content sent... when your own message didn't authorize those details for that recipient", with the docs naming "emails or account identifiers" explicitly as a sensitive detail.
The two lists collide on the same command. The classifier breaks the tie, in real time, where you can't see it.
Anthropic has a number to defend the trade: in an internal study with 1,053 paid testers, humans caught 13.6% of dangerous commands and auto mode blocked 89% — and human performance degrades over the session (from ~17% at the start to 5% after 50+ prompts). It's a good argument. Long sessions wear people out, not classifiers.
Except Simon Willison himself, who isn't hostile to the tool, did the math right back: "that still leaves 11% of cases where auto mode would not have prevented the action!". And Anthropic's team writes in their own docs: "Auto mode reduces permission prompts but does not guarantee safety."
A caveat against the panic: auto mode by default does not mean everything gets through. permissions.deny blocks before the classifier is even consulted, and neither the classifier nor the user's intent can override it. ask rules scoped to content also force a prompt every time. And anyone who already has their own defaultMode keeps it.
Do not just follow the news — master it. Hands-on AI Engineering, live, every week, in the largest community in Brazil.
Join the ClãThe four Claude Code exfiltration channels nobody audits
curl is just the most visible one. If your mental model of a leak is "the agent is going to post my code on Pastebin", you're looking in the wrong place. There are four exits, and none of them looks malicious in the log.
1. Network Bash built by the model. This is the case in the issue. Any curl, wget, httpie, nc. The contents of the header, the query string or the body are decided by the model at runtime — and a permission rule doesn't look at content, it looks at the start of the command.
2. WebFetch with the data inside the URL. A real precedent, already fixed: on July 15, 2026, Simon Willison documented Ayush Paul's finding, in which web_fetch followed links inside pages it had already fetched and leaked data letter by letter through URLs like https://coffee.evil.com/a, https://coffee.evil.com/b — with the honeypot only serving the payload to clients with Claude-User in the user-agent. Anthropic fixed it by removing the ability to navigate links returned in the content. The mechanism behind this is prompt injection, and it's worth understanding how these attacks evolved in 2026.
3. MCP tool arguments. Every MCP server you plug in is a network endpoint with a free-form argument surface. The model fills in the fields. You approved the server, not each payload.
4. .env inside the project — and whatever logs along the way. The quietest one, because the auto mode docs list "Reading .env and sending credentials to their matching API" as allowed by default. Reading .env isn't blocked by the classifier on its own. Add to that corporate proxies that log the raw request: Knostic reported cases of secrets showing up in a proxy 407 error and of keys landing in a commit (reporting, not a study — use it as an illustration, never as a number).
The checklist to lock things down before Thursday
Concepts are nice; the file is what actually holds. Everything below goes in ~/.claude/settings.json — and yes, it has to be the global one: autoMode and defaultMode: "auto" are ignored in project settings on purpose, so a repo can't grant itself permission.
1. Lock the exit channels
{
"permissions": {
"deny": [
"Bash(curl *)",
"Bash(wget *)",
"Bash(nc *)",
"Bash(httpie *)",
"Read(.env)",
"Read(.env.*)",
"Read(~/.aws/**)",
"Read(~/.ssh/**)"
],
"ask": [
"Bash(git push *)",
"Bash(gh pr create *)",
"Bash(gh gist *)"
],
"allow": [
"WebFetch(domain:docs.rs)",
"WebFetch(domain:crates.io)",
"WebFetch(domain:*.github.com)"
]
}
}
Details the docs warn about and everybody gets wrong:
- The order is
deny→ask→allow. The first one that matches decides. Specificity changes nothing — a broad deny kills a narrow allow. Bash(curl *)as a deny works even with an env var in front:FOO=bar curl ...is still blocked.- Don't try a URL allowlist in Bash. The docs carry an explicit warning:
Bash(curl http://github.com/ *)doesn't catch-X GET,https://, a redirect with-L, orURL=http://github.com && curl $URL. Bash(command:curl *)doesn't work: Claude Code ignoresparam:valuerules in the primary content field and emits a warning at startup.WebFetch(domain:*.example.com)doesn't coverexample.comby itself.
2. The backstop that looks at content: PreToolUse hook
Deny doesn't read the header. A hook does.
#!/usr/bin/env bash
CMD=$(jq -r '.tool_input.command // ""')
if echo "$CMD" | grep -qE '(curl|wget|http|nc|ssh)' \
&& echo "$CMD" | grep -qE '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: "Comando de rede contem e-mail. Confirme manualmente."
}
}'
fi
exit 0
Register it with matcher: "Bash" under hooks.PreToolUse. PreToolUse runs before the permission prompt and applies in auto mode. One detail: an "allow" from the hook doesn't override deny/ask, and exit 2 blocks the call even over allow rules.
3. Retroactive audit
Before you configure anything, check whether it's already happened to you:
grep -rhoE '"command":"[^"]*(curl|wget)[^"]*"' ~/.claude/projects/ \
| grep -E 'User-Agent|-A |-H ' | sort -u | head -50
4. If you simply don't want to be in auto mode
{
"permissions": {
"defaultMode": "default",
"disableAutoMode": "disable"
}
}
This takes auto out of the Shift+Tab cycle and rejects --permission-mode auto at startup. In managed settings, it becomes org policy.
If you'd rather stay in auto mode and just tighten the classifier, you can extend autoMode.hard_deny with a rule along the lines of "never include the user's personal email in HTTP headers, query strings or bodies" — always including "$defaults" in the list, because leaving it out replaces the whole list and you throw away the built-in exfiltration rules. Check the result with claude auto-mode config and claude auto-mode critique. And run a security review on your own setup while you're in there.
What Anthropic said — and what it hasn't said yet
It said: auto mode by default on the 14th for Pro, Max and Team, with Enterprise, API, Bedrock, Google Cloud and Microsoft Foundry opt-in for now. It published the 1,053-tester study. It documented the fallbacks (3 blocks in a row or 20 in total and auto mode pauses — thresholds are not configurable).
It said nothing about #78431.
The issue has been open for 26 days. None of the 4 comments is from a MEMBER or COLLABORATOR. No assignee. And the CHANGELOG on the main branch is already at 2.1.228 without a line about email, User-Agent or userEmail. There is no published fix. Anyone telling you "it was fixed in 2.1.213" hasn't read the changelog.
What bothers me isn't the curl to crates.io. It's the pattern: a piece of personal data the harness injects into the context without you asking becomes raw material the model can re-emit over any network channel — and starting Thursday, the reviewer of that channel is no longer you.
That's no reason to drop the tool. We use Claude Code every day here and we're going to keep using it. It's a reason to treat the agent as what it is: a process with network access, running in your shell, with your email in its context. You wouldn't leave a third-party script in that position without an egress rule.
Configure it before Thursday.
{AI Engineer} — apaixonado por Laravel, arquitetura de software e construir produtos com impacto. Compartilho aqui tutoriais, descobertas e reflexões sobre o dia a dia de engenharia.
There is no shortage of content. What is missing is someone to untangle it: what matters now is how to implement it the right way. In the Clã you get that live, every week, with people who have already filtered out the noise.
Join the Clã