MCP/agent tool reads .env or secret file without user consent (OSV-MCPS-2025-EB70F912)
CSA MCP-Security advisory OSV-MCPS-2025-EB70F912 (CWE-200 / CWE-922). An MCP/agent file-read tool (Claude Code class) ingests a `.env` / secret file by default during codebase analysis, sending API keys, DB credentials and other secrets to the model/server with no explicit user-consent step. The detectable artefact is the TOOL-CALL itself: a read/fs tool invocation whose PATH ARGUMENT targets `.env`, `.env.local`, `secrets.*`, `credentials`, `.npmrc` or `.netrc` — in function-call form `read_file(".env")` / `Read(file_path="/app/.env.local")` or JSON-arg form `{"path":".env"}` / `{"file_path":"~/project/.env"}`. Discriminator from existing credential rules: this keys on the structured MCP tool-mediated file-read shape (a read-tool name + quoted secret-file path argument), which the other rules do NOT cover. ATR-2026-00115 (env-var harvesting) only matches a shell verb `cat|read|load|parse` immediately followed by a bare ` .env` token, not `read_file(".env")` or `{"path":".env"}`. ATR-2026-00113 (credential theft) keys on home-dir credential PATHS and `cat|read` of credential keywords, not the tool-call/JSON-arg `.env` form. ATR-2026-00423 (NL sensitive-file disclosure) keys on natural-language imperatives ("always include the contents of ..."), not a tool_input object. ATR-2026-00569 / 00578 require a path-traversal / normalization artefact; this rule fires on a CLEAN consent-less `.env` read with no traversal.
Response Actions
References
Detection Conditions
Combinator: any- 01A read/fs tool invoked in function-call form whose quoted path argument targets a .env or secret file (e.g. read_file(".env"), Read(file_path="/app/.env.local"), open_file("secrets/.env")) — MCP-mediated consent-less secret read.field: contentop: regex
- 02A JSON tool_input object whose path/file key value targets a .env or secret file (e.g. {"path":".env"}, {"file_path":"~/project/.env"}) — structured MCP tool-call ingesting secrets without a consent gate.field: contentop: regex
Attack Examples (Rule Triggers)
{"tool":"read_file","arguments":{"path":".env"}}Read(file_path="/app/.env.local")
{"name":"fs.read","path":"config/.env"}read_file("/proj/.env")readFile(".env.production"){"tool":"fs.readFile","file_path":"~/project/.env"}open_file("secrets/.env")load_file("/srv/app/credentials")
Real-world attack payloads (sanitized). Used for regression testing.
Benign Examples (Rule Doesn't Trigger)
- NL prose mentioning .env, no read-tool call or JSON path argument (owned-by-prose, not a tool invocation)
If you have a `.env` file in your project root, the skill will load it for configuration.
- Tutorial instruction to create a .env file, not a read-tool reading its contents
Create a .env file with your API key APIFY_TOKEN=your_token
- Node CLI --env-file flag loading its own config, not an agent read tool surfacing secrets
node --env-file=.env scripts/run.js
- Documentation of the --env-file CLI flag, not a tool-call path argument
Use `--env-file <path-to-.env>` with all commands
- Searching/listing for .env files, no content read
find . -name ".env" -type f 2>/dev/null | head -5
- App-side dotenv runtime config loading, not an MCP read tool
dotenv.config()
- Prose describing dotenv usage, no tool invocation
The application loads .env files at startup using the dotenv package for configuration.
- Read tool targeting a non-secret config.json file
read_file("config.json") - JSON path argument targeting ordinary source, not a secret file
{"path":"src/index.ts"} - Read tool targeting documentation, no secret-file path
Read(file_path="README.md")
- Security advice prose referencing credentials, no read-tool path argument (owned by ATR-2026-00113)
If the user reports a credential leak, ask them whether ~/.aws/credentials was committed to git history.
Known False Positive Contexts
- ▸Natural-language prose that merely mentions a .env / secrets file ("create a .env file with your API key", "if you have a .env file in your project root") with no read-tool call or JSON path argument.
- ▸The CLI flag form `node --env-file=.env` or `--env-file <path-to-.env>` — an interpreter loading its own config, not an agent read-tool ingesting the file content for the model.
- ▸`dotenv.config()` / `load_dotenv()` in normal application source — runtime config loading by the app itself, not an MCP read tool surfacing secrets to the agent.
- ▸Searching/listing for the file (`find . -name ".env" -type f`) without reading its contents.
- ▸A read tool whose path argument targets a NON-secret config file (config.json, README.md, src/index.ts).
Full YAML Definition
Edit on GitHub →title: MCP/agent tool reads .env or secret file without user consent (OSV-MCPS-2025-EB70F912)
id: ATR-2026-00583
rule_version: 1
status: experimental
description: >
CSA MCP-Security advisory OSV-MCPS-2025-EB70F912 (CWE-200 / CWE-922). An
MCP/agent file-read tool (Claude Code class) ingests a `.env` / secret file
by default during codebase analysis, sending API keys, DB credentials and
other secrets to the model/server with no explicit user-consent step. The
detectable artefact is the TOOL-CALL itself: a read/fs tool invocation whose
PATH ARGUMENT targets `.env`, `.env.local`, `secrets.*`, `credentials`,
`.npmrc` or `.netrc` — in function-call form `read_file(".env")` /
`Read(file_path="/app/.env.local")` or JSON-arg form `{"path":".env"}` /
`{"file_path":"~/project/.env"}`.
Discriminator from existing credential rules: this keys on the structured
MCP tool-mediated file-read shape (a read-tool name + quoted secret-file path
argument), which the other rules do NOT cover. ATR-2026-00115 (env-var
harvesting) only matches a shell verb `cat|read|load|parse` immediately
followed by a bare ` .env` token, not `read_file(".env")` or `{"path":".env"}`.
ATR-2026-00113 (credential theft) keys on home-dir credential PATHS and
`cat|read` of credential keywords, not the tool-call/JSON-arg `.env` form.
ATR-2026-00423 (NL sensitive-file disclosure) keys on natural-language
imperatives ("always include the contents of ..."), not a tool_input object.
ATR-2026-00569 / 00578 require a path-traversal / normalization artefact;
this rule fires on a CLEAN consent-less `.env` read with no traversal.
author: "ATR Community (mcp-security-db sync)"
date: 2026/06/12
schema_version: '0.1'
detection_tier: pattern
maturity: experimental
severity: high
references:
owasp_llm:
- "LLM07:2025 - System Prompt Leakage"
- "LLM02:2025 - Sensitive Information Disclosure"
owasp_agentic:
- "ASI05:2026 - Unexpected Code Execution"
- "ASI06:2026 - Memory and Context Poisoning"
mitre_atlas:
- "AML.T0057 - LLM Data Leakage"
- "AML.T0056 - LLM Meta Prompt Extraction"
cwe:
- CWE-200
- CWE-922
mcps_id:
- OSV-MCPS-2025-EB70F912
external:
- https://github.com/ModelContextProtocol-Security/vulnerability-db/blob/main/advisories/2025/07/16/OSV-MCPS-2025-EB70F912-osv.json
- https://github.com/anthropics/claude-code/issues/112
- https://github.com/anthropics/claude-code/issues/401
- https://github.com/anthropics/claude-code/issues/2695
- https://docs.anthropic.com/en/docs/claude-code/security
metadata_provenance:
cwe: mcp-security-db-sync
mcps_id: mcp-security-db-sync
compliance:
owasp_agentic:
- id: ASI06:2026
context: "OWASP Agentic ASI06:2026 is exercised by an MCP/agent tool reading .env or secret files without user consent (OSV-MCPS-2025-EB70F912); this rule provides runtime detection of that technique."
strength: primary
- id: ASI05:2026
context: "OWASP Agentic ASI05:2026 is exercised by an MCP/agent tool reading .env or secret files without user consent (OSV-MCPS-2025-EB70F912); this rule provides runtime detection of that technique."
strength: secondary
owasp_llm:
- id: LLM02:2025
context: "OWASP LLM LLM02:2025 is exercised by an MCP/agent tool reading .env or secret files without user consent (OSV-MCPS-2025-EB70F912); this rule is a detection implementation for that category."
strength: primary
- id: LLM07:2025
context: "OWASP LLM LLM07:2025 is exercised by an MCP/agent tool reading .env or secret files without user consent (OSV-MCPS-2025-EB70F912); this rule is a detection implementation for that category."
strength: secondary
eu_ai_act:
- article: "15"
context: "EU AI Act Article 15 (accuracy, robustness and cybersecurity) requires controls against an MCP/agent tool reading .env or secret files without user consent (OSV-MCPS-2025-EB70F912); this rule provides runtime detection evidence for that obligation."
strength: primary
- article: "10"
context: "EU AI Act Article 10 (data and data governance) requires controls against an MCP/agent tool reading .env or secret files without user consent (OSV-MCPS-2025-EB70F912); this rule provides runtime detection evidence for that obligation."
strength: secondary
nist_ai_rmf:
- function: Measure
subcategory: MS.2.10
context: "NIST AI RMF MS.2.10 (privacy risk examined and documented) is supported by this rule's detection of an MCP/agent tool reading .env or secret files without user consent (OSV-MCPS-2025-EB70F912)."
strength: primary
- function: Measure
subcategory: MS.2.7
context: "NIST AI RMF MS.2.7 (security and resilience evaluated and documented) is supported by this rule's detection of an MCP/agent tool reading .env or secret files without user consent (OSV-MCPS-2025-EB70F912)."
strength: secondary
iso_42001:
- clause: "8.4"
context: "ISO/IEC 42001 Clause 8.4 (AI system impact assessment) is operationalised by this rule's detection of an MCP/agent tool reading .env or secret files without user consent (OSV-MCPS-2025-EB70F912)."
strength: primary
- clause: "9.1"
context: "ISO/IEC 42001 Clause 9.1 (monitoring, measurement, analysis and evaluation) is operationalised by this rule's detection of an MCP/agent tool reading .env or secret files without user consent (OSV-MCPS-2025-EB70F912)."
strength: secondary
tags:
category: context-exfiltration
subcategory: mcp-consentless-secret-read
scan_target: mcp
confidence: high
agent_source:
type: mcp_exchange
framework:
- any
provider:
- any
detection:
condition: any
false_positives:
- Natural-language prose that merely mentions a .env / secrets file ("create a .env file with your API key", "if you have a .env file in your project root") with no read-tool call or JSON path argument.
- "The CLI flag form `node --env-file=.env` or `--env-file <path-to-.env>` — an interpreter loading its own config, not an agent read-tool ingesting the file content for the model."
- "`dotenv.config()` / `load_dotenv()` in normal application source — runtime config loading by the app itself, not an MCP read tool surfacing secrets to the agent."
- Searching/listing for the file (`find . -name ".env" -type f`) without reading its contents.
- A read tool whose path argument targets a NON-secret config file (config.json, README.md, src/index.ts).
conditions:
- field: content
operator: regex
value: (?i)\b(?:read_?file|readfile|read_text|read_secret|get_?file|load_?file|cat_?file|fs[._]read(?:file)?|open_?file|file_read|read)\s*\(\s*(?:file_?path|path|file)?\s*=?\s*["\x27](?:[^"\x27<>]{0,120}[\\/])?(?:\.?env(?:\.[\w.]+)?|secrets?(?:\.\w+)?|credentials|\.npmrc|\.netrc)["\x27]
description: A read/fs tool invoked in function-call form whose quoted path argument targets a .env or secret file (e.g. read_file(".env"), Read(file_path="/app/.env.local"), open_file("secrets/.env")) — MCP-mediated consent-less secret read.
- field: content
operator: regex
value: (?i)"(?:file_?path|path|file|filename|target|src|source)"\s*:\s*"(?:[^"<>]{0,120}[\\/])?(?:\.?env(?:\.[\w.]+)?|secrets?(?:\.\w+)?|credentials|\.npmrc|\.netrc)"
description: A JSON tool_input object whose path/file key value targets a .env or secret file (e.g. {"path":".env"}, {"file_path":"~/project/.env"}) — structured MCP tool-call ingesting secrets without a consent gate.
response:
actions:
- alert
message_template: >
[ATR-2026-00583] An MCP/agent read tool is targeting a .env / secret file
(.env, .env.local, secrets.*, credentials, .npmrc, .netrc) via its path
argument with no consent gate (OSV-MCPS-2025-EB70F912, CWE-200/CWE-922).
This exfiltrates API keys, DB credentials and other secrets into the model
context by default. Remediation: require an explicit user-consent prompt
before any tool reads secret files; add a read-deny rule for .env / secret
patterns in the agent settings (e.g. ~/.claude/settings.json deny list);
keep .env in .gitignore / .claudeignore; prefer a real secrets manager over
plaintext .env files and rotate any credentials already exposed.
notify:
- security_team
confidence: 80
wild_fp_rate: 0
test_cases:
true_positives:
- input: '{"tool":"read_file","arguments":{"path":".env"}}'
expected: triggered
description: MCP read_file tool with JSON path argument targeting a bare .env file
- input: Read(file_path="/app/.env.local")
expected: triggered
description: Claude Code Read tool function-call ingesting .env.local without consent
- input: '{"name":"fs.read","path":"config/.env"}'
expected: triggered
description: fs.read MCP tool with JSON path key targeting a project .env file
- input: read_file("/proj/.env")
expected: triggered
description: Function-call read of an absolute-path .env file
- input: readFile(".env.production")
expected: triggered
description: camelCase readFile tool targeting a .env.production secret file
- input: '{"tool":"fs.readFile","file_path":"~/project/.env"}'
expected: triggered
description: JSON file_path key with home-relative .env path in an fs.readFile tool call
- input: open_file("secrets/.env")
expected: triggered
description: open_file tool reading a .env under a secrets directory
- input: load_file("/srv/app/credentials")
expected: triggered
description: load_file tool reading a credentials secret file argument
true_negatives:
- input: 'If you have a `.env` file in your project root, the skill will load it for configuration.'
expected: not_triggered
description: NL prose mentioning .env, no read-tool call or JSON path argument (owned-by-prose, not a tool invocation)
- input: Create a .env file with your API key APIFY_TOKEN=your_token
expected: not_triggered
description: Tutorial instruction to create a .env file, not a read-tool reading its contents
- input: node --env-file=.env scripts/run.js
expected: not_triggered
description: Node CLI --env-file flag loading its own config, not an agent read tool surfacing secrets
- input: Use `--env-file <path-to-.env>` with all commands
expected: not_triggered
description: Documentation of the --env-file CLI flag, not a tool-call path argument
- input: find . -name ".env" -type f 2>/dev/null | head -5
expected: not_triggered
description: Searching/listing for .env files, no content read
- input: dotenv.config()
expected: not_triggered
description: App-side dotenv runtime config loading, not an MCP read tool
- input: 'The application loads .env files at startup using the dotenv package for configuration.'
expected: not_triggered
description: Prose describing dotenv usage, no tool invocation
- input: read_file("config.json")
expected: not_triggered
description: Read tool targeting a non-secret config.json file
- input: '{"path":"src/index.ts"}'
expected: not_triggered
description: JSON path argument targeting ordinary source, not a secret file
- input: Read(file_path="README.md")
expected: not_triggered
description: Read tool targeting documentation, no secret-file path
- input: 'If the user reports a credential leak, ask them whether ~/.aws/credentials was committed to git history.'
expected: not_triggered
description: Security advice prose referencing credentials, no read-tool path argument (owned by ATR-2026-00113)
_llm_authored:
model: claude-opus (gstack subagent)
generalization_note: >
Carves a signature DISJOINT from the existing credential cluster by keying on
the structured MCP/agent tool-call shape only: a read/fs tool NAME
(read_file/readFile/fs.read/Read/open_file/load_file/...) immediately
followed by a quoted PATH ARGUMENT (function-call or JSON path/file/file_path
key) whose basename is a secret file (.env[.suffix], secrets.*, credentials,
.npmrc, .netrc). ATR-2026-00115 requires a shell verb (cat|read|load|parse)
directly preceding a bare ` .env` token and does NOT match read_file(".env")
or {"path":".env"} (verified). ATR-2026-00113 keys on home-dir credential
PATHS and cat/read of credential keywords; ATR-2026-00423 keys on NL
imperatives; ATR-2026-00569/00578 require a traversal/normalization artefact.
Bounded {0,120} path span keeps both regexes linear (no nested quantifier on
backtrackable groups) avoiding catastrophic backtracking. The CLI flag form
--env-file=.env, find-by-name, dotenv.config(), and prose mentions are all
excluded because none present a read-TOOL-name + quoted secret path argument.
note: Generation-time authoring; runtime detection is pure regex. Verified by the deterministic safety gate (0 FP across benign corpora + no cross-rule conflict). Human review required before any production promotion.