security: path traversal fix, Claude Code credential gate, DANGEROUS_PATTERNS gaps#7156
Merged
Conversation
Gate function for checking whether a user has explicitly selected a provider via hermes model/setup, auth.json active_provider, or env vars. Used in subsequent commits to prevent unauthorized credential auto-discovery. Follows the pattern from PR #4210. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…er config
_seed_from_singletons('anthropic') now checks
is_provider_explicitly_configured('anthropic') before reading
~/.claude/.credentials.json. Without this, the auxiliary client
fallback chain silently discovers and uses Claude Code tokens when
the user's primary provider key is invalid — consuming their Claude
Max subscription quota without consent.
Follows the same gating pattern as PR #4210 (setup wizard gate)
but applied to the credential pool seeding path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, removing a claude_code credential from the anthropic pool only printed a note — the next load_pool() re-seeded it from ~/.claude/.credentials.json. Now writes a 'suppressed_sources' flag to auth.json that _seed_from_singletons checks before seeding. Follows the pattern of env: source removal (clears .env var) and device_code removal (clears auth store state). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…configured _resolve_api_key_provider() now checks is_provider_explicitly_configured before calling _try_anthropic(). Previously, any auxiliary fallback (e.g. when kimi-coding key was invalid) would silently discover and use Claude Code OAuth tokens — consuming the user's Claude Max subscription without their knowledge. This is the auxiliary-client counterpart of the setup-wizard gate in PR #4210. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Four gaps in DANGEROUS_PATTERNS found by running 10 targeted tests that
each mapped to a specific pattern in approval.py and checked whether the
documented defense actually held.
1. **Heredoc script injection** — `python3 << 'EOF'` bypasses the
existing `-e`/`-c` flag pattern. Adds pattern for interpreter + `<<`
covering python{2,3}, perl, ruby, node.
2. **PID expansion self-termination** — `kill -9 $(pgrep hermes)` is
opaque to the existing `pkill|killall` + name pattern because command
substitution is not expanded at detection time. Adds structural
patterns matching `kill` + `$(pgrep` and backtick variants.
3. **Git destructive operations** — `git reset --hard`, `push --force`,
`push -f`, `clean -f*`, and `branch -D` were entirely absent.
Note: `branch -d` also triggers because IGNORECASE is global —
acceptable since -d is still a delete, just a safe one, and the
prompt is only a confirmation, not a hard block.
4. **chmod +x then execute** — two-step social engineering where a
script containing dangerous commands is first written to disk (not
checked by write_file), then made executable and run as `./script`.
Pattern catches `chmod +x ... [;&|]+ ./` combos. Does not solve the
deeper architectural issue (write_file not checking content) — that
is called out in the PR description as a known limitation.
Tests: 23 new cases across 4 test classes, all in test_approval.py:
- TestHeredocScriptExecution (7 cases, incl. regressions for -c)
- TestPgrepKillExpansion (5 cases, incl. safe kill PID negative)
- TestGitDestructiveOps (8 cases, incl. safe git status/push negatives)
- TestChmodExecuteCombo (3 cases, incl. safe chmod-only negative)
Full suite: 146 passed, 0 failed.
This was referenced Apr 10, 2026
Closed
Closed
CybercodeXx
pushed a commit
to CybercodeXx/hermes-agent
that referenced
this pull request
Apr 17, 2026
Scenario: user configures kimi-coding with a malformed API key. When the main agent fails and auxiliary fallback runs, the fix must prevent _try_anthropic() from silently reading ~/.claude/.credentials.json. 9 tests covering all three vulnerable paths from PRs NousResearch#7009/NousResearch#7156: A. _seed_from_singletons() — credential pool gate B. _resolve_api_key_provider() — auxiliary client fallback chain C. suppress_credential_source() — re-seed prevention after auth remove Also verifies CLAUDE_CODE_OAUTH_TOKEN (implicit) vs ANTHROPIC_API_KEY (explicit) are distinguished correctly by is_provider_explicitly_configured(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three security fixes salvaged from PRs #7065, #7009, and #6961. All contributor authorship preserved.
1. Skill manager path traversal (PR #7065, @Dusk1e)
Symlinks inside skill directories could escape to arbitrary files for write/patch/remove. Adds
_resolve_skill_target()with resolved-path containment check. 3 tests.2. Claude Code credential gate (PR #7009, @wanpengxie)
When a user's primary provider fails, the auxiliary fallback chain silently discovered and used Claude Code OAuth tokens from
~/.claude/.credentials.jsonwithout consent. Addsis_provider_explicitly_configured()gate — credentials are only used when the user explicitly configured Anthropic. Defense in depth: gate in credential pool + gate in aux client + suppression onhermes auth remove. 9 tests.3. DANGEROUS_PATTERNS gaps (PR #6961, @win4r)
Closes 4 bypass categories: heredoc script execution (
python3 <<), pgrep kill expansion, git destructive ops (reset --hard, push --force, clean -f, branch -D), and chmod+exec combo. 11 new patterns, 23 tests.E2E verification
All three fixes verified with real file I/O, real symlink creation, real env var manipulation:
Test results
301 targeted tests passing across all affected files.