fix(security): add auth.json to build_write_denied_paths - #70967
fix(security): add auth.json to build_write_denied_paths#70967toprakeker wants to merge 2 commits into
Conversation
|
Thanks for addressing the verified Problems
Suggested changes
Automated hermes-sweeper review. |
|
Thanks — good catch. I added |
SummaryOne PR directly addresses and closes #70942. #70967 prevents agent-mediated destruction of active-profile and global-root credential stores, while its auth.json protection reverses the deliberate write policy documented for #45947. Related pull requests
Suggested consolidationKeep open with a salvage path: #70967 is the sole Verify-selected best fix, and its cache/bws_cache.json protection plus profile/root regression coverage are independently useful, but maintainers must explicitly decide whether to reverse #45947's deliberate auth.json writability before the broader change can proceed. There are no competing PRs to close as duplicates. Complex graphflowchart LR
classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
classDef best stroke-width:3px,stroke:#b45309
classDef target stroke-width:3px,stroke:#4338ca
I70942(["issue #70942 (open)"])
P70967["PR #70967 (open)"]
P70967 -->|best fix| I70942
class I70942 open
class P70967 open
class P70967 best
class P70967 target
click I70942 "https://github.com/NousResearch/hermes-agent/issues/70942"
click P70967 "https://github.com/NousResearch/hermes-agent/pull/70967"
Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label). Cross-PR triage: Reviewed 1 pull request and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 6 kB of PR diffs, 13 kB of issue/PR text, 3 kB of discussion (5 comments), 2 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch. |
…h#70942) auth.json is the provider credential store (OAuth refresh tokens, API keys, the whole credential_pool). It was already read-denied via get_read_block_error()'s credential_file_names tuple, but missing from build_write_denied_paths() — so the agent's own write_file, patch, delete_path, and move_file (plus the ACP fs/write_text_file shim) could overwrite or delete it with no guard at all. Mirrors the existing .env / .anthropic_oauth.json handling: denies both the active-profile path and the top-level root path (same root-widening shape as NousResearch#15981), so the credential store stays protected whether or not a profile is active. Also adds auth.lock, auth/google_oauth.json, and webhook_subscriptions.json to the write denylist — these were already in get_read_block_error()'s credential_file_names tuple but had the same write-side gap as auth.json. Updates the existing test that asserted auth.json/webhook_subscriptions.json were writable (that assertion encoded the bug) and adds a TestWriteDenyCredentialStore class covering each new path plus the root-widening-under-profile case. Tests: 213/213 passing across all file_safety/write_deny/credential test files (tests/agent/test_file_safety*.py, tests/tools/test_write_deny.py, tests/tools/test_credential_files.py, tests/tools/test_cross_profile_guard.py, tests/tools/test_file_write_safety.py) — no regressions.
|
Rebased onto current New head: Verification: The remaining non-technical blocker is still the policy decision called out by triage: whether maintainers want to reverse the prior |
605ffca to
872322a
Compare
|
Closing this older pre-contributor branch rather than asking maintainers to approve stale fork CI. The codebase has moved substantially since it was opened; if the underlying issue remains, I will re-evaluate it against current main and submit a fresh, current PR. |
Fixes #70942
Summary
build_write_denied_paths()inagent/file_safety.pyprotects.env,.anthropic_oauth.json, SSH keys, and a handful of other credentialfiles against writes/deletes/moves by the agent's own file tools.
auth.json— the actual provider credential store (OAuth refreshtokens, API keys, the whole
credential_pool) — was missing from thatlist entirely.
The asymmetry that gave this away:
get_read_block_error()in thesame module already treats
auth.jsonas a credential store andblocks reads of it. So the agent could not read
auth.json, butcould freely overwrite or delete it via
write_file,patch_replace,delete_path,move_file, or the ACPfs/write_text_fileshim —the read-deny arguably made this worse, since the agent can't inspect
what it's about to destroy before destroying it.
Fix
Mirrors the existing
.env/.anthropic_oauth.jsonhandling:auth.jsonat both the active-profile path and the top-levelroot path (same root-widening shape as write_file tool bypasses credential protection for global ~/.hermes/.env #15981, so the store stays
protected whether or not a profile is active).
auth.lock,auth/google_oauth.json, andwebhook_subscriptions.json— these were already read-deniedalongside
auth.jsoninget_read_block_error()'scredential_file_namestuple but had the same write-side gap.Tests
test_hermes_control_files_requested_writable— itpreviously asserted
auth.jsonandwebhook_subscriptions.jsonwere writable, which encoded the bug. Now only asserts
config.yamlis writable (still correct — config isn't a credential store).
TestWriteDenyCredentialStorewith 5 new tests: each new pathindividually, plus a root-widening-under-profile case matching the
existing
.envcoverage.Ran the full file-safety/credential test surface locally, no
regressions: