Skip to content

security: path traversal fix, Claude Code credential gate, DANGEROUS_PATTERNS gaps#7156

Merged
teknium1 merged 6 commits into
mainfrom
security/bucket-j
Apr 10, 2026
Merged

security: path traversal fix, Claude Code credential gate, DANGEROUS_PATTERNS gaps#7156
teknium1 merged 6 commits into
mainfrom
security/bucket-j

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

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.json without consent. Adds is_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 on hermes 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:

  • Symlink escapes blocked for write/patch/remove; legitimate writes pass
  • CLAUDE_CODE_OAUTH_TOKEN excluded from explicit config detection; ANTHROPIC_API_KEY included
  • All 8 dangerous commands flagged, all 7 safe commands pass (zero false positives)

Test results

301 targeted tests passing across all affected files.

Junass1 and others added 6 commits April 10, 2026 05:05
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.
@teknium1 teknium1 merged commit aedf6c7 into main Apr 10, 2026
5 of 6 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants