Skip to content

harden(env_passthrough): apply GHSA-rhgp-j443-p4rf filter to config.yaml path - #27794

Merged
teknium1 merged 1 commit into
NousResearch:mainfrom
roadhero:fitb/harden-env-passthrough-config
May 25, 2026
Merged

harden(env_passthrough): apply GHSA-rhgp-j443-p4rf filter to config.yaml path#27794
teknium1 merged 1 commit into
NousResearch:mainfrom
roadhero:fitb/harden-env-passthrough-config

Conversation

@roadhero

Copy link
Copy Markdown
Contributor

What

Apply the same Hermes-provider-credential filter to the config.yaml path of
tools/env_passthrough.py that already exists on the skill-declared path.
Three lines of real change; a few more for the warning message and a small
comment explaining why.

The asymmetry this closes

tools/env_passthrough.py has two intake paths that feed the same allowlist:

  1. register_env_passthrough() (line 70). Called when a skill declares
    required_environment_variables in its frontmatter. This path filters
    via _is_hermes_provider_credential() and refuses to register any name
    that is in _HERMES_PROVIDER_ENV_BLOCKLIST, logging a warning that
    cites GHSA-rhgp-j443-p4rf.
  2. _load_config_passthrough() (line 103). Reads terminal.env_passthrough
    from config.yaml. Does not filter at all; whatever string is in the
    list goes into the allowlist as-is.

Both feed is_env_passthrough() (line 125), which is consulted by
tools/environments/local.py and tools/code_execution_tool.py before
they strip a variable from the child env.

The docstring on register_env_passthrough frames the filter as a
guarantee about the execute_code sandbox's credential scrubbing, not as
a skill-specific check. The config path silently breaks that guarantee.

Why it matters

The GHSA-rhgp-j443-p4rf fix landed because a malicious skill could
register ANTHROPIC_TOKEN / OPENAI_API_KEY as passthrough and receive
the credential in an execute_code child. The fix closed the skill path
but left the config path open. Two scenarios where that gap is
reachable today:

  1. A skill that asks the operator to add the credential to
    terminal.env_passthrough in config.yaml "so this skill can work."
    The skill can't register the name itself anymore (filtered), so it
    social-engineers the operator into doing it via config. The operator
    sees a config-edit suggestion in plain text and might accept it, even
    though the same registration via the skill's own frontmatter would be
    refused.
  2. Any in-process actor that can write to ~/.hermes/config.yaml (the
    path is not in _check_sensitive_path's blocklist in
    tools/file_tools.py) can extend the allowlist with provider keys,
    bypassing the GHSA fix without going through the skill loader at all.

Neither is a "vulnerability" under the existing SECURITY.md (operator
trust envelope), and I'm not framing it that way. It's a defense-in-depth
inconsistency: the skill-path docstring promises a guarantee that the
config-path silently breaks.

The fix

Apply the same _is_hermes_provider_credential filter inside
_load_config_passthrough, with a warning message that mirrors the
skill-path one but says "Operator configuration" and "config.yaml"
instead of "Skills".

             for item in passthrough:
-                if isinstance(item, str) and item.strip():
-                    result.add(item.strip())
+                if not isinstance(item, str) or not item.strip():
+                    continue
+                name = item.strip()
+                if _is_hermes_provider_credential(name):
+                    logger.warning(
+                        "env passthrough: refusing to register Hermes "
+                        "provider credential %r from config.yaml ..."
+                    )
+                    continue
+                result.add(name)

Behavior changes

  • Operators using terminal.env_passthrough for non-Hermes API keys
    (TENOR_API_KEY, NOTION_TOKEN, etc.): no change. Those names aren't
    in _HERMES_PROVIDER_ENV_BLOCKLIST, so they pass through as before.
  • Operators using terminal.env_passthrough for ANTHROPIC_API_KEY
    (or any other Hermes-managed provider key): the entry is ignored at
    load time, with a logger.warning that explains why. The
    execute_code / terminal sandboxes see the variable scrubbed, as
    intended by GHSA-rhgp-j443-p4rf.

If an operator has a legitimate reason to expose a Hermes-managed
provider key to an execute_code child (for example, a test script that
calls the same provider), I'd be happy to add a separate explicit
terminal.env_passthrough_unsafe knob in a follow-up that takes the
filter off, so the choice is loud and named. Wanted to keep this PR
surgical.

Context

I found this while doing a private security review of hermes-agent
v2026.5.16 ahead of upstreaming a few fixes for Fox in the Box. The
review followed the SECURITY.md scope rules and didn't surface anything
that's an advisory-grade vulnerability, so this is the cleanest "good
citizen" hardening item it produced. Filing it as a regular PR rather
than a private advisory, per the SECURITY.md guidance for in-process
heuristic improvements.

Happy to add a regression test if you want one. The natural home looks
like tests/tools/test_env_passthrough_config_filter.py, covering:
(1) a benign third-party key in terminal.env_passthrough passes
through; (2) a name from _HERMES_PROVIDER_ENV_BLOCKLIST in
terminal.env_passthrough is rejected with a logger.warning;
(3) is_env_passthrough() returns False for the rejected name even
when it appears verbatim in config.

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

Labels

P1 High — major feature broken, no workaround tool/code-exec execute_code sandbox type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants