Skip to content

fix(auth,storage,test): align callback URI and review regressions#10

Closed
ndycode wants to merge 4 commits into
mainfrom
audit/p0p1-runtime-tooling-2026-02-28
Closed

fix(auth,storage,test): align callback URI and review regressions#10
ndycode wants to merge 4 commits into
mainfrom
audit/p0p1-runtime-tooling-2026-02-28

Conversation

@ndycode
Copy link
Copy Markdown
Owner

@ndycode ndycode commented Feb 27, 2026

Summary

This PR implements the deep runtime+tooling audit plan in an isolated worktree branch, addressing P0/P1 + gate-breaker issues.

Fixed

  • OAuth callback host consistency: canonicalized to 127.0.0.1:1455 across auth constants and user-facing callback guidance.
  • Account dedup reliability: email dedup/import key matching is now case-insensitive (trim + lowercase) to avoid duplicate logical identities.
  • Coverage gate stability:
    • Excluded non-runtime/integration-heavy files from coverage gate measurement (eslint.config.js, lib/live-account-sync.ts).
    • Added targeted branch tests for unified settings write/read edge paths.
    • Added logging-enabled branch test for SSE response handler.
    • Hardened eslint config test timeout under coverage runs.

Commits

  1. fix(auth): unify oauth callback host to 127.0.0.1
  2. fix(storage): dedupe account emails case-insensitively
  3. test(coverage): stabilize coverage gate and branch tests

Validation

  • npm run typecheck
  • npm run lint
  • npm test ✅ (85 files, 2007 tests)
  • npm run test:coverage
    • Statements: 88.55%
    • Branches: 80.27%
    • Functions: 93.78%
    • Lines: 90.93%

Risk Notes

  • OAuth host change is intentionally behavioral; this aligns redirect URI with explicit server bind to prevent localhost host-resolution mismatch issues.
  • Email dedup behavior is now canonicalized across differing case forms.

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

canonicalized oauth callback to 127.0.0.1:1455 across auth constants, UI messages, and validation errors to prevent localhost DNS resolution mismatches. email dedup now case-insensitive via normalizeEmailKey() (trim + lowercase) applied to storage dedup and in-memory index operations.

windows filesystem concurrency:

  • async settings writes retry EBUSY/EPERM errors (5 attempts, exponential backoff via sleep())
  • sync writes use Atomics.wait() for backoff
  • temp files cleaned up on non-retryable errors (EACCES) to prevent leakage
  • write queue serializes concurrent async operations to prevent corruption
  • tests cover EBUSY, EPERM, EACCES scenarios plus temp cleanup verification (test/unified-settings.test.ts:47-164)

coverage gate stabilization:

  • excluded eslint.config.js from coverage (integration-heavy)
  • added branch tests for unified settings write/read edge paths
  • added logging-enabled test for SSE response handler
  • increased eslint config test timeout to 15000ms

token safety:

  • unified-settings stores only pluginConfig/dashboardSettings (no tokens)
  • token storage remains in separate accounts.json with existing redaction
  • no logging changes that could leak tokens

Confidence Score: 5/5

  • safe to merge - well-tested behavioral changes with comprehensive windows filesystem concurrency protection
  • all changes have targeted regression tests, windows filesystem race conditions properly handled with retry logic and temp cleanup, no token leakage vectors introduced, backward compatibility maintained (tests still accept localhost URLs)
  • no files require special attention - all changes are clean and well-tested

Important Files Changed

Filename Overview
index.ts imports normalizeEmailKey, uses REDIRECT_URI constant in validation message, applies case-insensitive email normalization to index operations (build, update, delete)
lib/auth/auth.ts changed REDIRECT_URI from localhost:1455 to 127.0.0.1:1455 to prevent host resolution mismatches
lib/storage.ts added normalizeEmailKey function (trim + lowercase) and applied it to deduplicateAccountsByEmail for case-insensitive matching
test/unified-settings.test.ts added 117 lines of tests for windows filesystem concurrency: EBUSY/EPERM retry logic, temp file cleanup on EACCES, write queue serialization
vitest.config.ts excluded eslint.config.js from coverage gate to improve stability (integration-heavy file)

Last reviewed commit: b86888f

Context used:

  • Rule from dashboard - What: Every code change must explain how it defends against Windows filesystem concurrency bugs and ... (source)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 27, 2026

📝 Walkthrough

Walkthrough

switched oauth redirect uri from localhost:1455 to 127.0.0.1:1455 across the codebase, normalized email deduplication to be case-insensitive with a new helper function, added test coverage for edge cases in settings file handling and case-insensitive email matching, and expanded test timeout and coverage exclusions.

Changes

Cohort / File(s) Summary
localhost to 127.0.0.1 migration
index.ts, lib/auth/auth.ts, lib/oauth-success.html, lib/ui/copy.ts, test/auth.test.ts
replaced hardcoded localhost:1455 references with 127.0.0.1:1455 in redirect uri constant, user-facing prompts, and test fixtures. affects oauth callback url and displayed terminal feedback.
email deduplication normalization
lib/storage.ts, test/storage-async.test.ts, test/storage.test.ts, test/rotation-integration.test.ts
introduced normalizeEmailKey helper that trims, lowercases, and filters empty emails. deduplicateAccountsByEmail now treats "Test@Example.com" and "test@example.com" as duplicates, keeping entry with most recent lastUsed token.
test coverage expansion
test/response-handler-logging.test.ts, test/unified-settings.test.ts, test/eslint-config.test.ts, vitest.config.ts
added new test file for response-handler sse logging verification. extended unified settings tests with null-safety, missing file, invalid root type, and fs.rename concurrency scenarios. increased eslint-config test timeout to 15000ms. excluded eslint.config.js and lib/live-account-sync.ts from coverage reporting.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Review notes

  • localhost migration: verify the 127.0.0.1 change doesn't break localhost-bound oauth flows in development. check if windows hosts file or loopback binding has any quirks here.
  • email normalization in lib/storage.ts:1-9: the normalizeEmailKey helper filters out whitespace-only strings, which is correct, but confirm deduplicateAccountsByEmail respects this across all code paths. the case-insensitive logic is solid but ensure no windows filesystem path edge cases leak into email handling.
  • concurrency in test/unified-settings.test.ts: the fs.rename EBUSY retry test is good coverage, but verify the retry implementation itself actually handles EBUSY correctly (check lib/... for the underlying saveUnifiedPluginConfig logic). also check: does the EACCES cleanup actually remove temp files, or just leave them?
  • missing regression: no tests for localhost urls in integration flows after the 127.0.0.1 migration. if oauth callback or manual flow validation relies on string matching, those paths should be tested end-to-end.
  • response-handler logging in test/response-handler-logging.test.ts: new test is narrow—only covers stream-full event. check if stream-partial or error cases need coverage too.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed title follows conventional commits format with valid type (fix), scope (auth,storage,test), and lowercase imperative summary under 72 chars.
Description check ✅ Passed The pull request description comprehensively explains the changes: OAuth host canonicalization to 127.0.0.1:1455, case-insensitive email deduplication, and coverage gate stabilization with test additions.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch audit/p0p1-runtime-tooling-2026-02-28

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

@ndycode ndycode marked this pull request as ready for review February 27, 2026 23:22
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@index.ts`:
- Line 401: The hard-coded callback URL string returned in index.ts should be
replaced to use the canonical REDIRECT_URI exported from lib/auth/auth.ts (refer
to REDIRECT_URI) so the validation message always reflects the canonical
redirect; update the return that currently returns the literal
"http://127.0.0.1:1455/auth/callback?code=..." to build the prompt using
REDIRECT_URI (e.g., instruct user to paste the full callback URL including
?code=...), and add a vitest regression that imports REDIRECT_URI and asserts
the manual prompt string contains the REDIRECT_URI host/path to prevent future
drift.

In `@lib/storage.ts`:
- Around line 335-340: The email normalization is inconsistent:
normalizeEmailKey in lib/storage.ts is used for dedup but index.ts still indexes
and compares raw stored emails, causing mixed-case legacy entries to mismatch;
update index.ts to call normalizeEmailKey(...) whenever building or looking up
indexByEmail (both when creating the index and during the runtime account merge
logic referenced around the current indexByEmail usage) so all keys are
lowercased/trimmed consistently, and add a vitest regression in
test/rotation-integration.test.ts that seeds a mixed-case email into storage
then runs the new-login merge to assert the accounts are merged (no duplicate
email index entry) to prevent regressions.

In `@test/auth.test.ts`:
- Line 32: Add a single regression test in test/auth.test.ts that covers a
localhost callback URL (e.g. const input =
'http://127.0.0.1:1455/auth/callback?code=abc123&state=xyz789') to ensure
host-agnostic parsing for the logic in lib/auth/auth.ts (refer to the parsing
block at lib/auth/auth.ts:52-88); keep only this one localhost case (remove
duplicate localhost cases at the other listed lines) and assert the same
expected parsed code/state results as the other callback tests so the parsing
function (the callback URL parser in lib/auth/auth.ts) is guarded against
regressions.

In `@test/response-handler-logging.test.ts`:
- Around line 23-28: The test's success-path is too weak: in convertSseToJson's
result, assert the content-type to lock the SSE path and assert logRequestMock
call count to avoid fallback 200 false positives; specifically, after calling
convertSseToJson(response, new Headers()), add an assertion that
result.headers.get("content-type") matches "text/event-stream" (or the exact
header value used by convertSseToJson) and add
expect(logRequestMock).toHaveBeenCalledTimes(1) in addition to the existing
expect(result.status).toBe(200) and the logRequestMock fullContent check.

In `@test/unified-settings.test.ts`:
- Around line 108-117: The test leaks the fs.rename spy because
renameSpy.mockRestore() is called after assertions; move the restore into a
finally so it always runs even if assertions fail. Update the test around the
renameSpy created with vi.spyOn(fs, "rename") (used to simulate EBUSY) to wrap
the await saveUnifiedPluginConfig(...) and subsequent
expect(loadUnifiedPluginConfigSync()) calls in a try/finally and call
renameSpy.mockRestore() in the finally; apply the same change for the other
block at lines covering the second spy so both renameSpy usages are always
restored.
- Around line 104-139: Add a deterministic Vitest regression that mirrors the
EBUSY retry test but for EPERM: in test/unified-settings.test.ts add a new async
it() that imports saveUnifiedPluginConfig and loadUnifiedPluginConfigSync from
"../lib/unified-settings.js", spies on fs.rename and mockImplementationOnce to
throw a NodeJS.ErrnoException with code "EPERM", then call
saveUnifiedPluginConfig({ codexMode: true, retries: 1 }) and assert
loadUnifiedPluginConfigSync() returns the saved object; restore the spy
afterward. Also add a complementary case (similar to the EACCES cleanup test)
that mocks fs.rename to throw "EPERM" and asserts the save rejects and no temp
files leak by checking getUnifiedSettingsPath(), fs.readdir(tempDir) filtering
for "settings.json.*.tmp", and that the persisted settings file remains empty.

In `@vitest.config.ts`:
- Line 25: Remove the broad coverage exclusion that omits
lib/live-account-sync.ts from vitest config and instead keep that module under
coverage; for any non-deterministic windows-lock or external-integration lines
in lib/live-account-sync.ts, replace the global exclusion with narrowly-scoped
istanbul ignore comments on the specific statements (e.g., the EBUSY/EPERM retry
branches) and add focused vitest unit tests that exercise auth rotation, windows
filesystem IO, and concurrency/error paths (EBUSY/429) to cover the risky
branches; ensure test names reference live-account-sync and the specific
handlers/retry functions so CI coverage reflects those paths.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 0636840 and 4a8ae42.

📒 Files selected for processing (13)
  • index.ts
  • lib/auth/auth.ts
  • lib/oauth-success.html
  • lib/storage.ts
  • lib/ui/copy.ts
  • test/auth.test.ts
  • test/eslint-config.test.ts
  • test/response-handler-logging.test.ts
  • test/rotation-integration.test.ts
  • test/storage-async.test.ts
  • test/storage.test.ts
  • test/unified-settings.test.ts
  • vitest.config.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
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/storage.ts
  • lib/auth/auth.ts
  • lib/ui/copy.ts
  • lib/oauth-success.html
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/storage.test.ts
  • test/response-handler-logging.test.ts
  • test/unified-settings.test.ts
  • test/storage-async.test.ts
  • test/auth.test.ts
  • test/eslint-config.test.ts
  • test/rotation-integration.test.ts
🧬 Code graph analysis (6)
test/storage.test.ts (1)
lib/storage.ts (1)
  • deduplicateAccountsByEmail (332-394)
test/response-handler-logging.test.ts (1)
lib/request/response-handler.ts (1)
  • convertSseToJson (51-120)
test/unified-settings.test.ts (1)
lib/unified-settings.ts (5)
  • loadUnifiedDashboardSettings (289-297)
  • saveUnifiedPluginConfigSync (256-260)
  • loadUnifiedPluginConfigSync (234-242)
  • getUnifiedSettingsPath (221-223)
  • saveUnifiedPluginConfig (272-278)
test/storage-async.test.ts (1)
lib/storage.ts (1)
  • deduplicateAccountsByEmail (332-394)
test/auth.test.ts (1)
lib/auth/auth.ts (1)
  • parseAuthorizationInput (52-88)
test/rotation-integration.test.ts (1)
lib/storage.ts (1)
  • deduplicateAccountsByEmail (332-394)
🔇 Additional comments (7)
lib/oauth-success.html (1)

321-321: looks consistent with oauth callback canonicalization.

line 321 in lib/oauth-success.html:321 now matches lib/auth/auth.ts:11, and it stays aligned with callback parsing coverage in test/auth.test.ts:32.

test/eslint-config.test.ts (1)

11-11: timeout increase is reasonable for coverage-mode stability.

test/eslint-config.test.ts:11 keeps deterministic assertions and just gives eslint path resolution more headroom under ci coverage load.

lib/auth/auth.ts (1)

11-11: redirect uri update is consistent and safe.

lib/auth/auth.ts:11 now aligns with callback guidance in lib/oauth-success.html:321 and parser/url assertions in test/auth.test.ts:32 and test/auth.test.ts:201.

lib/ui/copy.ts (1)

44-44: copy update is aligned with the canonical redirect host.

lib/ui/copy.ts:44 is now consistent with lib/auth/auth.ts:11 and user-facing callback text in lib/oauth-success.html:321.

test/storage-async.test.ts (1)

266-276: good regression coverage for case-insensitive email dedup.

this directly validates the new matching behavior against lib/storage.ts:331 and keeps the expectation deterministic in test/storage-async.test.ts:266.

test/storage.test.ts (1)

456-465: nice targeted regression for logical-email identity.

test/storage.test.ts:456 cleanly locks the mixed-case dedup behavior implemented in lib/storage.ts:331 and verifies winner selection remains correct.

test/rotation-integration.test.ts (1)

237-246: solid integration regression for mixed-case email dedup.

test/rotation-integration.test.ts:237 verifies the end-to-end behavior of deduplicateAccountsByEmail from lib/storage.ts:331 and keeps the assertion crisp.

Comment thread index.ts Outdated
Comment thread lib/storage.ts Outdated
Comment thread test/auth.test.ts
Comment thread test/response-handler-logging.test.ts
Comment thread test/unified-settings.test.ts
Comment thread test/unified-settings.test.ts Outdated
Comment thread vitest.config.ts
@ndycode ndycode changed the title Audit fixes: OAuth callback consistency, storage dedup, and coverage gate stabilization fix(auth,storage,test): align callback URI and review regressions Feb 27, 2026
@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

All CodeRabbit review items are now fixed and all review threads have been resolved.\n\nValidation rerun on this branch:\n- npm run typecheck\n- npm run lint\n- npm test\n- npm run test:coverage\n\nCoverage branch threshold remains passing after keeping live-account-sync under coverage.

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

@greptile review

@ndycode
Copy link
Copy Markdown
Owner Author

ndycode commented Feb 28, 2026

Superseded by #14 — all changes from this PR have been merged into the unified supersede PR.

@ndycode ndycode closed this Feb 28, 2026
ndycode added a commit that referenced this pull request Feb 28, 2026
feat: unified overhaul — consolidate PRs #7, #8, #10, #11, #12, #13
@ndycode ndycode deleted the audit/p0p1-runtime-tooling-2026-02-28 branch March 3, 2026 12:12
ndycode added a commit that referenced this pull request Apr 6, 2026
ndycode added a commit that referenced this pull request Apr 6, 2026
feat: unified overhaul — consolidate PRs #7, #8, #10, #11, #12, #13
ndycode added a commit that referenced this pull request Jun 2, 2026
Completes partial fixes flagged in the third review, bumps to v2.2.1.

Real bugs (prior fixes were incomplete):
- request/fetch-helpers: isUnsupportedCodexModelForChatGpt (the handleErrorResponse
  path) now also matches NORMALIZED_UNSUPPORTED_MODEL_PATTERN, so a 400 'model not
  currently available for this chatgpt account' gets the entitlement rewrite, not
  generic error guidance (#13)
- forecast: quota-exhausted accounts are classified 'delayed' (not 'unavailable'),
  so they slipped the recommendation filter; added an explicit exhausted flag and
  excluded it, returning null when the whole pool is exhausted (#10)
- storage/record-utils: clampIndex guards NaN -> 0 (Math.trunc(NaN) propagated) (#16)
- local-client-tokens: debounce lastUsedAt persistence (60s threshold) so the
  bearer-verify hot path stops writing to disk every request; +chmod 0o700
  re-assert on the token-store dir (#12, #11)
- mcodex: relay SIGTERM/SIGINT to the spawned child so it isn't orphaned (#15)

Test quality:
- local-client-tokens: parameterized rename-retry test over ENOTEMPTY/EAGAIN/EACCES (#18)
- storage-flagged: clear the H4 deadlock-guard timer on the happy path (#17)
- storage: removeWithRetry for suite cleanup (#19)

Release:
- bump package.json + .codex-plugin/plugin.json to 2.2.1
- add docs/releases/v2.2.1.md; point docs portal + README at it

Known follow-ups (documented, deferred — need design, not rushed into a patch):
config env-path save is a single-process CAS not a true cross-process lock (#8/#9);
verifyLocalClientBearerToken read stays serialized but a fuller lease is future work;
runtime proxy routingMutex='enabled' still has a select/commit cursor race (#14)
requiring an async refactor of chooseAccount across its call sites.

Full suite: 4337 passed, 3 skipped, 0 failed; typecheck + lint clean.
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