Skip to content

Commit 3dd35a9

Browse files
teknium1Patrick Daley
authored andcommitted
feat(security): make secret redaction off by default (#16794)
Flips security.redact_secrets from true to false in DEFAULT_CONFIG, and the HERMES_REDACT_SECRETS env-var fallback in agent/redact.py now requires explicit opt-in ("1"/"true"/"yes"/"on") to enable. New installs and users without a security.redact_secrets key get pass- through tool output. Existing users whose config.yaml explicitly sets redact_secrets: true keep redaction on — the config-yaml -> env-var bridges in hermes_cli/main.py and gateway/run.py still honor their setting. Also updates the inline config comments, website docs, and the hermes-agent skill so /hermes config set security.redact_secrets true is now the documented way to turn it on.
1 parent 2345217 commit 3dd35a9

5 files changed

Lines changed: 76 additions & 20 deletions

File tree

agent/redact.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,12 @@
5656
})
5757

5858
# Snapshot at import time so runtime env mutations (e.g. LLM-generated
59-
# `export HERMES_REDACT_SECRETS=false`) cannot disable redaction mid-session.
60-
_REDACT_ENABLED = os.getenv("HERMES_REDACT_SECRETS", "").lower() not in ("0", "false", "no", "off")
59+
# `export HERMES_REDACT_SECRETS=true`) cannot enable/disable redaction
60+
# mid-session. OFF by default — user must opt in via
61+
# `security.redact_secrets: true` in config.yaml (bridged to this env var
62+
# in hermes_cli/main.py and gateway/run.py) or `HERMES_REDACT_SECRETS=true`
63+
# in ~/.hermes/.env.
64+
_REDACT_ENABLED = os.getenv("HERMES_REDACT_SECRETS", "").lower() in ("1", "true", "yes", "on")
6165

6266
# Known API key prefixes -- match the prefix + contiguous token chars
6367
_PREFIX_PATTERNS = [
@@ -257,7 +261,7 @@ def redact_sensitive_text(text: str) -> str:
257261
"""Apply all redaction patterns to a block of text.
258262
259263
Safe to call on any string -- non-matching text passes through unchanged.
260-
Disabled when security.redact_secrets is false in config.yaml.
264+
Disabled by default — enable via security.redact_secrets: true in config.yaml.
261265
"""
262266
if text is None:
263267
return None

hermes_cli/config.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,7 @@ def _ensure_hermes_home_managed(home: Path):
942942
# Pre-exec security scanning via tirith
943943
"security": {
944944
"allow_private_urls": False, # Allow requests to private/internal IPs (for OpenWrt, proxies, VPNs)
945-
"redact_secrets": True,
945+
"redact_secrets": False,
946946
"tirith_enabled": True,
947947
"tirith_path": "tirith",
948948
"tirith_timeout": 5,
@@ -3337,14 +3337,16 @@ def load_config() -> Dict[str, Any]:
33373337

33383338
_SECURITY_COMMENT = """
33393339
# ── Security ──────────────────────────────────────────────────────────
3340-
# API keys, tokens, and passwords are redacted from tool output by default.
3341-
# Set to false to see full values (useful for debugging auth issues).
3340+
# Secret redaction is OFF by default — tool output (terminal stdout,
3341+
# read_file results, web content) passes through unmodified. Set
3342+
# redact_secrets to true to mask strings that look like API keys, tokens,
3343+
# and passwords before they enter the model context and logs.
33423344
# tirith pre-exec scanning is enabled by default when the tirith binary
33433345
# is available. Configure via security.tirith_* keys or env vars
33443346
# (TIRITH_ENABLED, TIRITH_BIN, TIRITH_TIMEOUT, TIRITH_FAIL_OPEN).
33453347
#
33463348
# security:
3347-
# redact_secrets: false
3349+
# redact_secrets: true
33483350
# tirith_enabled: true
33493351
# tirith_path: "tirith"
33503352
# tirith_timeout: 5
@@ -3377,11 +3379,11 @@ def load_config() -> Dict[str, Any]:
33773379

33783380
_COMMENTED_SECTIONS = """
33793381
# ── Security ──────────────────────────────────────────────────────────
3380-
# API keys, tokens, and passwords are redacted from tool output by default.
3381-
# Set to false to see full values (useful for debugging auth issues).
3382+
# Secret redaction is OFF by default. Set to true to mask strings that
3383+
# look like API keys, tokens, and passwords in tool output and logs.
33823384
#
33833385
# security:
3384-
# redact_secrets: false
3386+
# redact_secrets: true
33853387
33863388
# ── Fallback Model ────────────────────────────────────────────────────
33873389
# Automatic provider failover when primary is unavailable.

skills/autonomous-ai-agents/hermes-agent/SKILL.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -408,17 +408,17 @@ Common "why is Hermes doing X to my output / tool calls / commands?" toggles —
408408

409409
### Secret redaction in tool output
410410

411-
Hermes auto-redacts strings that look like API keys, tokens, and secrets in all tool output (terminal stdout, `read_file`, web content, subagent summaries, etc.) so the model never sees raw credentials. If the user is intentionally working with mock tokens, share-management tokens, or their own secrets and the redaction is getting in the way:
411+
Secret redaction is **off by default**tool output (terminal stdout, `read_file`, web content, subagent summaries, etc.) passes through unmodified. If the user wants Hermes to auto-mask strings that look like API keys, tokens, and secrets before they enter the conversation context and logs:
412412

413413
```bash
414-
hermes config set security.redact_secrets false # disable globally
414+
hermes config set security.redact_secrets true # enable globally
415415
```
416416

417-
**Restart required.** `security.redact_secrets` is snapshotted at import time — setting it mid-session (e.g. via `export HERMES_REDACT_SECRETS=false` from a tool call) will NOT take effect for the running process. Tell the user to run `hermes config set security.redact_secrets false` in a terminal, then start a new session. This is deliberate — it prevents an LLM from turning off redaction on itself mid-task.
417+
**Restart required.** `security.redact_secrets` is snapshotted at import time — toggling it mid-session (e.g. via `export HERMES_REDACT_SECRETS=true` from a tool call) will NOT take effect for the running process. Tell the user to run `hermes config set security.redact_secrets true` in a terminal, then start a new session. This is deliberate — it prevents an LLM from flipping the toggle on itself mid-task.
418418

419-
Re-enable with:
419+
Disable again with:
420420
```bash
421-
hermes config set security.redact_secrets true
421+
hermes config set security.redact_secrets false
422422
```
423423

424424
### PII redaction in gateway messages

tests/hermes_cli/test_redact_config_bridge.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,12 @@ def test_redact_secrets_false_in_config_yaml_is_honored(tmp_path):
7272
assert "ENV_VAR=false" in result.stdout
7373

7474

75-
def test_redact_secrets_default_true_when_unset(tmp_path):
76-
"""Without the config key, redaction stays on by default."""
75+
def test_redact_secrets_default_false_when_unset(tmp_path):
76+
"""Without the config key, redaction stays OFF by default.
77+
78+
Secret redaction is opt-in — users who want it must set
79+
`security.redact_secrets: true` explicitly (or HERMES_REDACT_SECRETS=true).
80+
"""
7781
hermes_home = tmp_path / ".hermes"
7882
hermes_home.mkdir()
7983
(hermes_home / "config.yaml").write_text("{}\n") # empty config
@@ -103,7 +107,53 @@ def test_redact_secrets_default_true_when_unset(tmp_path):
103107
timeout=30,
104108
)
105109
assert result.returncode == 0, f"probe failed: {result.stderr}"
106-
assert "REDACT_ENABLED=True" in result.stdout
110+
assert "REDACT_ENABLED=False" in result.stdout
111+
112+
113+
def test_redact_secrets_true_in_config_yaml_is_honored(tmp_path):
114+
"""Setting `security.redact_secrets: true` in config.yaml must enable
115+
redaction — even though it's set in YAML, not as an env var."""
116+
hermes_home = tmp_path / ".hermes"
117+
hermes_home.mkdir()
118+
(hermes_home / "config.yaml").write_text(
119+
textwrap.dedent(
120+
"""\
121+
security:
122+
redact_secrets: true
123+
"""
124+
)
125+
)
126+
(hermes_home / ".env").write_text("")
127+
128+
probe = textwrap.dedent(
129+
"""\
130+
import sys, os
131+
os.environ.pop("HERMES_REDACT_SECRETS", None)
132+
sys.path.insert(0, %r)
133+
import hermes_cli.main
134+
import agent.redact
135+
print(f"REDACT_ENABLED={agent.redact._REDACT_ENABLED}")
136+
print(f"ENV_VAR={os.environ.get('HERMES_REDACT_SECRETS', '<unset>')}")
137+
"""
138+
) % str(REPO_ROOT)
139+
140+
env = dict(os.environ)
141+
env["HERMES_HOME"] = str(hermes_home)
142+
env.pop("HERMES_REDACT_SECRETS", None)
143+
144+
result = subprocess.run(
145+
[sys.executable, "-c", probe],
146+
env=env,
147+
capture_output=True,
148+
text=True,
149+
cwd=str(REPO_ROOT),
150+
timeout=30,
151+
)
152+
assert result.returncode == 0, f"probe failed: {result.stderr}"
153+
assert "REDACT_ENABLED=True" in result.stdout, (
154+
f"Config toggle not honored.\nstdout: {result.stdout}\nstderr: {result.stderr}"
155+
)
156+
assert "ENV_VAR=true" in result.stdout
107157

108158

109159
def test_dotenv_redact_secrets_beats_config_yaml(tmp_path):

website/docs/user-guide/configuration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,7 +1302,7 @@ Pre-execution security scanning and secret redaction:
13021302

13031303
```yaml
13041304
security:
1305-
redact_secrets: true # Redact API key patterns in tool output and logs
1305+
redact_secrets: false # Redact API key patterns in tool output and logs (off by default)
13061306
tirith_enabled: true # Enable Tirith security scanning for terminal commands
13071307
tirith_path: "tirith" # Path to tirith binary (default: "tirith" in $PATH)
13081308
tirith_timeout: 5 # Seconds to wait for tirith scan before timing out
@@ -1313,7 +1313,7 @@ security:
13131313
shared_files: []
13141314
```
13151315

1316-
- `redact_secrets` — automatically detects and redacts patterns that look like API keys, tokens, and passwords in tool output before it enters the conversation context and logs.
1316+
- `redact_secrets` when `true`, automatically detects and redacts patterns that look like API keys, tokens, and passwords in tool output before it enters the conversation context and logs. **Off by default** — enable if you commonly work with real credentials in tool output and want a safety net. Set to `true` explicitly to turn on.
13171317
- `tirith_enabled` — when `true`, terminal commands are scanned by [Tirith](https://github.com/StackGuardian/tirith) before execution to detect potentially dangerous operations.
13181318
- `tirith_path` — path to the tirith binary. Set this if tirith is installed in a non-standard location.
13191319
- `tirith_timeout` — maximum seconds to wait for a tirith scan. Commands proceed if the scan times out.

0 commit comments

Comments
 (0)