Security Audit #3
Hermes agent configuration review — 06 Aug 2026. Two critical exposures, three unresolved high-risk gaps carried from Audit #2, and a clean bill for a hardened core.
Scope & Severity Summary
Audit #3 of the Hermes agent configuration. Prior audit ran 28 Jun 2026 (Audit #2: 2 Critical / 3 High / 4 Medium). This pass reviews v0.18.2 (2026.7.7.2), upstream commit eb527605, local 7b5ba205.
| Severity | Count | Status |
|---|---|---|
| Critical | 2 | 1 live credential exposure + 1 silent no-op guardrail |
| High | 2 | both carried over from Audit #2, still unresolved |
| Medium | 4 | hardening opportunities, ~1-5 min each |
| Low | 4 | file-permission cleanup + note |
| Passing | 7 | secrets, firewall, SSRF, MCP, cron — all clean |
Critical Findings
Two genuine exposures — one is the only real credential leak found in this audit.
custom_providers[Qwen3.6].api_key holds a real 67-char key. Secrets belong in .env (mode 600), not plaintext config. This is the one genuine credential exposure found.Fix: move to
.env as QWEN36_API_KEY=..., reference in config as api_key: ${QWEN36_API_KEY} or provider env var. ~2 min.
config.yaml has tirith_enabled: true, tirith_path: tirith, tirith_fail_open: false — but which tirith and pip show tirith both fail. The config promises prompt-injection/tenant guardrails that aren't actually running. Fail-open is false, so either it's silently no-oping or blocking tool calls unpredictably. False sense of security.Fix: install tirith, or set
tirith_enabled: false to stop advertising protection that isn't there. ~5-10 min.
High Findings
Both carried over from Audit #2 and still not addressed — they are the largest residual risk surface.
Fix: staged upgrade with backup + rollback plan. Effort high, biggest payoff.
~30+ min.
allowed_chats is empty: Only TELEGRAM_ALLOWED_USERS (user-ID) restricts. If the bot gets added to an untrusted group, any chat can trigger it. Carried from Audit #2.Fix: set
allowed_chats to your chat ID (944548986). ~1 min.
Medium Findings
Hardening opportunities — none are active breaches, but each closes a supply-chain or data-handling gap.
allow_lazy_installs: true: Agent may auto-install packages mid-task without asking (supply-chain risk). INSTALL GATE memory already covers this — make it enforced in config. ~1 min.
redact_pii: false: Sensitive personal data not auto-redacted in output. ~1 min to enable.
website_blocklist enabled but domains: []: Framework armed, zero domains listed. Not a risk by itself, but no active protection. ~1 min to populate.
approvals.mode: manual + auxiliary.approval unset: Manual mode only gates destructive patterns (rm, git reset, dd); most commands auto-approve silently. With cron_mode: deny, cron is safe, but interactive sessions don't ask before non-destructive ops. Consider smart mode with an approval model. ~5 min.
Low Findings
Housekeeping — mostly file-permission hardening; quick wins that reduce local attack surface.
google_token.json (live access + refresh tokens) and google_client_secret.json both 644. Should be 600. ~1 min: chmod 600.
hermes_state.db + state.db (1.1GB) + kanban.db 644: Full conversation history / state DBs world-readable. Fix early. ~1 min.
config.yaml.bak.20260629_223258 is 644 (contains config, possibly creds). Newer backups are 600. ~1 min.
Passing Checks
Seven controls verified clean — the hardened core. Notable improvement: SSRF defense is now explicit in both contexts.
.env mode 600 (owner-only), all keys redacted in audit.✅ 2.
.env in .gitignore; never committed to git history.✅ 3. macOS firewall enabled (State=1).
✅ 4. Ollama binds
127.0.0.1:11434 only (not 0.0.0.0).✅ 5.
allow_private_urls: false in BOTH browser and security contexts (SSRF defended — IMPROVED since Audit #2, which flagged it true).✅ 6. MCP servers are local binaries only; no remote endpoints; no API keys in MCP env vars.
✅ 7. Cron jobs: 15/17 disabled; only Macro Watch + one-shot gbrain reminder active. Delivery targets all trusted (telegram/origin/local).
✅ 8. No secrets in MCP server env.
Prioritized Fix Table
All 12 findings ranked by severity × effort — highest risk for least effort first. The two Critical fixes total ~12 minutes.
| # | Finding | Sev | Effort | Fix |
|---|---|---|---|---|
| 1 | Qwen key in config.yaml | Critical | 2 min | move to .env |
| 2 | Tirith enabled, not installed | Critical | 5-10 min | install or disable |
| 4 | allowed_chats empty | High | 1 min | set chat ID |
| 3 | 19,616 commits / 3,109 sec behind | High | 30+ min | staged upgrade |
| 5 | lazy installs on | Medium | 1 min | allow_lazy_installs: false |
| 6 | redact_pii off | Medium | 1 min | enable |
| 7 | blocklist empty | Medium | 1 min | populate domains |
| 8 | manual approvals | Medium | 5 min | consider smart mode |
| 9 | google tokens 644 | Low | 1 min | chmod 600 |
| 10 | state/kanban db 644 | Low | 1 min | chmod 600 |
| 11 | stale config backup 644 | Low | 1 min | chmod 600 |
| 12 | no guest profile | Low | — | n/a |
~/.hermes/config.yaml, .env, MCP server config, cron jobs, and file permissions on 06 Aug 2026. Findings verified live where applicable (e.g. which tirith, pip show tirith, chmod state, git history). Effort estimates are single-operator minutes. No external systems consulted — this is an internal configuration audit.