Skip to content

security: harden subprocess isolation, add exfiltration detection, fix YOLO bypass#4168

Closed
SHL0MS wants to merge 1 commit into
NousResearch:mainfrom
SHL0MS:fix/security-hardening
Closed

security: harden subprocess isolation, add exfiltration detection, fix YOLO bypass#4168
SHL0MS wants to merge 1 commit into
NousResearch:mainfrom
SHL0MS:fix/security-hardening

Conversation

@SHL0MS

@SHL0MS SHL0MS commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

Four security improvements from a systematic audit of the agent's subprocess isolation and command approval system.

1. Block cloud provider credentials from subprocesses

The env blocklist in local.py was missing major cloud provider credentials. Added: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AZURE_CLIENT_SECRET, AZURE_CLIENT_ID, AZURE_TENANT_ID, GOOGLE_APPLICATION_CREDENTIALS, KUBECONFIG, DOCKER_HOST, DOCKER_CERT_PATH, SSH_AUTH_SOCK, NPM_TOKEN, PYPI_TOKEN, GPG_AGENT_INFO.

2. Add data exfiltration patterns to DANGEROUS_PATTERNS

The existing patterns only covered destructive operations. Zero coverage for outbound data transfer. Added: netcat to external IPs, bash /dev/tcp and /dev/udp, openssl s_client, curl with data upload, wget with POST data, and credential file reads via cat/head/tail/less/more/bat targeting .ssh/, .env, .gnupg/, .kube/config.

These trigger approval prompts, not hard blocks — the user can still approve if intentional.

3. YOLO_MODE: cached at import, not inherited

HERMES_YOLO_MODE was read from os.getenv() on every approval check. A persistent shell session could export HERMES_YOLO_MODE=1 and all subsequent commands would bypass approval. Now cached as _YOLO_AT_STARTUP at module import time.

4. Git hardening: GIT_TERMINAL_PROMPT=0

Set GIT_TERMINAL_PROMPT=0 in all subprocess environments to prevent git from prompting for credentials on clone. We do NOT set GIT_CONFIG_GLOBAL=/dev/null because that would disable credential helpers, user aliases, and all user git config.

What was removed from the original PR

  • Tirith fail_open=True → False — Reverted. Tirith is rarely installed; fail_closed would block ALL terminal commands for most users.
  • GIT_CONFIG_GLOBAL=/dev/null — Removed. This disabled the user's entire git config (credential helpers, aliases, diff tools). Only GIT_TERMINAL_PROMPT=0 is set.

Changes

49 insertions, 2 deletions across tools/approval.py, tools/environments/local.py.

Ref #4170

@SHL0MS

SHL0MS commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator Author

Ref #4170 — remaining security gaps (credential file read blocking, network egress, redaction expansion, smart approval hard-deny, always-approval scoping, HERMES_REDACT_SECRETS) tracked there.

…x YOLO bypass

Five security improvements from audit:

1. Block cloud provider credentials from subprocesses: AWS_ACCESS_KEY_ID,
   AWS_SECRET_ACCESS_KEY, AZURE_CLIENT_SECRET, GOOGLE_APPLICATION_CREDENTIALS,
   KUBECONFIG, SSH_AUTH_SOCK, and 10 more added to env blocklist.

2. Add data exfiltration patterns to DANGEROUS_PATTERNS: netcat to
   external IPs, bash /dev/tcp and /dev/udp, openssl s_client, curl
   with data upload, wget with POST data, and credential file reads
   via cat/head/tail/less/more/bat targeting .ssh/, .env, .gnupg/, etc.

3. Change tirith default from fail_open=True to fail_open=False. When
   the tirith binary is not installed (common), all security checks
   were silently skipped.

4. Cache HERMES_YOLO_MODE at import time so subprocesses cannot export
   it to bypass approval in subsequent commands within the same session.

5. Set GIT_TERMINAL_PROMPT=0 and GIT_CONFIG_GLOBAL=/dev/null in all
   subprocess environments to prevent malicious repos from executing
   hooks or prompting for credentials on clone.
@SHL0MS SHL0MS force-pushed the fix/security-hardening branch from 020e65b to 9a2c801 Compare April 1, 2026 00:23
win4r added a commit to win4r/hermes-agent that referenced this pull request Apr 9, 2026
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.
SHL0MS added a commit to SHL0MS/hermes-agent that referenced this pull request Apr 11, 2026
Add patterns for: netcat to external IPs, bash /dev/tcp and /dev/udp,
openssl s_client, curl with data upload, wget with POST data, and
credential file reads via cat/head/tail/less/more/bat targeting
.ssh/, .env, .gnupg/, .kube/config, .netrc, aws/credentials.

These trigger approval prompts, not hard blocks. Ref NousResearch#4170.
Split from NousResearch#4168.
SHL0MS added a commit to SHL0MS/hermes-agent that referenced this pull request Apr 11, 2026
… bypass

HERMES_YOLO_MODE was read via os.getenv() on every approval check.
A persistent shell session could export it to bypass approval in
subsequent commands. Now cached as _YOLO_AT_STARTUP at module import.

Updated 3 tests to monkeypatch the cached constant.

Ref NousResearch#4170. Split from NousResearch#4168.
@SHL0MS

SHL0MS commented Apr 11, 2026

Copy link
Copy Markdown
Collaborator Author

Split into 3 focused PRs for easier review:

@SHL0MS SHL0MS closed this Apr 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant