fix(web-server): close OAuth token TOCTOU by writing 0o600 atomically - #58006
fix(web-server): close OAuth token TOCTOU by writing 0o600 atomically#58006Que0x wants to merge 1 commit into
Conversation
`_save_anthropic_oauth_creds` wrote the Anthropic OAuth token file with `os.replace(tmp, path)` followed by a post-hoc `chmod(0o600)`. Between the rename and the chmod the token file existed at the default umask (0o644 on most hosts) — a window in which another local user could read the access/refresh tokens. Write via `utils.atomic_json_write(..., mode=0o600)`, which creates the temp with mode 0o600 *before* any content is written, fsyncs, atomically replaces, preserves the existing file's owner, and cleans up its temp on failure. This matches the `atomic_json_write(mode=0o600)` call already used elsewhere in this module for the credential-pool write, and NousResearch#56644's owner preservation. Tests updated for the new mechanism, plus a check that the write goes through `atomic_json_write(mode=0o600)` (mutation-verified).
|
looks mergeable Security evidence: The affected boundary is Anthropic OAuth access/refresh token persistence from the dashboard OAuth callback into Signed: GPT-5.5-xhigh in Codex |
|
Merged via PR #60236 (#60236) — your commit was cherry-picked onto current main with your authorship preserved in git log. The only adjustment was reapplying it onto the per-profile |
What does this PR do?
_save_anthropic_oauth_credspersists the Anthropic OAuth token file withos.replace(tmp, path)followed by a post-hocchmod(0o600):Between the rename and the chmod the token file exists at the default umask
(0o644 on most hosts), so for that window another local user can read the
access/refresh tokens.
Write via
utils.atomic_json_write(_HERMES_OAUTH_FILE, payload, indent=2, mode=0o600), which creates the temp with mode 0o600 before any content iswritten (
mkstemp+fchmod), fsyncs, atomically replaces, preserves theexisting file's owner, and cleans up its temp on failure. This is the same
atomic_json_write(mode=0o600)pattern already used elsewhere in this modulefor the credential-pool write, and mirrors the owner preservation from #56644.
Changes
hermes_cli/web_server.py— persist OAuth creds viaatomic_json_write(mode=0o600).tests/hermes_cli/test_web_server_oauth_write.py— update the atomicity testto the new mechanism and assert the write uses
mode=0o600.Tests
The write now goes through
atomic_json_write(mode=0o600)(mutation-verified —reverting drops the call and the assertion fails), and a simulated replace
failure leaves no partial or temp file behind.