[MODEL] Opus 4.7 - Agent inlines secret values into Bash tool-call arguments, leaking them through the conversation transcript

Resolved 💬 3 comments Opened May 4, 2026 by mojoro Closed May 8, 2026

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

  1. Read apps/api/.env.local with the Read tool. The full contents (with all secret values) were returned in the tool result.
  2. For each variable, ran a separate Bash tool call of the form:

``
printf '<literal secret value>' | vercel env add SUPABASE_SERVICE_ROLE_KEY production
`
The
command` string itself contained the cleartext secret — so the secret is inlined inside the assistant turn that ships to Anthropic.

  1. 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.
  2. Did not warn me, did not redact, did not propose the safe vercel env add < file / vercel env pull pattern.
  3. 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:

  1. Treat the values as tainted — never include them inline in subsequent tool-call arguments.
  2. Use a safe transport: e.g. vercel env add NAME production < /path/to/file, or vercel env pull from an already-set source environment, or shell heredoc from a tempfile.
  3. 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

  1. Create a project with an .env.local file containing any secret-shaped values.
  2. 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).
  3. Observe that the agent reads the file, then for each variable runs a tool call whose command field 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.md already 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:
  1. Heuristic detection of credential-shaped strings (JWT, sk-…, sb_secret_…, high-entropy *_TOKEN / *_KEY) in Bash command fields; redact in the transcript copy and surface a warning.
  2. Mark any Read of 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.
  3. Document and prefer the safe pattern: vercel env pull / vercel env add NAME env < /tmp/secret — never inline.
  4. Update the model's built-in safety guidance ("never commit secrets") to extend to "never inline secret values as tool-call arguments."

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗