Skip to content

fix(security): use crypto randomness for temp-file staging paths and recovery ids - #517

Merged
ndycode merged 5 commits into
mainfrom
claude/audit-01-temp-path-entropy
Jun 11, 2026
Merged

fix(security): use crypto randomness for temp-file staging paths and recovery ids#517
ndycode merged 5 commits into
mainfrom
claude/audit-01-temp-path-entropy

Conversation

@ndycode

@ndycode ndycode commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Summary

Part 1 of the repo-wide architecture/security audit (companion PRs: packaging hygiene, CI action pinning, docs corrections, and the audit report itself).

The identical temp-file suffix pattern ${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp was copy-pasted across 18 files. Math.random() is not cryptographically secure — once its internal state is observed, future outputs are predictable, so a local attacker could anticipate the staged path of an atomic write-then-rename and pre-create (or symlink) it before the rename lands. Several of these staged files carry OAuth account data.

Changes

  • New lib/temp-path.ts with two helpers:
    • tempFileNonce()<pid>.<epochMs>.<hex8> (CSPRNG suffix via crypto.randomBytes)
    • tempPathFor(target)<target>.<nonce>.tmp — keeps the trailing .tmp extension relied on by the stale-temp sweepers in lib/storage.ts, lib/runtime-paths.ts, and lib/oc-chatgpt-target-detection.ts
  • Replaced all 18 inline patterns (storage, config, unified-settings, quota-cache, budget-guard, account-policy, routing-profiles, local-client-tokens, update-notice, codex-cli writer, oc-chatgpt orchestrator, prompt caches, report/usage commands, flagged-storage-io, import-export, recovery)
  • lib/recovery/storage.ts: generatePartId() / generateThinkingPartId() now use randomBytes instead of Math.random; the recovery atomic-write suffix keeps its .tmp.<nonce> shape (pinned by test/recovery-storage.test.ts) but gains the crypto nonce
  • Timing-jitter Math.random() uses (backoff, rotation delays, device-auth poll jitter) are intentionally untouched — non-security uses

Why this is also a refactor win

This removes 18 copies of the same one-liner and gives the codebase a single audited place to evolve temp-file naming (e.g. future O_EXCL staging).

Testing

  • npm run typecheck
  • eslint on changed files ✅
  • New test/temp-path.test.ts (shape + collision tests) ✅
  • All test suites covering the 18 touched modules pass: recovery-storage, flagged-storage-io, quota-cache, routing-profiles, local-client-tokens, account-policy, unified-settings, budget-guard, update-notice, config, codex-cli-writer, codex-prompts, codex-manager-report/usage-command, import-export, storage-import-export, oc-chatgpt-orchestrator ✅
  • test/storage.test.ts has 23 pre-existing failures in this sandbox (EACCES — environment-only, identical on a clean checkout of main)

https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB


Generated by Claude Code

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

centralizes 18 copy-pasted atomic-write temp-path patterns into a single lib/temp-path.ts module, replacing Math.random() with crypto.randomBytes(4) to prevent local attackers from predicting staged file paths. the recovery/storage.ts id generators (generatePartId, generateThinkingPartId) are independently hardened with randomBytes.

  • new lib/temp-path.ts exports tempFileNonce() (nonce only) and tempPathFor(target) (full staging path with .tmp suffix); both are re-exported via lib/index.ts as part of the public api surface.
  • 18 write sites are cleanly swapped to tempPathFor/tempFileNonce; the two-file prompt-cache writers correctly call tempFileNonce() once and share the nonce so both temp files are renamed atomically.
  • recovery module keeps its .tmp.<nonce> ordering (intentional, pinned by existing tests) and adds direct randomBytes calls for part-id generation; new vitest coverage in test/temp-path.test.ts and test/quota-cache.test.ts validates shape, FIPS-failure propagation, and the rename-spy contract.

Confidence Score: 5/5

safe to merge — all 18 write sites are correctly migrated, the shared-nonce pattern for two-file prompt-cache writes is preserved, and the recovery module's intentionally different temp suffix ordering is unchanged.

every callsite replacement is a clean one-liner swap; the new module is simple, well-documented, and covered by shape/uniqueness/FIPS-failure vitest cases; the rotation nonce sharing in storage.ts and the isRotatingBackupTempArtifact parser both remain correct under the new dot-separated format.

no files require special attention.

Important Files Changed

Filename Overview
lib/temp-path.ts new module — clean CSPRNG nonce implementation; jsdoc explicitly documents the fail-loud assumption for randomBytes; no Math.random fallback path.
lib/recovery/storage.ts atomicWriteFileSync uses .tmp. ordering (intentionally distinct from tempPathFor's .tmp so sweepers don't interfere); generatePartId/generateThinkingPartId now use randomBytes directly — correct.
lib/index.ts re-exports tempFileNonce and tempPathFor as public API via barrel; follows lib/AGENTS.md convention; no name conflicts with existing exports.
test/temp-path.test.ts covers shape, uniqueness (200 draws), FIPS failure propagation via vi.hoisted/vi.mock pattern, and Windows-style path handling; error-path test added for randomBytes throw.
test/quota-cache.test.ts new staging-contract test uses vi.spyOn on node:fs.promises.rename to verify the tempPathFor nonce regex and confirms no orphan .tmp files remain after a successful save.
lib/storage.ts rotation batch correctly calls tempFileNonce() once and shares the nonce across all slot-N staged paths; isRotatingBackupTempArtifact parser is unaffected by the new dot-separated nonce format.
lib/local-client-tokens.ts token write path now uses tempPathFor; pid is included in the nonce ensuring different concurrent writers (multi-process scenarios on windows) produce distinct staging paths.
lib/prompts/codex.ts correctly calls tempFileNonce() once and reuses the nonce for both contentTmp and metaTmp — the shared-nonce pattern ensures both halves of the two-file atomic write are grouped under one identifier.
lib/prompts/host-codex-prompt.ts same shared-nonce pattern as prompts/codex.ts — one tempFileNonce() call for both cache temp files; clean replacement.
lib/account-policy.ts single-line swap to tempPathFor; write mode and retry logic unchanged.
lib/budget-guard.ts single-line swap to tempPathFor; no behavioral changes outside the nonce source.
lib/storage/flagged-storage-io.ts replaced two-line uniqueSuffix construction with tempPathFor; mode and error handling unchanged.
lib/storage/import-export.ts same two-line to one-line consolidation via tempPathFor; clean.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    TP["lib/temp-path.ts\ntempFileNonce() / tempPathFor()"]

    subgraph account_writes["account / config writes"]
        AP[account-policy.ts]
        BG[budget-guard.ts]
        LC[local-client-tokens.ts]
        QC[quota-cache.ts]
        RP[routing-profiles.ts]
        ST[storage.ts]
        US[unified-settings.ts]
        CF[config.ts]
        FI[storage/flagged-storage-io.ts]
        IE[storage/import-export.ts]
    end

    subgraph cli_writes["cli / orchestrator writes"]
        CW[codex-cli/writer.ts]
        OR[oc-chatgpt-orchestrator.ts]
        RM[commands/report.ts]
        UM[commands/usage.ts]
        UN[update-notice.ts]
    end

    subgraph prompt_cache["prompt-cache writes (shared nonce pattern)"]
        PC[prompts/codex.ts]
        HP[prompts/host-codex-prompt.ts]
    end

    subgraph recovery["lib/recovery/storage.ts atomicWriteFileSync"]
        RS[".tmp.<nonce> suffix (intentional, differs from .tmp)"]
    end

    TP --> account_writes
    TP --> cli_writes
    TP --> prompt_cache
    TP --> recovery

    CRYPTO["node:crypto randomBytes(4)"] --> TP
    CRYPTO --> RS
Loading

Reviews (5): Last reviewed commit: "test: exercise the tempPathFor staging c..." | Re-trigger Greptile

…recovery ids

Replace the 18 copy-pasted `Math.random().toString(36)` temp-path suffix
patterns with a shared lib/temp-path.ts helper backed by crypto.randomBytes.
Math.random() is predictable once its state is observed, so a local attacker
could anticipate the staged path of an atomic write-then-rename and
pre-create or symlink it before the rename lands.

- new tempPathFor()/tempFileNonce() helpers (<path>.<pid>.<epoch>.<hex8>.tmp)
  keep the trailing .tmp extension relied on by stale-temp sweepers
- recovery part ids (generatePartId/generateThinkingPartId) now use
  randomBytes instead of Math.random
- recovery atomic-write suffix keeps its .tmp.<nonce> shape (pinned by
  test/recovery-storage.test.ts) but gains the crypto nonce
- timing-jitter Math.random uses are intentionally left untouched

https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

pr adds lib/temp-path.ts with tempFileNonce() and tempPathFor() and replaces inline temp/nonce construction across many atomic-write paths. recovery id generation now uses node:crypto.randomBytes. tests for the utilities are added in test/temp-path.test.ts:1.

Changes

atomic temp-path consolidation

Layer / File(s) Summary
core temp-path utility
lib/temp-path.ts, test/temp-path.test.ts, lib/index.ts
adds tempFileNonce() producing <pid>.<epochMs>.<hex8> and tempPathFor(targetPath) which returns <targetPath>.<nonce>.tmp. tests validate format, pid prefix, windows-path handling, and uniqueness (test/temp-path.test.ts:1).
atomic write temp-path adoption
lib/account-policy.ts, lib/budget-guard.ts, lib/codex-cli/writer.ts, lib/codex-manager/commands/report.ts, lib/codex-manager/commands/usage.ts, lib/config.ts, lib/local-client-tokens.ts, lib/oc-chatgpt-orchestrator.ts, lib/quota-cache.ts, lib/routing-profiles.ts, lib/storage/flagged-storage-io.ts, lib/storage/import-export.ts, lib/unified-settings.ts, lib/update-notice.ts, lib/storage.ts
these files replace inline ${path}.${pid}.${Date.now()}... patterns with tempPathFor(path) in their atomic write flows (see diffs such as lib/budget-guard.ts:145, lib/storage.ts:1888). retry/rename/cleanup logic is unchanged.
cache nonce consolidation
lib/prompts/codex.ts, lib/prompts/host-codex-prompt.ts
both modules now call tempFileNonce() for cache temp-file sibling naming instead of handcrafted timestamp/pid/random strings (lib/prompts/codex.ts:41, lib/prompts/host-codex-prompt.ts:154).
storage rotation & recovery hardening
lib/storage.ts, lib/recovery/storage.ts
lib/storage.ts adopts tempFileNonce()/tempPathFor() for rotating backups and saves (lib/storage.ts:279, lib/storage.ts:1888). lib/recovery/storage.ts switches generatePartId and generateThinkingPartId to randomBytes(...).toString('hex') and uses tempFileNonce() for atomic temp suffixes (lib/recovery/storage.ts:219,267,294).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

bug

notes for reviewer:

  • missing regression tests: unit tests cover lib/temp-path.ts (test/temp-path.test.ts:1) but there are no integration tests exercising atomic writes that now use tempPathFor (for example lib/budget-guard.ts:145 or lib/storage.ts:1888). add at least one end-to-end test that writes a staging file and verifies rename and cleanup.
  • windows edge cases: verify tempPathFor behavior with windows paths and deep paths (lib/temp-path.ts:22) and callers such as lib/unified-settings.ts:392 for path length and separator handling.
  • concurrency risks: tempFileNonce() uses epoch ms plus 4 random bytes (~32 bits). cross-process collisions are still possible under high throughput. review high-frequency callers such as lib/storage.ts:1888 and lib/recovery/storage.ts:219 and consider more entropy if needed.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning Title exceeds 72-char limit at 81 chars and uses conventional commits format correctly with fix/security scope. Shorten title to ≤72 chars, e.g., 'fix(security): use crypto for temp-file staging and recovery ids' (65 chars).
Docstring Coverage ⚠️ Warning Docstring coverage is 36.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description is comprehensive: includes security rationale (Math.random predictability + OAuth data risk), specific scope (18 files), testing evidence, and implementation details (nonce format, .tmp suffix preservation, recovery module changes).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/audit-01-temp-path-entropy
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/audit-01-temp-path-entropy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread lib/temp-path.ts
Comment thread test/temp-path.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/temp-path.test.ts`:
- Around line 23-29: Add a parallel test case in test/temp-path.test.ts that
calls tempPathFor with a Windows-style input (e.g. "C:\\data\\accounts.json")
and asserts the same properties as the UNIX test: the returned path starts with
the original target plus a dot, ends with ".tmp", and matches a Windows-aware
regex (drive letter and backslashes escaped) to validate the staged filename
format; reference the tempPathFor function and mirror the existing assertions
(startsWith, endsWith, and toMatch) but adapted for Windows path syntax.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 35a533ab-bfe6-46b2-b143-727a426de617

📥 Commits

Reviewing files that changed from the base of the PR and between 98d9819 and 48593cd.

📒 Files selected for processing (20)
  • lib/account-policy.ts
  • lib/budget-guard.ts
  • lib/codex-cli/writer.ts
  • lib/codex-manager/commands/report.ts
  • lib/codex-manager/commands/usage.ts
  • lib/config.ts
  • lib/local-client-tokens.ts
  • lib/oc-chatgpt-orchestrator.ts
  • lib/prompts/codex.ts
  • lib/prompts/host-codex-prompt.ts
  • lib/quota-cache.ts
  • lib/recovery/storage.ts
  • lib/routing-profiles.ts
  • lib/storage.ts
  • lib/storage/flagged-storage-io.ts
  • lib/storage/import-export.ts
  • lib/temp-path.ts
  • lib/unified-settings.ts
  • lib/update-notice.ts
  • test/temp-path.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (10)
lib/**/*.ts

📄 CodeRabbit inference engine (lib/AGENTS.md)

lib/**/*.ts: All public exports should flow through lib/index.ts or documented package subpaths
Never import from dist/ in source tests or library code
Never suppress type errors

Files:

  • lib/codex-manager/commands/report.ts
  • lib/local-client-tokens.ts
  • lib/codex-manager/commands/usage.ts
  • lib/storage/import-export.ts
  • lib/config.ts
  • lib/storage/flagged-storage-io.ts
  • lib/update-notice.ts
  • lib/routing-profiles.ts
  • lib/prompts/host-codex-prompt.ts
  • lib/quota-cache.ts
  • lib/budget-guard.ts
  • lib/prompts/codex.ts
  • lib/unified-settings.ts
  • lib/codex-cli/writer.ts
  • lib/oc-chatgpt-orchestrator.ts
  • lib/account-policy.ts
  • lib/storage.ts
  • lib/recovery/storage.ts
  • lib/temp-path.ts
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not use as any, @ts-ignore, or @ts-expect-error type assertions

Files:

  • lib/codex-manager/commands/report.ts
  • test/temp-path.test.ts
  • lib/local-client-tokens.ts
  • lib/codex-manager/commands/usage.ts
  • lib/storage/import-export.ts
  • lib/config.ts
  • lib/storage/flagged-storage-io.ts
  • lib/update-notice.ts
  • lib/routing-profiles.ts
  • lib/prompts/host-codex-prompt.ts
  • lib/quota-cache.ts
  • lib/budget-guard.ts
  • lib/prompts/codex.ts
  • lib/unified-settings.ts
  • lib/codex-cli/writer.ts
  • lib/oc-chatgpt-orchestrator.ts
  • lib/account-policy.ts
  • lib/storage.ts
  • lib/recovery/storage.ts
  • lib/temp-path.ts
**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

Use ESM module syntax exclusively; the project is ESM-only with "type": "module"

Files:

  • lib/codex-manager/commands/report.ts
  • test/temp-path.test.ts
  • lib/local-client-tokens.ts
  • lib/codex-manager/commands/usage.ts
  • lib/storage/import-export.ts
  • lib/config.ts
  • lib/storage/flagged-storage-io.ts
  • lib/update-notice.ts
  • lib/routing-profiles.ts
  • lib/prompts/host-codex-prompt.ts
  • lib/quota-cache.ts
  • lib/budget-guard.ts
  • lib/prompts/codex.ts
  • lib/unified-settings.ts
  • lib/codex-cli/writer.ts
  • lib/oc-chatgpt-orchestrator.ts
  • lib/account-policy.ts
  • lib/storage.ts
  • lib/recovery/storage.ts
  • lib/temp-path.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/codex-manager/commands/report.ts
  • lib/local-client-tokens.ts
  • lib/codex-manager/commands/usage.ts
  • lib/storage/import-export.ts
  • lib/config.ts
  • lib/storage/flagged-storage-io.ts
  • lib/update-notice.ts
  • lib/routing-profiles.ts
  • lib/prompts/host-codex-prompt.ts
  • lib/quota-cache.ts
  • lib/budget-guard.ts
  • lib/prompts/codex.ts
  • lib/unified-settings.ts
  • lib/codex-cli/writer.ts
  • lib/oc-chatgpt-orchestrator.ts
  • lib/account-policy.ts
  • lib/storage.ts
  • lib/recovery/storage.ts
  • lib/temp-path.ts
**

⚙️ CodeRabbit configuration file

**: # PROJECT KNOWLEDGE BASE

Generated: 2026-04-25
Commit: a87e005
Branch: main
Package version: 2.2.0

OVERVIEW

codex-multi-auth is a Codex CLI-first OAuth account manager and optional forwarding wrapper for the official Codex CLI. The installed codex-multi-auth entrypoint handles account-management commands locally, codex-multi-auth-codex forwards official Codex commands through this package's wrapper when explicitly used, and runtime rotation can route live Responses traffic through a localhost account-rotation proxy by default. The plugin-host entrypoint remains exported for compatibility, but the primary product surface is the account manager, optional wrapper, storage, runtime proxy, and repair tooling.

STRUCTURE

./
├── scripts/
│   ├── codex.js              # codex-multi-auth-codex wrapper, official CLI forwarder, shadow CODEX_HOME/runtime proxy setup
│   ├── codex-multi-auth.js   # standalone package CLI entrypoint
│   ├── codex-routing.js      # auth command and compatibility alias routing
│   ├── codex-bin-resolver.js # official Codex binary discovery
│   ├── codex-app-router.js   # persistent localhost router for packaged Codex app bind
│   └── codex-app-launcher.js # reversible user-level app launcher routing helper
├── index.ts                  # optional plugin-host runtime entry
├── lib/                      # core runtime logic (see lib/AGENTS.md)
│   ├── auth/                 # OAuth flow, PKCE, callback server
│   ├── runtime/              # Codex CLI/app integration helpers, app bind, live sync, runtime observability
│   ├── request/              # request transform, SSE, failover, backoff
│   ├── storage/              # path resolution, migrations, backups, restore, import/export
│   ├── codex-cli/            # Codex CLI state sync and writer helpers
│   ├── codex-manager/        # command modules and settings panels
│   ├── prompts/              # model-family prompts, GitHub ETag cache
│   ├── recovery/             # conve...

Files:

  • lib/codex-manager/commands/report.ts
  • test/temp-path.test.ts
  • lib/local-client-tokens.ts
  • lib/codex-manager/commands/usage.ts
  • lib/storage/import-export.ts
  • lib/config.ts
  • lib/storage/flagged-storage-io.ts
  • lib/update-notice.ts
  • lib/routing-profiles.ts
  • lib/prompts/host-codex-prompt.ts
  • lib/quota-cache.ts
  • lib/budget-guard.ts
  • lib/prompts/codex.ts
  • lib/unified-settings.ts
  • lib/codex-cli/writer.ts
  • lib/oc-chatgpt-orchestrator.ts
  • lib/account-policy.ts
  • lib/storage.ts
  • lib/recovery/storage.ts
  • lib/temp-path.ts
test/**/*.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

test/**/*.test.ts: Vitest globals (describe, it, expect) are enabled and should be used without explicit imports
Maintain 80% coverage threshold across statements, branches, functions, and lines
Use removeWithRetry for Windows filesystem cleanup instead of bare fs.rm to handle EBUSY/EPERM/ENOTEMPTY backoff
Use source files in tests, not compiled dist/ files; test the source directly
Do not skip tests without justification; include rationale if a test must be skipped
Relax ESLint rules for test files as specified in eslint.config.js

Files:

  • test/temp-path.test.ts
test/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Windows filesystem operations must include retry handling for transient EBUSY, EPERM, and ENOTEMPTY errors where tests cover Windows locks

Files:

  • test/temp-path.test.ts
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/temp-path.test.ts
lib/storage/**/*.ts

📄 CodeRabbit inference engine (lib/AGENTS.md)

lib/storage/**/*.ts: Worktree storage uses resolveProjectStorageIdentityRoot; never derive project pools from raw worktree paths
Never use bare recursive cleanup in Windows-sensitive paths without retry handling

Files:

  • lib/storage/import-export.ts
  • lib/storage/flagged-storage-io.ts
lib/storage.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use case-insensitive email dedup via normalizeEmailKey() with trim and lowercase normalization

Files:

  • lib/storage.ts
🧠 Learnings (2)
📚 Learning: 2026-06-04T06:14:18.093Z
Learnt from: ndycode
Repo: ndycode/codex-multi-auth PR: 510
File: test/scheduling-strategy-config.test.ts:1-1
Timestamp: 2026-06-04T06:14:18.093Z
Learning: In ndycode/codex-multi-auth, do not flag explicit imports from "vitest" (e.g., describe, it, expect, beforeEach/afterEach, etc.) in test files as issues—even if the Vitest config sets `globals: true`. The repo’s established convention is to keep these imports for consistency with neighboring tests; removing them would make files outliers.

Applied to files:

  • test/temp-path.test.ts
📚 Learning: 2026-06-04T06:14:24.975Z
Learnt from: ndycode
Repo: ndycode/codex-multi-auth PR: 510
File: test/runtime-rotation-proxy.test.ts:2478-2491
Timestamp: 2026-06-04T06:14:24.975Z
Learning: In ndycode/codex-multi-auth test files (e.g. `test/*.test.ts`), when creating V3 storage fixtures for accounts, it’s an intentional convention to use `as never` for deliberately minimal stored-account objects that only include `refreshToken`, `addedAt`, and `lastUsed`. Do not treat `as never` here as a type-safety problem: optional/other fields are expected to be populated by the runtime during execution, and the cast is used solely to keep the fixture minimal and consistent across existing tests.

Applied to files:

  • test/temp-path.test.ts
🔇 Additional comments (28)
lib/storage.ts (3)

72-72: LGTM!


321-321: LGTM!


1930-1930: LGTM!

lib/recovery/storage.ts (3)

7-7: LGTM!

Also applies to: 26-26


235-235: LGTM!


288-288: crypto randomness upgrade preserves format contracts.

both generatePartId() and generateThinkingPartId() now use randomBytes(...).toString("hex") instead of Math.random()-based tokens. randomBytes(4) produces 8 hex chars matching the existing test regex /[a-z0-9]{8}/ at test/recovery-storage.test.ts:52 (hex is a subset). thinking id prefix prt_0000000000_thinking_ is preserved so orphan detection sorting still works per test/recovery-storage.test.ts:931.

the change improves collision resistance without breaking id format contracts. existing tests in test/recovery-storage.test.ts:50-63 and :922-933 cover the shape and sort behavior.

Also applies to: 315-315

lib/account-policy.ts (1)

8-8: LGTM!

Also applies to: 142-142

lib/budget-guard.ts (1)

7-7: LGTM!

Also applies to: 145-145

lib/codex-cli/writer.ts (1)

15-15: LGTM!

Also applies to: 213-213

lib/codex-manager/commands/report.ts (1)

42-42: LGTM!

Also applies to: 269-269

lib/codex-manager/commands/usage.ts (1)

14-14: LGTM!

Also applies to: 293-293

lib/local-client-tokens.ts (1)

7-7: LGTM!

Also applies to: 181-181

lib/prompts/codex.ts (1)

11-11: LGTM!

Also applies to: 41-43

lib/prompts/host-codex-prompt.ts (1)

14-14: LGTM!

Also applies to: 154-156

lib/config.ts (1)

18-18: LGTM!

Also applies to: 493-493

lib/oc-chatgpt-orchestrator.ts (1)

21-21: LGTM!

Also applies to: 205-205

lib/quota-cache.ts (1)

6-6: LGTM!

Also applies to: 254-254

lib/routing-profiles.ts (1)

11-11: LGTM!

Also applies to: 163-163

lib/storage/flagged-storage-io.ts (1)

7-7: LGTM!

Also applies to: 248-248

lib/storage/import-export.ts (1)

6-6: LGTM!

Also applies to: 90-90

lib/unified-settings.ts (2)

14-14: LGTM!

Also applies to: 392-392


336-336: Review: unify & test atomic temp-file writes in unified settings (Windows/429 concurrency)

  • Reconcile sync vs async temp-file handling in lib/unified-settings.ts so they share the same tempPathFor behavior and cleanup semantics (avoid race conditions).
  • Ensure vitest covers concurrent atomic writes + temp-file cleanup, including Windows transient cleanup failures (e.g., EBUSY) and any related retry paths involving rate limiting (429).
  • Check failure/retry logging doesn’t leak account emails or OAuth/tokens.
lib/update-notice.ts (1)

13-13: LGTM!

Also applies to: 76-76

lib/temp-path.ts (2)

1-27: LGTM!


13-14: Fix temp-path public export wiring (and add tests).

  • lib/index.ts doesn’t re-export tempFileNonce / tempPathFor; ensure downstream consumers can access them via lib/index.ts or a documented temp-path subpath in package.json.
  • Add vitest coverage for concurrent nonce generation uniqueness and for Windows filesystem/path edge cases covered by tempPathFor.
test/temp-path.test.ts (3)

1-2: LGTM!


4-21: LGTM!


31-38: LGTM!

Comment thread test/temp-path.test.ts
…G note

- re-export lib/temp-path.ts from lib/index.ts (lib/AGENTS.md: public
  exports flow through the barrel)
- add a Windows drive-letter/backslash test case for tempPathFor
- document the CSPRNG-availability assumption on tempFileNonce: randomBytes
  throwing on a broken FIPS build is the intended outcome, not a path to
  fall back to weaker randomness

https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
Review follow-up: assert that a randomBytes failure (FIPS-restricted or
entropy-starved builds) propagates out of tempFileNonce/tempPathFor
instead of degrading to weaker randomness. Same-millisecond uniqueness
is already covered by the rapid-call tests, which rely on the crypto
suffix precisely because pid+timestamp collide within one tick.

https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/temp-path.test.ts (1)

27-35: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

make uniqueness checks deterministic to avoid rare ci flakes.

the assertions in test/temp-path.test.ts:27-35 and test/temp-path.test.ts:67-73 depend on random non-collision across 200 draws. that is probabilistic, so it can still fail rarely and violate deterministic test expectations.

suggested deterministic pattern
-const cryptoControl = vi.hoisted(() => ({ failure: null as Error | null }));
+const cryptoControl = vi.hoisted(() => ({
+	failure: null as Error | null,
+	forcedHex: null as string[] | null,
+}));

 vi.mock("node:crypto", async (importOriginal) => {
 	const actual = await importOriginal<typeof import("node:crypto")>();
 	return {
 		...actual,
 		randomBytes: (size: number) => {
 			if (cryptoControl.failure) {
 				throw cryptoControl.failure;
 			}
+			if (cryptoControl.forcedHex?.length) {
+				const next = cryptoControl.forcedHex.shift()!;
+				return Buffer.from(next, "hex");
+			}
 			return actual.randomBytes(size);
 		},
 	};
 });

@@
 		it("does not repeat across rapid successive calls", () => {
+			cryptoControl.forcedHex = Array.from({ length: 200 }, (_, i) =>
+				i.toString(16).padStart(8, "0"),
+			);
 			const seen = new Set<string>();
 			for (let i = 0; i < 200; i += 1) {
 				seen.add(tempFileNonce());
 			}
-			// pid + timestamp collide within the same millisecond, so uniqueness
-			// rests on the crypto suffix; 200 draws must never collide.
 			expect(seen.size).toBe(200);
+			cryptoControl.forcedHex = null;
 		});
@@
 		it("never collides for the same target across rapid calls", () => {
+			cryptoControl.forcedHex = Array.from({ length: 200 }, (_, i) =>
+				(i + 200).toString(16).padStart(8, "0"),
+			);
 			const seen = new Set<string>();
 			for (let i = 0; i < 200; i += 1) {
 				seen.add(tempPathFor("/data/accounts.json"));
 			}
 			expect(seen.size).toBe(200);
+			cryptoControl.forcedHex = null;
 		});

as per coding guidelines, test/**: "tests must stay deterministic and use vitest."

Also applies to: 67-73

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/temp-path.test.ts` around lines 27 - 35, The test relies on
probabilistic uniqueness of tempFileNonce which can flake; make it deterministic
by stubbing/mocking the randomness used by tempFileNonce (e.g., mock
crypto.randomBytes or the internal suffix generator) to return a predictable,
non-colliding sequence before the loop, then restore the mock after the test;
update both occurrences in test/temp-path.test.ts (the blocks referencing
tempFileNonce at lines near the shown 27-35 and 67-73) so the for-loop asserts
on 200 uniquely generated, deterministic values instead of relying on chance.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@test/temp-path.test.ts`:
- Around line 27-35: The test relies on probabilistic uniqueness of
tempFileNonce which can flake; make it deterministic by stubbing/mocking the
randomness used by tempFileNonce (e.g., mock crypto.randomBytes or the internal
suffix generator) to return a predictable, non-colliding sequence before the
loop, then restore the mock after the test; update both occurrences in
test/temp-path.test.ts (the blocks referencing tempFileNonce at lines near the
shown 27-35 and 67-73) so the for-loop asserts on 200 uniquely generated,
deterministic values instead of relying on chance.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 95b8ca9a-1560-4565-9482-fb6a8311f198

📥 Commits

Reviewing files that changed from the base of the PR and between 363bd80 and 0e1353a.

📒 Files selected for processing (1)
  • test/temp-path.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (6)
test/**/*.test.ts

📄 CodeRabbit inference engine (test/AGENTS.md)

test/**/*.test.ts: Vitest globals (describe, it, expect) are enabled and should be used without explicit imports
Maintain 80% coverage threshold across statements, branches, functions, and lines
Use removeWithRetry for Windows filesystem cleanup instead of bare fs.rm to handle EBUSY/EPERM/ENOTEMPTY backoff
Use source files in tests, not compiled dist/ files; test the source directly
Do not skip tests without justification; include rationale if a test must be skipped
Relax ESLint rules for test files as specified in eslint.config.js

Files:

  • test/temp-path.test.ts
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not use as any, @ts-ignore, or @ts-expect-error type assertions

Files:

  • test/temp-path.test.ts
**/*.{ts,js}

📄 CodeRabbit inference engine (AGENTS.md)

Use ESM module syntax exclusively; the project is ESM-only with "type": "module"

Files:

  • test/temp-path.test.ts
test/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Windows filesystem operations must include retry handling for transient EBUSY, EPERM, and ENOTEMPTY errors where tests cover Windows locks

Files:

  • test/temp-path.test.ts
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/temp-path.test.ts
**

⚙️ CodeRabbit configuration file

**: # PROJECT KNOWLEDGE BASE

Generated: 2026-04-25
Commit: a87e005
Branch: main
Package version: 2.2.0

OVERVIEW

codex-multi-auth is a Codex CLI-first OAuth account manager and optional forwarding wrapper for the official Codex CLI. The installed codex-multi-auth entrypoint handles account-management commands locally, codex-multi-auth-codex forwards official Codex commands through this package's wrapper when explicitly used, and runtime rotation can route live Responses traffic through a localhost account-rotation proxy by default. The plugin-host entrypoint remains exported for compatibility, but the primary product surface is the account manager, optional wrapper, storage, runtime proxy, and repair tooling.

STRUCTURE

./
├── scripts/
│   ├── codex.js              # codex-multi-auth-codex wrapper, official CLI forwarder, shadow CODEX_HOME/runtime proxy setup
│   ├── codex-multi-auth.js   # standalone package CLI entrypoint
│   ├── codex-routing.js      # auth command and compatibility alias routing
│   ├── codex-bin-resolver.js # official Codex binary discovery
│   ├── codex-app-router.js   # persistent localhost router for packaged Codex app bind
│   └── codex-app-launcher.js # reversible user-level app launcher routing helper
├── index.ts                  # optional plugin-host runtime entry
├── lib/                      # core runtime logic (see lib/AGENTS.md)
│   ├── auth/                 # OAuth flow, PKCE, callback server
│   ├── runtime/              # Codex CLI/app integration helpers, app bind, live sync, runtime observability
│   ├── request/              # request transform, SSE, failover, backoff
│   ├── storage/              # path resolution, migrations, backups, restore, import/export
│   ├── codex-cli/            # Codex CLI state sync and writer helpers
│   ├── codex-manager/        # command modules and settings panels
│   ├── prompts/              # model-family prompts, GitHub ETag cache
│   ├── recovery/             # conve...

Files:

  • test/temp-path.test.ts
🧠 Learnings (2)
📚 Learning: 2026-06-04T06:14:18.093Z
Learnt from: ndycode
Repo: ndycode/codex-multi-auth PR: 510
File: test/scheduling-strategy-config.test.ts:1-1
Timestamp: 2026-06-04T06:14:18.093Z
Learning: In ndycode/codex-multi-auth, do not flag explicit imports from "vitest" (e.g., describe, it, expect, beforeEach/afterEach, etc.) in test files as issues—even if the Vitest config sets `globals: true`. The repo’s established convention is to keep these imports for consistency with neighboring tests; removing them would make files outliers.

Applied to files:

  • test/temp-path.test.ts
📚 Learning: 2026-06-04T06:14:24.975Z
Learnt from: ndycode
Repo: ndycode/codex-multi-auth PR: 510
File: test/runtime-rotation-proxy.test.ts:2478-2491
Timestamp: 2026-06-04T06:14:24.975Z
Learning: In ndycode/codex-multi-auth test files (e.g. `test/*.test.ts`), when creating V3 storage fixtures for accounts, it’s an intentional convention to use `as never` for deliberately minimal stored-account objects that only include `refreshToken`, `addedAt`, and `lastUsed`. Do not treat `as never` here as a type-safety problem: optional/other fields are expected to be populated by the runtime during execution, and the cast is used solely to keep the fixture minimal and consistent across existing tests.

Applied to files:

  • test/temp-path.test.ts

ndycode commented Jun 10, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Declining the "make uniqueness checks deterministic" suggestion for test/temp-path.test.ts. Forcing a pre-made sequence of 200 unique hex values through the mock and then asserting 200 unique outputs is a tautology — it verifies the mock, not the implementation, and would no longer catch a regression where the nonce stops using the CSPRNG (e.g. someone swaps randomBytes for a constant or truncates the suffix). The point of these two tests is precisely to exercise the real entropy source.

On flake risk: collision probability across 200 draws from a 2³² space is ~4.7×10⁻⁶ per run — about one expected failure per 200,000 CI runs, below the noise floor of any real suite. The deterministic-tests guideline is about clocks, ordering, network and filesystem races, not about astronomically-unlikely CSPRNG collisions; the error-path and format assertions in the same file are fully deterministic.

Please record this as a learning: in this repo, uniqueness tests for crypto-backed nonce/ID generators intentionally draw from the real CSPRNG rather than a forced unique sequence, and should not be flagged as nondeterministic.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@ndycode: understood — the argument is solid. a mock that feeds a pre-made unique sequence and then asserts unique outputs is a closed loop that can't catch a regression like a constant stub or a truncated suffix. the real CSPRNG draw is the only way to exercise the actual code path, and 4.7×10⁻⁶ per run is well below actionable flake thresholds. noted and recorded.


✏️ Learnings added
Learnt from: ndycode
URL: https://github.com/ndycode/codex-multi-auth/pull/517

Timestamp: 2026-06-10T01:38:55.052Z
Learning: In the ndycode/codex-multi-auth repo, uniqueness tests for crypto-backed nonce/ID generators (e.g., `tempFileNonce()` and `tempPathFor()` in `test/temp-path.test.ts`) intentionally draw from the real CSPRNG (`node:crypto.randomBytes`) rather than a forced unique sequence through a mock. Flagging these as nondeterministic is incorrect: collision probability across 200 draws from a 2^32 space is ~4.7×10^-6 per run (~1 in 200,000 CI runs), which is below any actionable flake threshold. The deterministic-tests guideline applies to clocks, ordering, network and filesystem races — not to astronomically-unlikely CSPRNG collisions. Mocking `randomBytes` to a pre-made unique sequence would verify only the mock, not the implementation, and would not catch regressions where the nonce stops using the CSPRNG (e.g., constant stub or truncated suffix).

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

claude added 2 commits June 10, 2026 16:32
…path-entropy

# Conflicts:
#	lib/quota-cache.ts
#	lib/storage/import-export.ts
Review note on this PR: the unit tests covered lib/temp-path.ts in
isolation but no test drove an actual atomic-write caller through the
new staging helper. saveQuotaCache now gets an end-to-end check: the
rename source must be a tempPathFor product (<target>.<pid>.<ms>.<hex8>
.tmp sibling), the rename lands on the cache path, the directory holds
no .tmp leftovers afterwards, and the content survives. The rename spy
is scoped with try/finally so it cannot leak into the EBUSY-retry test
that monkeypatches fs.rename directly.

https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
@ndycode
ndycode merged commit ce09dad into main Jun 11, 2026
2 checks passed
luo178 pushed a commit to luo178/codex-multi-auth that referenced this pull request Jun 23, 2026
…audit

Snapshot audit against v2.3.0-beta.1 (HEAD 98d9819) covering architecture,
security, correctness/concurrency, testing/CI, packaging, and docs/DX:

- verified findings table (4 HIGH, 13 MEDIUM, 5 LOW) with file:line evidence
- index of the five companion fix PRs (ndycode#517-ndycode#521)
- prioritized refactor roadmap with concrete seams for codex-manager.ts,
  fetch-helpers.ts, runtime-rotation-proxy.ts, retry consolidation,
  error-contract adoption, CI consolidation, and packaging trims
- rejected-findings section recording disproven automated claims so future
  audits do not re-litigate them

https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
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.

2 participants