fix(security): protect Hermes control-plane files from prompt injection

Adds active-HERMES_HOME control-plane files to the write deny list:
auth.json, config.yaml, webhook_subscriptions.json, and any path
under mcp-tokens/. realpath() resolves before comparison so
directory-traversal and symlink targets are normalised, preventing
trivial deny-list bypass via ../ tricks.

Without this, a prompt-injected agent could rewrite Hermes' own
auth state or routing config via write_file / patch — without
triggering the terminal dangerous-command approval — and persist
attacker-controlled behaviour across sessions.

Fixes #14072
This commit is contained in:
Pratik Rai 2026-04-23 02:29:22 +05:30 committed by Teknium
parent 6f436a463e
commit 1f5219fda5
2 changed files with 67 additions and 0 deletions

View file

@ -60,6 +60,50 @@ class TestIsWriteDenied:
def test_tilde_expansion(self):
assert _is_write_denied("~/.ssh/authorized_keys") is True
@pytest.mark.parametrize(
"path",
[
"auth.json",
"config.yaml",
"webhook_subscriptions.json",
"mcp-tokens/token1.json",
"mcp-tokens/subdir/token2.json",
],
)
def test_hermes_control_files_and_mcp_tokens_denied(self, path):
"""Hermes control files and mcp-tokens entries must be write-denied."""
from hermes_constants import get_hermes_home
hermes_home = get_hermes_home()
full_path = str(hermes_home / path)
assert _is_write_denied(full_path) is True
@pytest.mark.parametrize(
"path",
[
"dummy/../config.yaml",
"./auth.json",
"mcp-tokens/../config.yaml",
],
)
def test_hermes_control_files_traversal_denied(self, path):
"""Path traversal attempts to control files must be blocked by realpath."""
from hermes_constants import get_hermes_home
hermes_home = get_hermes_home()
full_path = str(hermes_home / path)
assert _is_write_denied(full_path) is True
@pytest.mark.parametrize(
"path",
[
"/tmp/standard_file.txt",
"~/projects/myapp/main.py",
"/var/log/app.log",
],
)
def test_standard_paths_allowed(self, path):
"""Unrelated paths must still be allowed."""
assert _is_write_denied(path) is False
# =========================================================================