hermes - 💡(How to fix) Fix [Bug]: Tirith shell scanner blocks pipe-to-interpreter even when LHS is a local user-owned executable

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

The Tirith shell-scanner's pipe-to-interpreter rule flags any command of the form <executable> | python3 (or | jq, | awk, etc.) as HIGH-severity, regardless of whether the left-hand executable is a local script under the user's control. This makes the common pattern "run my local wrapper, filter its JSON output with python3/jq" unusable for kanban workers, and forces a workaround where the worker must write output to a temp file and re-read it — or restructure prompts to avoid pipes entirely.

Root Cause

The Tirith shell-scanner's pipe-to-interpreter rule flags any command of the form <executable> | python3 (or | jq, | awk, etc.) as HIGH-severity, regardless of whether the left-hand executable is a local script under the user's control. This makes the common pattern "run my local wrapper, filter its JSON output with python3/jq" unusable for kanban workers, and forces a workaround where the worker must write output to a temp file and re-read it — or restructure prompts to avoid pipes entirely.

Fix Action

Fix / Workaround

The Tirith shell-scanner's pipe-to-interpreter rule flags any command of the form <executable> | python3 (or | jq, | awk, etc.) as HIGH-severity, regardless of whether the left-hand executable is a local script under the user's control. This makes the common pattern "run my local wrapper, filter its JSON output with python3/jq" unusable for kanban workers, and forces a workaround where the worker must write output to a temp file and re-read it — or restructure prompts to avoid pipes entirely.

Possible mitigations:

  1. Allow-list local executables — if LHS resolves to a path under $HERMES_HOME/profiles/*/bin/, $HOME/.local/bin/, or another user-configured "trusted scripts" directory, skip the pipe-to-interpreter check.
  2. Per-profile config knob — extend security.trusted_url_prefixes (proposed in #29513) into security.trusted_executable_dirs so users can opt-in.
  3. Severity downgrade — keep the warning but emit it as MEDIUM and require explicit opt-out via approval rather than hard-block.
  • A kanban worker crashed on a routine read-only data-lookup task during production smoke testing.
  • We worked around it by editing the worker's SOUL.md to instruct "filter the JSON in-memory in your response rather than piping to python3" — but this is brittle (worker has to re-derive the workaround per task) and forfeits the natural shell-pipeline pattern.
  • The pipe_to_interpreter block is on the output side of a wrapper we explicitly created to be the only allowed read path to the vendor API; it provides no security benefit in this configuration.

Code Example

my-wrapper clients --limit 100 | python3 -c "import sys,json; d=json.load(sys.stdin); print([c for c in d['data'] if 'foo' in c.get('name','')])"
RAW_BUFFERClick to expand / collapse

Summary

The Tirith shell-scanner's pipe-to-interpreter rule flags any command of the form <executable> | python3 (or | jq, | awk, etc.) as HIGH-severity, regardless of whether the left-hand executable is a local script under the user's control. This makes the common pattern "run my local wrapper, filter its JSON output with python3/jq" unusable for kanban workers, and forces a workaround where the worker must write output to a temp file and re-read it — or restructure prompts to avoid pipes entirely.

Hermes version

hermes-agent 0.14.0 (pipx-installed, Linux/systemd user-mode gateway)

Reproduction

  1. Define a local read-only wrapper script at $HERMES_HOME/profiles/<worker>/bin/<wrapper-name> that calls a vendor API and returns JSON to stdout. (In our case, it queries a vendor REST API with a vault-stored read-only key.)
  2. Give a kanban worker a task that requires filtering the wrapper's output, e.g. "find which AP a given device is connected to" against a network-controller API wrapper.
  3. The worker produces a reasonable shell pipeline:
    my-wrapper clients --limit 100 | python3 -c "import sys,json; d=json.load(sys.stdin); print([c for c in d['data'] if 'foo' in c.get('name','')])"
  4. Tirith blocks the command with a HIGH-severity pipe_to_interpreter finding. Worker either retries identical pipelines (loops), or crashes without calling kanban_complete/kanban_block.

Expected

Tirith should distinguish between:

  • High-risk: curl <url> | python3 / wget <url> | sh (pipe untrusted downloaded content to an interpreter)
  • Low-risk: <local-executable> | python3 where the LHS resolves to a script the user owns and is on PATH

Possible mitigations:

  1. Allow-list local executables — if LHS resolves to a path under $HERMES_HOME/profiles/*/bin/, $HOME/.local/bin/, or another user-configured "trusted scripts" directory, skip the pipe-to-interpreter check.
  2. Per-profile config knob — extend security.trusted_url_prefixes (proposed in #29513) into security.trusted_executable_dirs so users can opt-in.
  3. Severity downgrade — keep the warning but emit it as MEDIUM and require explicit opt-out via approval rather than hard-block.

Actual impact

  • A kanban worker crashed on a routine read-only data-lookup task during production smoke testing.
  • We worked around it by editing the worker's SOUL.md to instruct "filter the JSON in-memory in your response rather than piping to python3" — but this is brittle (worker has to re-derive the workaround per task) and forfeits the natural shell-pipeline pattern.
  • The pipe_to_interpreter block is on the output side of a wrapper we explicitly created to be the only allowed read path to the vendor API; it provides no security benefit in this configuration.

Related issues

  • #29513 — security.trusted_url_prefixes feature for tirith scanners (same shape: opt-in trust)
  • #22722 — Tirith false-positive on sed | delimiter (same category: scanner over-fires on benign pipe usage)
  • #7826 — Security Audit: 4 Critical, 9 High severity findings in default configuration (broader context)

Notes

  • We are NOT proposing to disable the pipe-to-interpreter rule. The protection on curl <url> | python3 is legitimate and important.
  • We ARE proposing that the rule's LHS resolution take into account whether the executable is local / user-owned / on a trusted path.
  • Happy to test a patch — our setup reproduces this on every kanban dispatch that touches the affected wrapper.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING