Skip to content

fix(security): mask applied secret values in tool-result egress content - #77179

Closed
andrexibiza wants to merge 2 commits into
NousResearch:mainfrom
andrexibiza:fix/security-tool-result-egress-mask
Closed

fix(security): mask applied secret values in tool-result egress content#77179
andrexibiza wants to merge 2 commits into
NousResearch:mainfrom
andrexibiza:fix/security-tool-result-egress-mask

Conversation

@andrexibiza

Copy link
Copy Markdown
Contributor

What changed and why

Tool results are assembled in make_tool_result_message (agent/tool_dispatch_helpers.py) and sent to the LLM provider API. Existing redaction at that boundary is shape-based only (vendor prefixes, URL credentials, auth headers). Opaque secret values — values applied from Bitwarden/1Password/command secret sources under any name (DATABASE_URL, FOO, arbitrary 1Password keys), or credential-suffixed env values like MY_SERVICE_TOKEN=abc123randomstring — pass through unmasked and ship verbatim to the provider. That is the highest-severity disclosure channel in the product.

This PR closes it with an exact-value pass:

  • agent/redact.py — adds mask_known_secret_values(text, extra_values=None):
    • _known_secret_values() collects values of env vars whose name ends with a credential suffix (_API_KEY, _TOKEN, _SECRET, _KEY, _PASSWORD) and whose value is ≥ 6 chars (shorter values collide with ordinary prose and are never masked).
    • extra_values threads in values applied from external secret sources (e.g. DATABASE_URL=postgres://user:supersecret@db).
    • Every known value is replaced with *** wherever it appears. Falsy input returns unchanged; the function never raises.
  • agent/tool_dispatch_helpers.pymake_tool_result_message masks the content after untrusted-content wrapping, before the message dict is built. Applied-secret values are resolved lazily at call time (get_hermes_home()hermes_cli.env_loader.get_secret_source_values(home)). Handles plain string content and multimodal content lists: only {"type": "text"} parts are masked, non-text parts (e.g. image_url) are preserved. The whole pass is best-effort — any failure (import error, home resolution error) skips masking and never breaks message construction.

Structural fields (tool_call_id, name, role) are never touched.

Why this matters to you as a user

If you use external secret sources (Bitwarden, 1Password, command-based secret resolution) or keep credentials in .env/shell env under any name, tool output that echoes those values back — connection strings, config dumps, printenv-style output, error messages — could previously carry the raw secret value to the model provider. After this change, the exact values of every known/applied secret are masked (***) at the egress boundary, so the provider never receives them, regardless of what name the secret was stored under.

How to test

HERMES_PYTHON=<your-venv>/Scripts/python.exe scripts/run_tests.sh tests/agent/test_redact.py -q
  • 81 tests passed, 0 failed (includes 8 new tests: env-value masking, extra-value masking, short-value preservation, falsy/never-raises, and three make_tool_result_message egress tests — plain content, multimodal text-masked/image-preserved, and message construction surviving a secret-source failure).

Regression:

HERMES_PYTHON=<your-venv>/Scripts/python.exe scripts/run_tests.sh tests/agent/test_redact.py tests/tools/test_build_subprocess_env.py -q
  • 87 tests passed, 0 failed.

Hygiene: git diff --check clean; scripts/check-windows-footguns.py clean on all changed files.

Platforms tested

Windows 11, git-bash (MSYS), Python 3.12, via scripts/run_tests.sh.

Related

Closes #77162. Part of the secrets-exfiltration disclosure-class series: #77008 #77012 #77020 #77027 #77031 #77039.

@alt-glitch alt-glitch added type/security Security vulnerability or hardening comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/auth Authentication, OAuth, credential pools P3 Low — cosmetic, nice to have labels Aug 3, 2026
@andrexibiza

Copy link
Copy Markdown
Contributor Author

worktree newb sorry

@andrexibiza

Copy link
Copy Markdown
Contributor Author

Superseded by #77198 (same fix, QA'd + wire-path gate test). Closing to avoid duplicate-PR confusion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(security): exact-value applied-secret redaction missing on tool-result -> provider egress path

2 participants