fix(auth): fail closed when a session's stored expiry is unparseable#1498
fix(auth): fail closed when a session's stored expiry is unparseable#1498nickmopen wants to merge 2 commits into
Conversation
authenticateSessionToken rejected revoked and past-dated sessions, but a session whose stored expires_at is malformed/empty parsed to NaN, and `NaN <= Date.now()` is false — so it authenticated as if it never expired. Guard the parse with Number.isFinite so an unparseable expiry fails closed (rejected), matching the expired-session path. Mirrors the existing expired-session test with a malformed-expiry case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1498 +/- ##
=======================================
Coverage 95.37% 95.37%
=======================================
Files 199 199
Lines 21546 21547 +1
Branches 7791 7791
=======================================
+ Hits 20550 20551 +1
Misses 416 416
Partials 580 580
🚀 New features to boost your workflow:
|
|
Caution 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🛑 Gittensory review — blocked
🛑 Blocked Review summary CI checks failing
Nits — 6 non-blocking
Review context
Contributor next steps
Signal definitions
Review detailsGenerated from public PR metadata and the diff. Advisory only; deterministic signals remain authoritative. The change correctly patches a fail-open in `authenticateSessionToken` (src/auth/security.ts:121–124): by extracting the `Date.parse` result and guarding with `Number.isFinite`, a malformed or empty `expires_at` stored in the DB is now rejected rather than silently authenticated as a never-expiring session. The logic is minimal and correct — `Number.isFinite(NaN)` is `false`, the short-circuit fires, and revoked/legitimately-expired session paths are unchanged. The regression test in the diff directly covers the new behavior, though it is nested inside an existing `it()` case. Nits (4)
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Summary
authenticateSessionToken(src/auth/security.ts) gates a session on:A session whose stored
expires_atis malformed or empty parses toNaN, andNaN <= Date.now()isfalse. Combined with a nullrevokedAt, the session is then treated as valid and never-expiring — a fail-open on the expiry check.Problem
expires_atis normally written by our own session-creation path, but the auth check should not depend on that invariant holding for every row (DB drift, a partial write, a manually inserted row). An unparseable expiry must be rejected, not silently honored forever. This matches the project's fail-closed posture (e.g. #1368 "fail closed when … unreadable").Fix
Parse once and guard with
Number.isFinite, so an unparseable expiry fails closed exactly like an expired one. Revoked and past-dated behavior is unchanged.Validation
Run from repo root (Node 24):
npm run test:ci # GATE_EXIT=0 — 4607 passed / 4 skipped; typecheck, coverage, workers, MCP, UI all greenAdded a regression assertion alongside the existing expired-session test in
test/unit/auth.test.ts: a session withexpires_at = "not-a-date"now authenticates tonull(it would have returned a valid identity before this fix).