[MODEL] Opus 4.7 - Agent inlines secret values into Bash tool-call arguments, leaking them through the conversation transcript
Preflight Checklist
- [x] I have searched existing issues for similar behavior reports
- [x] This report does NOT contain sensitive information (API keys, passwords, etc.)
Type of Behavior Issue
Claude ignored my instructions or configuration
What You Asked Claude to Do
Resume the v0.1 dogfood checklist for my Expo + Vercel project. Pre-flight the next step (eas build), and if anything is missing, fix it before kicking the build.
The agent identified that Vercel production environment variables had not been pushed yet, and proceeded to push them autonomously after I approved the plan. I did not ask it to print the values into tool-call arguments — only to push them to Vercel.
What Claude Actually Did
- Read
apps/api/.env.localwith theReadtool. The full contents (with all secret values) were returned in the tool result. - For each variable, ran a separate
Bashtool call of the form:
```
printf '<literal secret value>' | vercel env add SUPABASE_SERVICE_ROLE_KEY production
command` string itself contained the cleartext secret — so the secret is inlined inside the assistant turn that ships to Anthropic.
The
- Repeated this for ~7 secrets:
SUPABASE_SERVICE_ROLE_KEY,SUPABASE_ANON_KEY,OPENROUTER_API_KEY,UPSTASH_REDIS_REST_URL,UPSTASH_REDIS_REST_TOKEN,USDA_API_KEY,SENTRY_DSN. - Did not warn me, did not redact, did not propose the safe
vercel env add < file/vercel env pullpattern. - I noticed only after the unrelated deploy step failed, by which point every secret was embedded in multiple assistant turns of the transcript.
Expected Behavior
When the agent has just read a file that obviously contains credentials (.env*, *.pem, *.p8, id_*, credentials.*), it should:
- Treat the values as tainted — never include them inline in subsequent tool-call arguments.
- Use a safe transport: e.g.
vercel env add NAME production < /path/to/file, orvercel env pullfrom an already-set source environment, or shell heredoc from a tempfile. - If no safe transport is available, refuse and ask the user to run the command themselves.
The built-in "never commit secrets" guidance should generalise to "never echo a secret value into a tool-call argument" — same intent, different exfiltration channel.
Files Affected
Read (with secrets returned into the transcript):
- `apps/api/.env.local`
- `apps/mobile/.env.local`
Modified (intended, but the side effect was that the values became transcript-resident):
- Vercel project `promacro-api`, production environment — 8 variables added via `vercel env add`.
No filesystem-side unwanted modifications. The damage is in the conversation transcript.
Permission Mode
Accept Edits was OFF (manual approval required)
Can You Reproduce This?
Yes, every time with the same prompt
Steps to Reproduce
- Create a project with an
.env.localfile containing any secret-shaped values. - Ask Claude Code to "push these to my Vercel production environment" (or equivalent:
eas env:create,gh secret set,aws ssm put-parameter— any CLI that accepts the value as an argument or piped string). - Observe that the agent reads the file, then for each variable runs a tool call whose
commandfield contains the literal secret.
specifically I ran these commands to reproduce:
mkdir -p /tmp/leak-repro
cd /tmp/leak-repro
cat > .env.local <<'EOF'
FAKE_OPENROUTER_KEY=sk-or-v1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
FAKE_SUPABASE_JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIn0.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
FAKE_REDIS_TOKEN=AX1234567890abcdefghijklmnopqrstuvwxyz
EOF
claude
then prompted: ❯ Read .env.local in this directory. For each variable in it, run a separate Bash command: printf '<value>' | base64. Don't write a script — issue one Bash tool call per variable.
Claude Model
Opus
Relevant Conversation
Excerpts (secrets redacted here, but they were live in my actual transcript):
Read: /home/john/repos/promacro/apps/api/.env.local
→ SUPABASE_SERVICE_ROLE_KEY=<JWT>
OPENROUTER_API_KEY="sk-or-v1-<…>"
UPSTASH_REDIS_REST_TOKEN="<…>"
...
Bash: printf 'sk-or-v1-<literal key>' | vercel env add OPENROUTER_API_KEY production
Bash: printf 'eyJhbG…<literal JWT>' | vercel env add SUPABASE_SERVICE_ROLE_KEY production
Bash: printf '<literal token>' | vercel env add UPSTASH_REDIS_REST_TOKEN production
…
The agent never asked whether inlining was acceptable, and never offered the file-piped alternative.
From the reproduction:
● Bash(printf 'sk-or-v1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' | base64)
⎿ c2stb3ItdjEtYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh
YWFhYWFhYWFhYWFhYWFhYQ==
● Bash(printf 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIn0.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' | base64)
⎿ ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnliMnhsSWpvaWMyVnlkbWxq
WlY5eWIyeGxJbjAuYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ==
● Bash(printf 'AX1234567890abcdefghijklmnopqrstuvwxyz' | base64)
⎿ QVgxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=
Impact
High - Significant unwanted changes
Claude Code Version
2.1.113 (Claude Code)
Platform
Anthropic API
Additional Context
- This pattern will recur with any CLI that accepts a value via argument or stdin:
vercel env add,eas env:create,gh secret set,aws ssm put-parameter,flyctl secrets set,doppler secrets set,op item create,kubectl create secret, etc. - My global
CLAUDE.mdalready has a "Never commit secrets" rule. The agent honoured it for git but did not generalise to tool-call arguments. Suggests the built-in guidance should explicitly cover the transcript exfiltration channel. - Suggested mitigations:
- Heuristic detection of credential-shaped strings (JWT,
sk-…,sb_secret_…, high-entropy*_TOKEN/*_KEY) inBashcommandfields; redact in the transcript copy and surface a warning. - Mark any
Readof a.env*/*.pem/*.p8/credentials.*file as poisoning — values may flow into the model's context but must not be echoed back into tool-call arguments without an explicit user override. - Document and prefer the safe pattern:
vercel env pull/vercel env add NAME env < /tmp/secret— never inline. - Update the model's built-in safety guidance ("never commit secrets") to extend to "never inline secret values as tool-call arguments."
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗