fix(cursor): address second-round review feedback for auth module

- Add comprehensive unit tests for cursor-auth.test.ts
  - validateToken: valid/invalid tokens, short tokens, UUID formats, empty strings
  - extractUserInfo: JWT parsing, email handling, non-JWT tokens, malformed base64
  - saveCredentials/loadCredentials: round-trip, invalid JSON/types, missing fields
  - checkAuthStatus: authenticated/not authenticated, expired tokens, JWT exp, invalid dates
  - deleteCredentials: delete existing/non-existent files, multiple deletes
  - All tests use CCS_HOME env var for isolation, real file I/O, no mocks

- Fix dead try-catch around new Date() in checkAuthStatus()
  - Replace try-catch with isNaN check (new Date('garbage') returns Invalid Date, not throw)
  - Properly handle Invalid Date by checking isNaN(getTime())

- Fix email populated with sub claim in extractUserInfo()
  - Change email: decoded.email || decoded.sub to email: decoded.email || undefined
  - Prevent non-email values (UUIDs) from populating email field

- Add type guards for JSON.parse result in extractUserInfo()
  - Cast to Record<string, unknown> and validate types
  - Use typeof checks for email, userId, exp fields
This commit is contained in:
Tam Nhu Tran
2026-02-12 01:23:29 +07:00
parent b412ba2a9e
commit 84a256d0ac
2 changed files with 399 additions and 8 deletions
+11 -8
View File
@@ -164,11 +164,16 @@ export function extractUserInfo(
}
const decoded = JSON.parse(
Buffer.from(payload.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString()
);
) as Record<string, unknown>;
return {
email: decoded.email || decoded.sub,
userId: decoded.sub || decoded.user_id,
exp: decoded.exp,
email: typeof decoded.email === 'string' ? decoded.email : undefined,
userId:
typeof decoded.sub === 'string'
? decoded.sub
: typeof decoded.user_id === 'string'
? decoded.user_id
: undefined,
exp: typeof decoded.exp === 'number' ? decoded.exp : undefined,
};
}
} catch {
@@ -274,13 +279,11 @@ export function checkAuthStatus(): CursorAuthStatus {
} else {
// Fallback to importedAt heuristic
const TOKEN_EXPIRY_HOURS = 24;
try {
const importedDate = new Date(credentials.importedAt);
const importedDate = new Date(credentials.importedAt);
if (!isNaN(importedDate.getTime())) {
const now = new Date();
tokenAge = Math.floor((now.getTime() - importedDate.getTime()) / (1000 * 60 * 60));
expired = tokenAge >= TOKEN_EXPIRY_HOURS;
} catch {
// Invalid date format
}
}