Skip to content

security: configurable sudo prompt timeout + SSH config metadata redaction#6547

Open
win4r wants to merge 2 commits into
NousResearch:mainfrom
win4r:fix/sudo-timeout-and-ssh-redaction
Open

security: configurable sudo prompt timeout + SSH config metadata redaction#6547
win4r wants to merge 2 commits into
NousResearch:mainfrom
win4r:fix/sudo-timeout-and-ssh-redaction

Conversation

@win4r

@win4r win4r commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Two independent security hardenings surfaced by a user-authorised local security audit of v0.8.0 (13/15 attack vectors blocked; full defense-in-depth review passed).

1. fix(terminal): configurable sudo prompt timeout

_prompt_for_sudo_password hardcoded a 45s timeout. In cron / gateway sessions an accidental sudo invocation would block the whole run for 45s before failing. This adds HERMES_SUDO_TIMEOUT as an integer env var:

Value Behaviour
unset (default) 45s prompt — unchanged
HERMES_SUDO_TIMEOUT=10 10s prompt
HERMES_SUDO_TIMEOUT=0 skip immediately, return empty (cron/gateway path)
HERMES_SUDO_TIMEOUT=abc warn, fall back to 45s (typos cannot silently disable)
negative values clamp to 0

The callback path (prompt_toolkit UI) is also guarded — when timeout is 0 the UI callback is NOT invoked, so a HERMES_SUDO_TIMEOUT=0 gateway session cannot accidentally prompt the user via the UI path either.

2. fix(redact): mask sensitive SSH config directives on file read

Reading ~/.ssh/config via read_file previously echoed the entire file verbatim. That leaks:

  • IdentityFile → exact paths to the user's private keys (single most valuable enumeration target)
  • ProxyJump / ProxyCommand → internal bastion hosts and the commands needed to reach protected networks
  • IdentityAgent / CertificateFile → similar metadata

PR #4168 (open) adds an approval-prompt pattern for cat/head/tail on .ssh/, which covers the terminal-tool path. This patch is complementary: it adds the read_file path by extending the existing redact_sensitive_text pipeline, which is the same layer that already masks API keys, tokens, DB connection strings, and private-key blocks.

Redaction is intentionally surgical — Host, HostName, User, Port stay visible because they are frequently needed for legitimate debugging, and leaking them without the identity-file path doesn't materially help an attacker. Matching is case-insensitive per sshd_config(5).

Before (today):

Host github.com
  IdentityFile ~/.ssh/id_ed25519
  ProxyJump bastion.corp:2222

After:

Host github.com
  IdentityFile ***
  ProxyJump ***

Related Issue

No open issue — findings come from an authorised local audit. Reproduction steps are in the "How to Test" section below.

Fixes #

Type of Change

  • 🔒 Security fix

Changes Made

  • tools/terminal_tool.py: add _DEFAULT_SUDO_TIMEOUT, _get_sudo_timeout(), make _prompt_for_sudo_password honour HERMES_SUDO_TIMEOUT
  • agent/redact.py: add _SSH_CONFIG_SENSITIVE_RE and apply it inside redact_sensitive_text
  • tests/tools/test_terminal_sudo_timeout.py: 12 new tests
  • tests/agent/test_redact.py: 10 new tests in TestSSHConfigRedaction

How to Test

Fix #1 — sudo timeout reproduction:

# Before this PR: blocks for 45s
hermes chat -q "run sudo cat /etc/sudoers"

# After this PR:
HERMES_SUDO_TIMEOUT=0 hermes chat -q "run sudo cat /etc/sudoers"
# → sudo fails immediately, no 45s wait

Fix #2 — SSH config redaction reproduction:

# Before this PR
hermes chat -q "read ~/.ssh/config"
# → IdentityFile / ProxyJump values visible

# After this PR
hermes chat -q "read ~/.ssh/config"
# → IdentityFile *** / ProxyJump ***
# Host / HostName / User still visible

Test suite:

pytest tests/agent/test_redact.py tests/tools/test_terminal_sudo_timeout.py -v
# 22 new tests pass

pytest tests/agent/test_redact.py tests/tools/test_terminal_*.py -q
# 113 passed (all existing + new)

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(terminal):, fix(redact):)
  • I searched for existing PRs to make sure this isn't a duplicate (noted overlap with security: harden subprocess isolation, add exfiltration detection, fix YOLO bypass #4168 — deliberately scoped around it)
  • My PR contains only changes related to this fix/feature
  • I've run pytest tests/agent/test_redact.py tests/tools/test_terminal_*.py -q and all tests pass
  • I've added tests for my changes
  • I've tested on my platform: macOS 26.3.1 (Apple Silicon), Python 3.11.9

Documentation & Housekeeping

  • Docstrings updated — _prompt_for_sudo_password, _transform_sudo_command note the new env var; _SSH_CONFIG_SENSITIVE_RE has a full design-rationale comment
  • No new config keys added (env var only) — cli-config.yaml.example unchanged, N/A
  • No architecture changes — CONTRIBUTING.md/AGENTS.md unchanged, N/A
  • Cross-platform considered: HERMES_SUDO_TIMEOUT parsing is stdlib-only; the regex in redact.py uses no platform-specific features
  • No tool schema changes, N/A

Relationship to other open PRs

win4r added 2 commits April 9, 2026 03:48
…TIMEOUT

The sudo password prompt timeout was hardcoded to 45 seconds in
`_prompt_for_sudo_password`. In non-interactive automation (cron jobs,
gateway messages, scheduled agents), an accidental sudo invocation would
block the whole run for 45s before gracefully failing — wasted time on
every scheduled tick.

This adds `HERMES_SUDO_TIMEOUT` as an integer environment variable that
controls the prompt timeout. Setting it to `0` skips the prompt entirely
without blocking at all, which is the correct behaviour for cron and
gateway sessions where there is no human at the keyboard anyway.

- `HERMES_SUDO_TIMEOUT` unset → default 45s (existing behaviour)
- `HERMES_SUDO_TIMEOUT=10` → 10s prompt
- `HERMES_SUDO_TIMEOUT=0` → skip immediately, return empty password
- `HERMES_SUDO_TIMEOUT=abc` → warn, fall back to 45s (typos can't
  silently disable the prompt)
- negative values clamp to 0

The callback-based path (used by the interactive CLI) is also guarded:
when timeout is 0 the callback is NOT invoked, so a `HERMES_SUDO_TIMEOUT=0`
gateway session cannot accidentally prompt the user via the UI callback.

Tests: `tests/tools/test_terminal_sudo_timeout.py` (12 cases) covering
env parsing, fast-skip path, and callback suppression.
Reading `~/.ssh/config` via the `read_file` tool previously echoed the
entire contents verbatim, leaking:

  - `IdentityFile` paths (tells an attacker exactly which key files to
    steal — the single most valuable piece of info for credential theft)
  - `ProxyJump` / `ProxyCommand` (enumerates internal bastion hosts and
    the exact commands needed to reach protected networks)
  - `IdentityAgent` / `CertificateFile` (similar metadata exposure)

PR NousResearch#4168 adds an approval-prompt pattern for `cat`/`head`/`tail` on
`.ssh/`, which covers the terminal-tool path. This patch adds the
complementary fix for the `read_file` path: sensitive directives are
masked via the existing `redact_sensitive_text` pipeline that already
handles API keys, tokens, and private key blocks.

The redaction is surgical by design. `Host`, `HostName`, `User`, and
`Port` remain visible because they are frequently needed for legitimate
debugging (and leaking them alone — without the identity file path —
does not materially help an attacker who already has read access).

Matching is case-insensitive per sshd_config(5) and multiline-aware so
each config line is evaluated independently. Values are replaced with
`***` so the structure of the config (which keywords are set, and
where) stays visible.

Tests: `tests/agent/test_redact.py::TestSSHConfigRedaction` (10 cases)
covering each directive, case-insensitivity, full-config structure
preservation, and a negative case ensuring prose mentions of the
keyword are NOT incorrectly redacted.
@alt-glitch alt-glitch added type/security Security vulnerability or hardening tool/terminal Terminal execution and process management tool/file File tools (read, write, patch, search) P2 Medium — degraded but workaround exists labels Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Medium — degraded but workaround exists tool/file File tools (read, write, patch, search) tool/terminal Terminal execution and process management type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants