-
-
Notifications
You must be signed in to change notification settings - Fork 90
feat(cursor): add credential detection, SQLite token extraction, and auth status #526
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add CursorCredentials, CursorAuthStatus, and related types - Implement autoDetectTokens() using sqlite3 CLI (no native deps) - Add validateToken() for token format validation - Add extractUserInfo() for JWT decoding - Add saveCredentials/loadCredentials using getCcsDir() - Add checkAuthStatus() for auth verification - Platform-specific state.vscdb paths (Linux/macOS/Windows) - Graceful error handling for missing database/tokens Refs: #519 (sub-task of #517)
Code Review: PR #526 —
|
| Check | Status | Notes |
|---|---|---|
| Command injection prevention | ✅ | execFileSync used (no shell interpretation) |
| SQL injection prevention | ❌ | Key parameter interpolated directly into SQL query |
| File permission hardening | ✅ | 0o600 for files, 0o700 for directories |
| Secrets in source code | ✅ | No hardcoded secrets |
| Input validation | Token validated; SQL key not sanitized; loaded JSON weakly validated | |
| Path traversal prevention | ✅ | Paths constructed from platform APIs, not user input |
| JWT handling | ✅ | Decode-only, no verification (appropriate for local token extraction) |
| Credential storage | Plaintext JSON on disk (acceptable with 0o600 perms, consistent with codebase) |
📊 Code Quality Checklist
| Criteria | Status | Notes |
|---|---|---|
Follows CLAUDE.md guidelines |
YAGNI violation in types.ts | |
Uses getCcsDir() for CCS paths |
✅ | Correctly used for credential storage |
Uses os.homedir() appropriately |
✅ | Only for Cursor's own install paths (not CCS paths) |
No any types |
✅ | Uses unknown for parsed JSON |
| No non-null assertions | ✅ | None found |
| Conventional commit format | ✅ | feat(cursor): and fix(cursor): |
| Tests included | ❌ | No tests for 407 lines of new code |
| ASCII-only CLI output | ✅ | No CLI output in this module (library code) |
| Error handling | ✅ | Graceful fallbacks throughout |
| Cross-platform support | ✅ | Linux, macOS, Windows paths handled |
💡 Recommendations
| Priority | Issue | Action |
|---|---|---|
| 🔴 High | SQL injection in queryStateDb() |
Sanitize key param by escaping single quotes |
| 🔴 High | No test coverage | Add tests/unit/cursor/cursor-auth.test.ts |
| 🟡 Medium | UUID regex bug | Change {32,} to {32}, remove - from char class |
| 🟡 Medium | YAGNI in types.ts | Remove unused interfaces; add them in their respective PRs |
| 🟡 Medium | PR description mentions refreshToken |
Either implement or update description |
| 🟡 Medium | Token expiry based on import time | Consider using JWT exp claim when available |
| 🟢 Low | Weak loadCredentials() validation |
Add typeof checks on values, not just key existence |
| 🟢 Low | Silent sqlite3 failure | Detect ENOENT and return descriptive error |
| 🟢 Low | No deleteCredentials() |
Add credential cleanup function |
🎯 Overall Assessment
⚠️ APPROVED WITH NOTES
The module demonstrates solid security awareness (file permissions, execFileSync, graceful error handling) and follows most codebase conventions. However, two items should be addressed before merge:
- SQL injection in
queryStateDb()— While currently called only with hardcoded keys, this is a latent vulnerability that violates defense-in-depth. A one-line fix (key.replace(/'/g, "''")) eliminates the risk. - Missing tests — 407 lines of security-sensitive credential handling code with zero tests is a significant gap. The module's pure functions (
validateToken,extractUserInfo) andgetCcsDir()-based storage make it straightforward to test.
The YAGNI issues in types.ts and UUID regex bug are worth fixing but not blocking.
🤖 Reviewed by
gemini-claude-opus-4-6-thinking
- Sanitize SQL key parameter in queryStateDb to prevent injection
- Fix UUID regex: use exact {32} length, remove redundant hyphen from
stripped character class
- Remove unused types from types.ts (YAGNI): CursorDaemonStatus,
CursorModel, MessageRole, CursorMessage, CursorToolCall,
CursorToolResult, ProtobufExtractResult — belong in their respective
module PRs
Code Review: PR #526 —
|
| Check | Status | Notes |
|---|---|---|
| Command injection prevention | ✅ | execFileSync — no shell interpretation |
| SQL injection prevention | ✅ | Key parameter sanitized with single-quote escaping |
| File permission hardening | ✅ | 0o600 for credentials, 0o700 for directory |
| Secrets in source code | ✅ | No hardcoded secrets |
| Input validation | Token validated; loaded JSON weakly validated (key existence only) | |
| Path traversal prevention | ✅ | Paths from platform APIs, not user input |
| JWT handling | ✅ | Decode-only, no verification (appropriate for local extraction) |
| Credential storage | ✅ | Plaintext with 0o600 permissions (consistent with codebase pattern) |
📊 Code Quality Checklist
| Criteria | Status | Notes |
|---|---|---|
Follows CLAUDE.md guidelines |
✅ | YAGNI cleaned up, getCcsDir() used correctly |
Uses getCcsDir() for CCS paths |
✅ | cursor-auth.ts:177 |
Uses os.homedir() appropriately |
✅ | Only for Cursor's own install paths (not CCS paths) |
No any types |
Implicit any from JSON.parse at line 158 |
|
| No non-null assertions | ✅ | None found |
| Conventional commit format | ✅ | feat(cursor):, fix(cursor): |
| Tests included | ❌ | No tests for 317 lines of new code |
| ASCII-only CLI output | ✅ | Module is library code with no CLI output |
| Error handling | Dead try-catch around new Date() |
|
| Cross-platform support | ✅ | Linux, macOS paths handled; Windows returns clear error |
💡 Recommendations
| Priority | Issue | Action |
|---|---|---|
| 🔴 High | No test coverage | Add tests/unit/cursor/cursor-auth.test.ts covering validateToken, extractUserInfo, credential persistence, and checkAuthStatus |
| 🟡 Medium | Dead try-catch around new Date() |
Replace with isNaN() check — new Date() never throws |
| 🟡 Medium | email populated with sub claim |
Use decoded.email || undefined instead of decoded.email || decoded.sub |
| 🟡 Medium | PR description mentions refreshToken |
Update description to match implementation |
| 🟢 Low | Weak loadCredentials() validation |
Add typeof checks on values, not just key existence |
| 🟢 Low | Silent sqlite3 ENOENT |
Surface "sqlite3 not installed" error to user |
| 🟢 Low | Implicit any from JSON.parse |
Cast to Record<string, unknown> with type guards |
🎯 Overall Assessment
⚠️ APPROVED WITH NOTES
The author has done good work addressing the critical security findings from the initial review — SQL injection is mitigated, execFileSync prevents shell interpretation, file permissions are restrictive, and the YAGNI cleanup leaves a focused type surface. The module is well-structured and follows codebase conventions.
Remaining items to address:
- Tests (HIGH) — This is security-sensitive credential handling code. Unit tests for the pure functions are straightforward and should be included before merge.
- Dead
try-catch(MEDIUM) —new Date()never throws; a corruptedimportedAtsilently reportsexpired: false. Quick fix withisNaN(). - Email/sub confusion (MEDIUM) — Minor but could surface incorrect data to users.
The low-priority items are non-blocking suggestions for improvement.
🤖 Reviewed by
gemini-claude-opus-4-6-thinking
- Enhanced extractUserInfo to return JWT exp claim for expiry detection - Updated checkAuthStatus to use JWT exp when available, fallback to importedAt heuristic - Added typeof validation for loadCredentials fields - Added ENOENT detection for sqlite3 availability in queryStateDb - Added deleteCredentials function for cleanup - Updated PR #526 description to remove refreshToken mention
- 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
|
/review |
Code Review: PR #526 — feat(cursor): add credential detection, SQLite token extraction, and auth status📋 SummaryThis PR introduces a Cursor IDE authentication module with SQLite-based credential auto-detection, JWT token parsing, and credential persistence. The code is well-structured with clean separation between types and logic, proper use of ✅ Strengths
🔍 Observations & Suggestions🔴 HIGH — Test path mismatch: 4 negative tests pass for the wrong reason
Four The tests write to: But Note the missing Affected tests:
Fix: Use // Option A: Use the exported helper
import { getCredentialsPath } from '../../../src/cursor/cursor-auth';
const credPath = getCredentialsPath();
// Option B: Include .ccs in the path
const credPath = path.join(tempDir, '.ccs', 'cursor', 'credentials.json');🟡 MEDIUM — Fragile
|
| Check | Status | Notes |
|---|---|---|
| Command injection prevention | ✅ | execFileSync with array args |
| SQL injection prevention | ✅ | Single-quote escaping + private function with hardcoded keys only |
| File permission hardening | ✅ | 0o600 file, 0o700 directory |
| No secrets in logs/output | ✅ | No console.log of tokens |
| Input validation at boundaries | ✅ | loadCredentials validates types before cast |
| JWT signature verification | ❌ N/A | Intentionally not verified — this extracts metadata, not authenticates. Documented behavior. |
| Credential cleanup available | ✅ | deleteCredentials() provided |
Test isolation from real ~/.ccs |
✅ | CCS_HOME env var respected |
Windows APPDATA fallback |
✅ | Falls back to ~/AppData/Roaming if env var missing |
📊 Code Quality Checklist
| Criterion | Status | Notes |
|---|---|---|
| TypeScript strict mode compatible | ✅ | Proper type narrowing, no any |
Uses getCcsDir() (not os.homedir()) |
✅ | cursor-auth.ts:190 |
| No emojis in CLI output | ✅ | No terminal output in this module |
| Error handling | ✅ | Fail-fast with clear error messages |
| Conventional commits | ✅ | All 5 commits follow format |
| YAGNI compliance | ✅ | Types trimmed in commit 7e4f080 |
| Cross-platform paths | ✅ | Platform-specific getTokenStoragePath() |
| Test isolation | ✅ | CCS_HOME + temp directory cleanup |
| No unused imports | ✅ | All imports consumed |
💡 Recommendations
| Priority | Item | Location |
|---|---|---|
| 🔴 High | Fix test path mismatch — 4 tests write to wrong directory and pass vacuously | tests/unit/cursor/cursor-auth.test.ts:189-245 |
| 🟡 Medium | Use importedAt for tokenAge when JWT exp is available, use exp only for expired flag |
src/cursor/cursor-auth.ts:278 |
| 🟡 Medium | Add autoDetectTokens tests for at minimum the Windows and missing-db branches |
tests/unit/cursor/ |
| 🟢 Low | Simplify deleteCredentials by removing redundant existsSync |
src/cursor/cursor-auth.ts:301-313 |
🎯 Overall Assessment
⚠️ APPROVED WITH NOTES
The implementation is solid with good security practices, proper test isolation, and clean architecture. The four broken tests (they pass for the wrong reason due to a path mismatch) should be fixed before merge — they currently provide false confidence that validation logic works when in fact they're only testing "file not found". The tokenAge calculation with JWT exp is fragile but functional. Overall, this is a well-thought-out auth module that follows the project's conventions.
🤖 Reviewed by
claude-opus-4-6-thinking
Code Review: PR #526 —
|
| Check | Status | Notes |
|---|---|---|
| Shell injection prevention | ✅ | execFileSync with args array, not execSync |
| SQL injection in SQLite queries | ✅ | Single-quote escaping; keys are hardcoded constants |
| Credential file permissions | ✅ | 0o600 files, 0o700 directories |
| No secrets in logs/errors | ✅ | Error messages contain no token data |
Test isolation from real ~/.ccs/ |
✅ | Uses CCS_HOME env var via getCcsDir() |
| No hardcoded secrets | ✅ | Clean |
| JWT parsed without verification | ✅ | Appropriate — extracting claims, not making auth decisions |
| Timeout on external process | ✅ | 5000ms timeout on execFileSync |
📊 Code Quality Checklist
| Criterion | Status | Notes |
|---|---|---|
| TypeScript strict mode compliant | ✅ | unknown parsing, type guards, no any |
| Follows existing codebase patterns | ✅ | Matches copilot-auth, cliproxy/auth patterns |
| Test isolation (CLAUDE.md) | ✅ | CCS_HOME properly used |
| No emojis in CLI output | ✅ | No CLI output in this module |
| Error handling | ✅ | Graceful null/false returns |
| Cross-platform consideration | ✅ | win32/darwin/linux paths; Windows auto-detect blocked with message |
| YAGNI / KISS compliance | ✅ | Focused scope, no unnecessary abstractions |
| Conventional commit format | ✅ | feat(cursor): prefix |
💡 Recommendations
| Priority | Recommendation |
|---|---|
| 🟢 Low | Fix exp truthiness check at cursor-auth.ts:278 — use !== undefined instead of truthiness to handle theoretical exp: 0 |
| 🟢 Low | Update PR description to remove CursorDaemonStatus mention or note it as deferred |
| 🟢 Low | Consider adding unit tests for getTokenStoragePath() platform branching |
🎯 Overall Assessment
✅ APPROVED
This is a well-implemented authentication module that follows established codebase patterns for credential handling, file security, and test isolation. The code is focused, type-safe, and defensively programmed with appropriate error handling. The test suite is thorough with good edge case coverage. The minor observations above are all low-priority and non-blocking.
🤖 Reviewed by
claude-opus-4-6-thinking
Summary
Files Added (2)
src/cursor/cursor-auth.ts— Credential detection, SQLite extraction, auth statussrc/cursor/types.ts— Shared type definitions (CursorCredentials, CursorAuthStatus, CursorDaemonStatus)Edge Cases Fixed (code review)
Test plan