Skip to content

fix(hermes): chmod 600 config.yaml + pin install URL to commit SHA#39

Open
dgokeeffe wants to merge 1 commit into
mainfrom
fix/hermes-chmod-and-sha-pin
Open

fix(hermes): chmod 600 config.yaml + pin install URL to commit SHA#39
dgokeeffe wants to merge 1 commit into
mainfrom
fix/hermes-chmod-and-sha-pin

Conversation

@dgokeeffe
Copy link
Copy Markdown
Collaborator

Summary

Hotfix for two pre-existing security bugs in setup_hermes.py that affect every CoDA deploy from main today. Both were surfaced by the independent review of #38 (enterprise-proxy-registry) but they are not gated on that feature — they exist on main and are present in every container right now.

Landing them as a small standalone PR for easier review and faster turnaround than waiting for the larger feature work.

F-05 — ~/.hermes/config.yaml written without chmod 0o600

  • What's wrong: config_path.write_text(...) inherits umask-derived permissions (typically 0o644 on container filesystems). The file contains the user's Databricks PAT in plaintext at api_key:. Any process running as the same UID inside the container can cat ~/.hermes/config.yaml and retrieve the token.
  • Fix: explicit config_path.chmod(0o600) after the write. Best-effort (caught OSError) — mirrors the setup_opencode.py:auth_path.chmod(0o600) pattern that's already in place for OpenCode's credentials.
  • Threat scope: in-container exfiltration. A malicious agent CLI install, a planted exfil tool from a compromised npm package, or any sibling process can read the PAT and use it against the workspace API for the token's remaining lifetime.

F-06 — Hermes installed from unpinned HEAD of upstream git

  • What's wrong: HERMES_PKG = "hermes-agent @ git+https://github.com/NousResearch/hermes-agent.git" with no commit pin. Every fresh CoDA container pulls whatever git rev-parse HEAD returns at install time.
  • Fix: pin to commit SHA 8e4f3ba4da5337e1ad674a876ac4fb8490f0b79c (2026-05-08). That SHA is ≥7 days old at the time of pinning, matching the npm cooldown semantics we already apply to opencode/codex/gemini.
  • Threat scope: a single force-push to NousResearch/hermes-agent's default branch — by a compromised maintainer account, a contributor with merge rights, or via gh org admin phish — runs arbitrary code in every CoDA container worldwide on next cold start. Low probability, unbounded blast radius.

Operational note

The pin needs deliberate rotation on each CoDA release. Comment in the code says this explicitly. Don't auto-update — the whole point is that a CoDA-side reviewer signs off when bumping.

Test plan

  • import setup_hermes locally — install runs from the pinned URL, no behavioural change in default flow
  • Verified on daveok via feat(enterprise): proxy/registry mode + security hardening + e2e tests #38's Playwright e2e test (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))
  • Reviewer: smoke-deploy to your workspace and confirm stat -c %a ~/.hermes/config.yaml returns 600

Relation to #38

These two fixes are also included in #38 (enterprise-proxy-registry). Landing them separately because:

  1. They're pre-existing exposures on main, independent of the enterprise feature
  2. Small enough to review in 5 minutes
  3. Won't block on the larger PR's review cycle

When #38 merges, this PR's changes will be a no-op (already present).

This pull request and its description were written by Isaac.

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant