From 1f342800eecb2f3813a559d28f0e82d60ab03262 Mon Sep 17 00:00:00 2001 From: David O'Keeffe Date: Sat, 16 May 2026 21:21:59 +1000 Subject: [PATCH] fix(hermes): chmod 600 config.yaml + pin install URL to commit SHA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two pre-existing security bugs in setup_hermes.py, surfaced by the independent review of the enterprise-mode feature but not gated on it. Affects every CoDA deploy from main today. F-05: ~/.hermes/config.yaml written without chmod 600 (plaintext PAT) ================================================================ `config_path.write_text(...)` inherits umask-derived permissions — typically 0o644 on container filesystems — making the embedded PAT readable by any process running as the same UID. Add explicit `config_path.chmod(0o600)` after the write, mirroring what setup_opencode.py already does for its auth.json. Threat: any in-container process (a malicious agent CLI install, a planted exfil tool, a compromised dep) can `cat ~/.hermes/config.yaml` and retrieve the current Databricks PAT. F-06: Hermes installed from unpinned HEAD of upstream git ================================================================ `HERMES_PKG = "hermes-agent @ git+https://github.com/NousResearch/hermes-agent.git"` with no commit pin. Every fresh CoDA container pulls whatever is at HEAD of NousResearch's default branch at install time. Threat: a single force-push to NousResearch/hermes-agent's default branch (compromised maintainer account, contributor with merge rights, gh org admin phish) ships arbitrary code into every CoDA container worldwide on next cold start. Mitigation: pin to commit 8e4f3ba4da5337e1ad674a876ac4fb8490f0b79c (2026-05-08). That SHA is >7 days old at time of pinning, matching the npm cooldown semantics we already apply elsewhere. Documented inline that this needs deliberate rotation on each CoDA release — not auto-update. Verified ================================================================ - Local `import setup_hermes` reproduces the install from the pinned URL successfully. - Independent test on daveok (today, via Playwright e2e against the enterprise feature branch which has the same fixes): [PASS] F-05 Hermes config chmod 0o600 [PASS] F-06 Hermes installed (Hermes Agent v0.13.0 (2026.5.7)) These two fixes are also part of the larger #38 enterprise-proxy-registry PR. Landing them as a separate hotfix because they're pre-existing exposures on main, not gated on the enterprise feature shipping. Co-authored-by: Isaac --- setup_hermes.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/setup_hermes.py b/setup_hermes.py index 07bb030..9f42b1e 100644 --- a/setup_hermes.py +++ b/setup_hermes.py @@ -50,7 +50,16 @@ # httpx, pyyaml, pydantic) cover chat + Databricks model serving. Not on PyPI, # so we install directly from GitHub. uv tool install handles venv + binary. # The mcp package is needed for HTTP transport (DeepWiki, Exa MCP servers). -HERMES_PKG = "hermes-agent @ git+https://github.com/NousResearch/hermes-agent.git" +# +# Pinned to a specific commit SHA (≥7 days old at the time of pinning) so a +# force-push to NousResearch/hermes-agent's default branch can't ship code +# into every CoDA container on the next cold start. Bump deliberately on +# CoDA releases; do not auto-update. +HERMES_PIN_SHA = "8e4f3ba4da5337e1ad674a876ac4fb8490f0b79c" # 2026-05-08 +HERMES_PKG = ( + "hermes-agent @ git+https://github.com/NousResearch/hermes-agent.git" + f"@{HERMES_PIN_SHA}" +) HERMES_EXTRA_DEPS = ["mcp>=1.2.0"] # 1. Install Hermes Agent (always, even without token). @@ -193,6 +202,15 @@ def _run(cmd, **kwargs): if should_write: config_path.write_text("\n".join(lines)) + # 0o600 — the file contains the plaintext PAT in `api_key:`. Without an + # explicit chmod the file inherits umask-derived perms (often 0o644 on + # container filesystems), making the token readable by any other process + # under the same UID. Matches setup_opencode.py's auth.json handling. + try: + config_path.chmod(0o600) + except OSError: + # Best effort — chmod can fail on some workspace filesystems. + pass print(f"Hermes config written: {config_path}") # 5. Adapt CLAUDE.md -> ~/.hermes/HERMES.md for first-run context