title: File-Tool Argument Targets a Modern Credential Store Outside the Classic Path List
id: ATR-2026-02684
rule_version: 2
status: experimental
description: |
  Detects a file-reading / file-attaching agent tool argument whose value names a credential store that ATR's existing sensitive-path rules do not list. Those rules (ATR-2026-02250, ATR-2026-00161 and the tool_call traversal family) were written around the 2010s-era set -- ~/.ssh/id_*, ~/.aws/credentials, ~/.netrc, ~/.kube/config, /etc/shadow, /proc/self/environ -- and stop there. The stores an agent actually finds on a 2026 developer or CI box are elsewhere: ~/.config/gh/hosts.yml (a GitHub token with repo scope), /var/run/secrets/kubernetes.io/serviceaccount/token (a live cluster identity), ~/.config/gcloud/*, ~/.pypirc, ~/.cargo/credentials, ~/.terraform.d/ credentials.tfrc.json, ~/.azure/*, ~/.config/rclone/rclone.conf, ~/.docker/config.json, ~/.npmrc. Mined from OWASP-ASI-INC-09330 (OpenClaw canvas path traversal), where the decisive evidence is exactly a file-path tool argument naming one of these. rule_version 2 (adversarial refutation, 2026-08-24): version 1 required only that a file-shaped argument NAME sit next to the path, and it accepted the bare words `path` and `file` as that name. Those are ordinary English words and also the two most common keys in DevOps configuration, so 15 of 16 hand-written benign inputs fired -- `rclone config paths` output, an actions/cache step, a docker-compose secret, an Ansible stat task, Kubernetes and Terraform documentation, an incident postmortem, a pytest failure line, and security writing explaining this very attack. The rule now requires one of four serialized-call shapes: a quoted argument key AND a quoted value; an agent-internal argument name (jsonlPath, systemPromptFile, personaFile ...); a function call whose CALLEE is a named file tool; or a tool call that names a file-reading tool and passes a generic path argument inside the same call. Prose, YAML configuration and CLI output no longer qualify.
author: ATR Community (CVE sweep)
date: 2026/08/23
schema_version: '0.1'
detection_tier: pattern
maturity: test
severity: high
references:
  cwe:
    - CWE-522
    - CWE-538
    - CWE-22
  owasp_llm:
    - LLM02:2025
    - LLM06:2025
  owasp_agentic:
    - ASI04:2026
    - ASI05:2026
  mitre_attack:
    - T1552.001 - Credentials In Files
    - T1528 - Steal Application Access Token
  mitre_atlas:
    - AML.T0055 - Unsecured Credentials
  external:
    - https://owasp.org/www-project-agentic-security-initiative/
    - https://kubernetes.io/docs/tasks/run-application/access-api-from-pod/
metadata_provenance:
  cwe: human-reviewed
  owasp_llm: human-reviewed
  owasp_agentic: human-reviewed
  mitre_attack: human-reviewed
  mitre_atlas: human-reviewed
compliance:
  eu_ai_act:
    - article: '15'
      context: Article 15 (accuracy, robustness and cybersecurity) requires protection against unauthorised access to system assets; this rule detects agent file-tool arguments aimed at credential stores.
      strength: primary
    - article: '10'
      context: Article 10 (data and data governance) is supported by preventing credential material from being pulled into the agent's context as if it were ordinary data.
      strength: secondary
  nist_ai_rmf:
    - subcategory: MG.2.3
      context: Credential theft through agent file tools is an identified AI risk requiring an active runtime countermeasure; this rule implements it.
      strength: primary
    - subcategory: MP.5.1
      context: Cataloguing modern credential-store exposure through agent tooling in the organizational AI risk register.
      strength: secondary
  iso_42001:
    - clause: '8.1'
      context: ISO/IEC 42001 Clause 8.1 (operational planning and control) is operationalised by screening file-tool arguments against a credential-store list.
      strength: primary
    - clause: '8.3'
      context: ISO/IEC 42001 Clause 8.3 (AI risk treatment) is implemented by alerting when an agent tool is pointed at a token store.
      strength: secondary
tags:
  category: context-exfiltration
  subcategory: modern-credential-store-read
  scan_target: agent
  confidence: high
agent_source:
  type: tool_call
  framework:
    - any
  provider:
    - any
detection:
  condition: any
  conditions:
    - field: content
      operator: regex
      value: (?i)["\x27](?:file_?path|filepath|file_?name|jsonl_?path|artifact_?path|agent_?file|instructions_?file_?path|system_?prompt_?file|prompt_?file|persona_?file|source_?file|input_?file|attachments?)["\x27]\s{0,4}[:=]\s{0,4}["\x27](?:~|\$HOME|/home/[\w.-]{1,24}|/Users/[\w.-]{1,24})?/?\.?(?:config/gh/hosts\.ya?ml|config/gcloud/(?:credentials\.db|application_default_credentials\.json|legacy_credentials)|config/rclone/rclone\.conf|pypirc|cargo/credentials(?:\.toml)?|terraform\.d/credentials\.tfrc\.json|azure/(?:accessTokens\.json|msal_token_cache\.json)|(?:var/)?run/secrets/kubernetes\.io/serviceaccount/(?:token|namespace|ca\.crt))\b
      description: 'JSON/dict tool-call shape: a QUOTED file-shaped argument key whose QUOTED value is a modern credential store -- GitHub CLI host tokens, gcloud credential databases, rclone remotes, PyPI upload credentials, cargo registry tokens, Terraform Cloud tokens, Azure token caches, or the in-pod Kubernetes service-account token. Both the key and the value must be quote-delimited, which is what a serialized tool call looks like and what DevOps YAML, CLI output and prose do not.'
    - field: content
      operator: regex
      value: (?i)\b(?:jsonl_?path|artifact_?path|agent_?file|instructions_?file_?path|system_?prompt_?file|prompt_?file|persona_?file)\s{0,4}[:=]\s{0,4}["\x27]?(?:~|\$HOME|/home/[\w.-]{1,24}|/Users/[\w.-]{1,24})?/?\.?(?:config/gh/hosts\.ya?ml|config/gcloud/(?:credentials\.db|application_default_credentials\.json|legacy_credentials)|config/rclone/rclone\.conf|pypirc|cargo/credentials(?:\.toml)?|terraform\.d/credentials\.tfrc\.json|azure/(?:accessTokens\.json|msal_token_cache\.json)|(?:var/)?run/secrets/kubernetes\.io/serviceaccount/(?:token|namespace|ca\.crt))\b
      description: Agent-internal argument names (jsonlPath, artifactPath, systemPromptFile, personaFile, promptFile, agentFile, instructionsFilePath) pointed at a credential store. These names do not occur in ordinary infrastructure config, so the quoting requirement is relaxed here.
    - field: content
      operator: regex
      value: (?i)\b(?:read_file|readfile|read_text_file|write_file|attach_file|upload_file|send_file|get_file|load_file|open_file|append_file)\s{0,4}\(\s{0,4}(?:path|file|file_?path|file_?name|filepath|src|source|attachments?)\s{0,4}=\s{0,4}["\x27](?:~|\$HOME|/home/[\w.-]{1,24}|/Users/[\w.-]{1,24})?/?\.?(?:config/gh/hosts\.ya?ml|config/gcloud/(?:credentials\.db|application_default_credentials\.json|legacy_credentials)|config/rclone/rclone\.conf|pypirc|cargo/credentials(?:\.toml)?|terraform\.d/credentials\.tfrc\.json|azure/(?:accessTokens\.json|msal_token_cache\.json)|(?:var/)?run/secrets/kubernetes\.io/serviceaccount/(?:token|namespace|ca\.crt))\b
      description: Function-call form where the CALLEE is a named file tool -- read_file(path="..."), attach_file(file="...") -- and the argument value is a credential store. The callee list is what separates this from os.stat(path=...) or shutil.copy(src=...) in ordinary code.
    - field: content
      operator: regex
      value: (?i)["\x27](?:tool|tool_?name|function|name)["\x27]\s{0,4}:\s{0,4}["\x27](?:read|read_file|readfile|read_text_file|read_many_files|cat|view|open|open_file|attach|attach_file|upload|upload_file|send_file|get_file|load_file|fetch_file|file_read)["\x27][^\n]{0,160}["\x27](?:path|file|file_?path|file_?name|filepath|src|source|attachments?)["\x27]\s{0,4}:\s{0,4}["\x27](?:~|\$HOME|/home/[\w.-]{1,24}|/Users/[\w.-]{1,24})?/?\.?(?:config/gh/hosts\.ya?ml|config/gcloud/(?:credentials\.db|application_default_credentials\.json|legacy_credentials)|config/rclone/rclone\.conf|pypirc|cargo/credentials(?:\.toml)?|terraform\.d/credentials\.tfrc\.json|azure/(?:accessTokens\.json|msal_token_cache\.json)|(?:var/)?run/secrets/kubernetes\.io/serviceaccount/(?:token|namespace|ca\.crt))\b
      description: 'Serialized tool call that names a file-reading tool and, within the same call, passes a generic path/file/src argument whose value is a credential store. Requiring the tool name is what keeps an Ansible task, a docker-compose secret or a backup manifest -- all of which carry a bare path:/file: key -- out of this condition.'
  false_positives:
    - 'ADVERSARIAL, 2026-08-24 refutation pass: rule_version 1 fired on 15 of 16 benign probes. Root cause: `path` and `file` were accepted as argument names, and they are ordinary English words as well as the most common keys in Kubernetes, Ansible, docker-compose and GitHub Actions YAML. All 15 are now recorded as true_negatives.'
    - 'RESIDUAL, accepted: a security article that reproduces the verbatim JSON payload ({"tool":"read_file","arguments":{"file_path":"~/.config/gh/hosts.yml"}}) still matches. Those are the same bytes as the attack and no pattern can separate them; the same limit is accepted by ATR-2026-02660-class rules.'
    - An incident-response runbook or troubleshooting note that names one of these paths in prose or in a bare `cat` command -- the path must be the value of a file-shaped argument, or the source of an upload
    - A legitimate CI step that reads its own ~/.npmrc or ~/.docker/config.json as part of publishing, written as a shell command rather than as a file-tool argument
    - Documentation listing where each tool stores its configuration (e.g. 'gh stores tokens in ~/.config/gh/hosts.yml')
    - A tool argument pointed at a non-credential file inside the same directory, e.g. path=~/.config/gh/config.yml (settings, not hosts.yml)
    - 'ADVERSARIAL, FIXED: a verb-gated second condition (upload/attach/-F file=@ near one of these paths) was written first and then DELETED. It fired on 8 of 11 hand-written benign twins -- every one of them a security-policy sentence such as ''never upload ~/.config/gh/hosts.yml to a support ticket''. Prose about the attack and the attack itself share the verb, so only the argument-assignment form is matched now.'
    - 'ADVERSARIAL, FIXED: a CI setup step writing ~/.npmrc or ~/.docker/config.json through write_file matched the first version of this rule. Both were removed from the list because a legitimate write and a credential read are indistinguishable at this layer. The remaining stores can still match on a write; that is accepted, because an agent writing someone''s gcloud or Terraform credential file is itself worth an alert.'
response:
  actions:
    - alert
  message_template: |
    [ATR-2026-02684] A file tool argument names a credential store (GitHub CLI, gcloud, Kubernetes service account, PyPI, cargo, Terraform, Azure, rclone, Docker or npm auth). Reading it pulls a live bearer credential into the agent's context.
confidence: 82
