{"record":{"id":"f9cd927ed94180a2","repo":"NousResearch/hermes-agent","slug":"path-is-a-sensitive-credential-or-internal-hermes","errorCode":null,"errorMessage":"path is a sensitive credential or internal Hermes path and cannot be attached","messagePattern":"path is a sensitive credential or internal Hermes path and cannot be attached","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/context_references.py","lineNumber":502,"sourceCode":"def _ensure_reference_path_allowed(path: Path) -> None:\n    from hermes_constants import get_hermes_home\n    home = Path(os.path.expanduser(\"~\")).resolve()\n    hermes_home = get_hermes_home().resolve()\n\n    blocked_exact = {home / rel for rel in _SENSITIVE_HOME_FILES}\n    blocked_exact.add(hermes_home / \".env\")\n    blocked_dirs = [home / rel for rel in _SENSITIVE_HOME_DIRS]\n    blocked_dirs.extend(hermes_home / rel for rel in _SENSITIVE_HERMES_DIRS)\n\n    if path in blocked_exact:\n        raise ValueError(\"path is a sensitive credential file and cannot be attached\")\n\n    for blocked_dir in blocked_dirs:\n        try:\n            path.relative_to(blocked_dir)\n        except ValueError:\n            continue\n        raise ValueError(\"path is a sensitive credential or internal Hermes path and cannot be attached\")\n\n    # Anchor to the canonical read deny-list (agent/file_safety.get_read_block_error),\n    # the single source of truth used by the file/terminal read path. The narrow\n    # list above predates that guard and never caught the real credential stores:\n    # provider keys (auth.json), Anthropic OAuth tokens (.anthropic_oauth.json),\n    # MCP OAuth material (mcp-tokens/), webhook HMAC secrets, and project-local\n    # .env files. That gap matters because the gateway feeds UNTRUSTED remote\n    # message text into reference expansion, so `@file:~/.hermes/auth.json` from a\n    # chat peer would otherwise read the operator's keys straight into context.\n    # Routing through the canonical guard closes the gap today and keeps this path\n    # protected automatically whenever that deny-list grows.\n    try:\n        from agent.file_safety import get_read_block_error\n\n        if get_read_block_error(str(path)) is not None:\n            raise ValueError(\n                \"path is a sensitive credential or internal Hermes path and cannot be attached\"\n            )","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/context_references.py#L484-L520","documentation":"The attached file reference lives inside a sensitive directory (entries of _SENSITIVE_HOME_DIRS or _SENSITIVE_HERMES_DIRS under ~ or the HERMES_HOME). The second tier of _ensure_reference_path_allowed in agent/context_references.py uses Path.relative_to() against each blocked directory to reject anything nested within them, guarding internal Hermes state and credential stores from being inlined into context.","triggerScenarios":"An @file: reference whose resolved path is under a blocked directory, e.g. @file:~/.hermes/auth.json, @file:~/.hermes/sessions/x.json, or any path within the sensitive home/HERMES_HOME directory lists. Matched by the relative_to loop over blocked_dirs.","commonSituations":"Asking the agent to inspect its own session or auth state for debugging; remote chat peers probing ~/.hermes/* stores through the gateway's reference expansion.","solutions":["Attach a copy of the (redacted) data placed in the workspace instead of the live file under the sensitive directory.","Use built-in surface instead: hermes logs or slash commands to inspect Hermes state safely.","Never point @file: at anything under ~/.hermes/ credential/internal directories."],"exampleFix":"# before\n@file:~/.hermes/auth.json\n# after — export names only, not values\nhermes provider list   # then attach the non-secret output saved in the workspace","handlingStrategy":"validation","validationCode":"from pathlib import Path\nimport os\n\ndef is_in_sensitive_dir(p: Path) -> bool:\n    home = Path(os.path.expanduser('~')).resolve()\n    for d in ('.hermes', '.ssh', '.gnupg'):  # mirror the sensitive dirs policy\n        try:\n            p.resolve().relative_to(home / d)\n            return True\n        except ValueError:\n            continue\n    return False","typeGuard":null,"tryCatchPattern":"try:\n    attach(path)\nexcept ValueError as e:\n    if \"sensitive credential or internal Hermes path\" in str(e):\n        # copy redacted data into the workspace instead\n        ...","preventionTips":["Do not point @file: references under ~/.hermes/ or other sensitive home dirs","Use hermes logs / slash commands to inspect agent state instead of attaching files","Copy only the needed non-secret data into the workspace before referencing"],"tags":["security","credentials","references","hermes-home"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}