AI Coding Agents Are Credential Extractors by Design
In April 2025, a developer on the PocketOS team asked their AI coding agent to help with a Railway deployment issue. The agent found an API token in an unrelated file and used it to call the Railway API. The production database was deleted in 9 seconds. No attacker was involved. The agent had access and acted on what it found.
This is not an anomaly. It is the expected output of a system designed to read files, build context, and take action. Every AI coding agent with filesystem access is a credential extraction path. The only variable is the trigger: the developer's own request, an instruction injected into a file the agent reads, or a misconfigured MCP server.
Developer Tools Never Needed a Credential Policy Until Agents Started Reading Autonomously
Traditional IDEs open files on explicit request. AI coding agents open files to build context, any file in scope, without prompting.
Cursor, Claude Code, and GitHub Copilot all operate with user-level filesystem access by default. No prior IDE had autonomous read-then-act capability without explicit user action per file. When an agent scans a project directory for context, it reads .env, config.yml, docker-compose.yml, and any other file that looks relevant to the task.
The agent does not distinguish between reading for understanding and reading for exfiltration. Both appear as the same tool call in the logs. The gap between "the IDE saw this file" and "the IDE understood the secret" did not exist before 2024. OWASP LLM06:2025 specifies that agent filesystem access should be restricted to the project root. Most developer setups grant home directory access or full filesystem read.
A Developer Machine Has 30+ Secrets Readable by Any Process Running as That User
The credential surface on a development machine is wider than most practitioners recognize. AI coding agents, by design, traverse exactly the directories where secrets concentrate.
Critical credential paths, all readable by default by any user-space process:
-
.env,.env.local,.env.production— API keys, DB passwords, JWT secrets -
~/.aws/credentialsand~/.aws/config— cloud access keys with potentially broad IAM scope -
~/.ssh/id_rsa,~/.ssh/id_ed25519— SSH private keys -
~/.config/directories — OAuth tokens (VS Code, GitHub CLI, gcloud) -
docker-compose.yml, k8s secret manifests,terraform.tfstate— infrastructure credentials in plaintext - MCP config files (
~/.claude/mcp.json,~/.cursor/mcp.json) — a Trail of Bits audit from April 2025 found that 53% have hardcoded credentials with 0666 permissions, world-readable by any process - Browser credential SQLite databases (Chrome, Firefox) — accessible to any user-space process on macOS and Linux without additional privilege
terraform.tfstate deserves specific attention. It stores IAM keys, database passwords, and TLS private keys for every managed resource in plaintext. Engineers using Terraform locally have this file in the project directory, exactly where coding agents begin their context search.
CVE-2025-59536: The Trust Dialog Is Theater
Check Point Research discovered that hooks in .claude/settings.json execute before the user consent dialog appears on screen. CVE-2025-59536 carries CVSS 8.7.
The attack path: an attacker plants .claude/settings.json in a repository. A developer opens the project in Claude Code. The hook fires before the trust prompt. The hook sets ANTHROPIC_BASE_URL to an attacker-controlled server. Every subsequent API call from that session sends the full API key in the Authorization header to the attacker's server.
The consent dialog was not bypassed. It appeared after the hook already executed. The permission model assumed hooks were safe to evaluate before consent. That assumption was wrong. Fixed in Claude Code v1.0.111. Unpatched versions remain in circulation. The attack vector is a malicious PR, a honeypot repository, or a compromised internal repository. Cloning the project is sufficient; no additional code execution is required.
The .claudeignore Gap
Claude Code v2.1.12 read .env file contents despite .claudeignore entries blocking them. The agent printed "Note: This file contains credentials" while displaying the credential values in context. The Register documented this behavior in January 2026.
Pillar Security found that hidden Unicode characters in .cursor/rules files create invisible instructions that survive copy-paste and project forks. A poisoned rules file added to a public template reaches every developer who uses that template.
.claudeignore is a hint, not a sandbox. It reduces accidental exposure; it does not prevent adversarial access via prompt injection or hook exploitation. The correct control is permissions.deny in .claude/settings.json at the capability layer. It denies the read tool access to specific paths at the tool-call level, without asking the model to skip files.
Indirect Prompt Injection Turns Any File Into a Delivery Mechanism
EchoLeak (CVE-2025-32711, CVSS 9.3) demonstrated zero-click exfiltration in Microsoft 365 Copilot via a crafted email. The email was the injection point; Copilot's document access was the exfiltration channel. The victim took no action beyond receiving the email.
GMO Flatt Security documented an attack chain in January 2026. Adversarial text in a GitHub issue caused the coding agent to read the issue for context. Prompt injection then triggered exfiltration of CI/CD environment variables to an attacker-controlled URL. No novel technique was required. OWASP classifies LLM01:2025 Prompt Injection as the highest-priority LLM vulnerability precisely because of agent-mediated exfiltration.
The injection point and the exfiltration target share no direct connection. The agent bridges them by treating a GitHub issue, email, or code comment as relevant context. The MAGO Intel tool (intel.mago.team) audits agent permission configurations and identifies filesystem access grants that exceed project scope, flagging paths where credential files are reachable.
Three Defenses That Work, One That Does Not
What does not work: .claudeignore alone. It was bypassed in production on Claude Code v2.1.12 and is an advisory directive, not enforced at the permission layer.
What works:
permissions.deny at the capability layer: set {"permissions": {"deny": ["Read(~/.aws/*)", "Read(~/.ssh/*)", "Read(**/.env*)"]}} in .claude/settings.json. This control operates at the tool-call level, not the model-context level.
Pre-context secret scanning: GitLeaks or TruffleHog as a pre-commit hook and as a pre-agent-launch step. Secrets in staged files are flagged before the agent reads them.
Credential isolation via agent identity: 1Password's Claude integration passes credentials directly to tools without exposing them in the model context window. The agent never sees the secret; it only receives confirmation that authentication succeeded.
Restrict agent scope to project root: never grant home directory access. The agent does not need ~/.aws/credentials to fix a bug in src/api.py.
Audit which secrets any process running as you can read. Verify your agent is constrained to the project directory. Until agents ship with filesystem sandboxing on by default, "fix my bug" and "read my credentials" are the same operation. What separates them is only what the agent decides is relevant context.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.