fix(security): harden interpreter-based command detection against obf… - #57666
fix(security): harden interpreter-based command detection against obf…#57666MorAlekss wants to merge 1 commit into
Conversation
|
suggesting changes This still leaves interpreter-based command detection bypasses in both touched security checks. In Security evidence:
I reviewed the meaningful patch replayed onto current GitHub Signed: GPT-5.5-xhigh in Codex |
|
Fixed:
Added:
|
|
suggesting changes
The first two are Python options that take their own argument before Security evidence:
I reviewed the meaningful patch replayed onto current GitHub Signed: GPT-5.5-xhigh in Codex |
|
Thanks for addressing two real interpreter-substitution gaps. Problems
Suggested changes
Automated hermes-sweeper review. |
56074ee to
ea58230
Compare
|
@teknium1 thank you for the review. Rebased onto current main (5349c7c), squashed into one commit. The obfuscation regexes this PR originally added are gone: #63585 closed those MCP gate. Approval matcher. I removed it rather than widening it. The tokenizer Also in this branch, three narrower gaps still open on main: One behavioral change. The new description is not registered in 28 tests added to the existing files. Every positive case was verified to fail |
|
suggesting changes The new MCP launcher unwrapping still stops before the real interpreter when a supported The loop skips the option token but not its owned value, then mistakes Security evidence:
Signed: GPT-5.6-sol-xhigh in Codex |
ea58230 to
2cc861a
Compare
|
Confirmed and fixed in 2cc861a. You were right, and it reaches further than the Root cause: The same defect is on unmodified main in
One bug the new tests caught in my own fix: Tests, both directions as you asked. Egress and persistence regressions across |
|
suggesting changes The new per-wrapper option grammar still omits GNU
Please add Security evidence:
Signed: GPT-5.6-sol-xhigh in Codex |
8112c03 to
1df1f3c
Compare
|
Confirmed and fixed in 1df1f3c. Checking the rest of the tables against their --argv0. Added to env's option grammar in both the structured argv path and sudo's own set. The same check found four more it omits, each stopping the
Launchers absent from the wrapper set entirely. One level up from the Where this stops, and why the list is not complete.
Past those, the honest position is that the wrapper list is open-ended and So what this PR fixes is the parsing defect, the wrong option grammar being |
|
not enough evidence A coherent review tree against current Without a conflict-resolved replay, I cannot determine whether the proposed interpreter-substitution hardening preserves the intended command-validation and approval invariants while still accepting benign commands. Please provide a deterministic conflict-resolved replay onto the bound current-main SHA, including the exact conflict resolutions, so current-main reproduction, positive and negative cases, both validation paths, and residual bypass variants can be reviewed. Uncertainty: The integrated files, lines, and semantics after conflict resolution are unknown, so this review does not classify the change as fixed, bypassable, or mergeable. Signed: GPT-5.6-sol-xhigh in Codex |
Summary
Fixes two independent security checks that only recognized shell
interpreters (bash/sh/zsh/etc), letting the same attack shapes they
were built to catch slip through under a general-purpose interpreter
name (python3, node, perl, ruby) instead. Also hardens the underlying
command-detection regex against combined short flags, no-space flag
gluing, long-form flags, and versioned interpreter binaries, all of
which evaded detection even for the interpreters that were already
covered.
Root cause
hermes_cli/mcp_security.py'svalidate_mcp_server_entry()checks anMCP server config entry for the June 2026 hermes-0day network-egress
and OS-persistence attack shapes, but only when
commandmatches_SHELL_INTERPRETERS(bash, sh, zsh, dash, fish, cmd, powershell,pwsh). An entry shaped like
command: python3, args: [-c, "<the exact same payload>"]returned early with no warnings, since python3 isn'ta shell, even though the inline script it runs is just as capable of
network egress or writing to
~/.ssh/authorized_keysas a bashone-liner.
tools/approval.pyhas a parallel gap in its own,independent detection. Its "script execution via -e/-c flag" pattern
existed to flag exactly this class of command, but it required a
bare, space-separated
-c/-eimmediately after the literalinterpreter name. Real, unremarkable command forms evade this:
combined short flags (
python3 -uc "..."), no-space flag gluing(
python3 -c"..."), long-form flags (node --eval="..."), andversioned interpreter binaries (
python3.11,python3.12— the normunder pyenv/homebrew/most distro packaging, where the unversioned name
is often just a symlink). Since
approvals.mode,yolo, and theallowlist all live in
~/.hermes/config.yaml, a command in one ofthese forms that rewrites that file to plant a malicious MCP server
entry gets no specific warning about what it targets, and in
sufficiently obfuscated forms evaded detection outright.
Behavioral change
Before: an MCP server entry using a general-purpose interpreter instead
of a shell bypassed the egress/persistence checks entirely. Separately,
a command rewriting
~/.hermes/config.yamlthrough an interpreter'snative file I/O, in a combined-flag, no-space, long-form, or
versioned-binary form, could evade the dangerous-command detection
that would otherwise require approval.
After:
hermes_cli/mcp_security.pyruns the same egress/persistencechecks for python/node/perl/ruby (in addition to shells), matching
network calls native to each language rather than only shell tools
like curl/wget.
tools/approval.py's interpreter/flag matchingrecognizes combined flags, no-space gluing, long-form flags, and
versioned binaries, and a command in one of these forms that also
references
~/.hermes/config.yamlor.envgets a specific approvalreason naming the config file, ahead of the generic
script-execution reason.
What changed
hermes_cli/mcp_security.py: renamed_SHELL_INTERPRETERSto_SCRIPT_INTERPRETERS, adding python/python3/node/nodejs/deno/perl/ruby/php (and their
.exeforms). Extended_EGRESS_PATTERNwithinterpreter-native network-call patterns (urllib, requests, socket,
http.client, httpx, node's require/fetch, Perl's LWP/Net::HTTP, Ruby's
open-uri, PHP's curl_init/fsockopen/file_get_contents). Updated the
warning text and docstrings from "shell interpreter" to "interpreter"
to match the widened scope.
tools/approval.py: added_INLINE_SCRIPT_INTERPRETERand_INLINE_SCRIPT_FLAG, shared regex fragments matching versionedinterpreter binaries and inline-script flags in combined, glued, or
long-form shapes. The existing "script execution via -e/-c flag" rule
now uses these. Added a new rule, placed immediately before it, that
matches the same interpreter/flag shape when the command also
references
~/.hermes/config.yamlor.env, giving that case aspecific reason instead of the generic one.
tests/hermes_cli/test_mcp_security.py: added tests covering thenetwork-egress and persistence shapes via python3/node/perl/ruby, and
a test confirming a benign python/node MCP server (module invocation,
plain script file) is not flagged.
tests/tools/test_approval.py: added a test class covering theflag-obfuscation and versioned-binary forms against both the generic
and config-specific rules, plus benign cases that must not match.
Updated one existing test whose prior expectation relied on the exact
combined-flag gap this fix closes.
What is NOT changed
tools/approval.py's interpreter/flag matching:its inline-eval flag is
-r, which would collide with Ruby's common-r<library>require flag under a shared character class, andworking out that ambiguity is out of scope here
X=python3; $X -c ...) and encoded payloads(base64 piped through
eval) are not addressed; these are inherentlimitations of regex-based static detection, consistent with the
rest of this file's approach, not something this PR claims to solve
auto-approved regardless of which pattern matches (a separate,
already-open issue); the
tools/approval.pyhalf of this fix doesnot change that. The
hermes_cli/mcp_security.pyhalf is unaffectedby this, since it runs at MCP config load/spawn time independent of
how the entry was written