mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-07 04:23:41 +00:00
fix(cursor): harden auth security and add token expiry warning
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
* - storage.serviceMachineId: Machine ID for checksum
|
* - storage.serviceMachineId: Machine ID for checksum
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { execSync } from 'child_process';
|
import { execFileSync } from 'child_process';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
@@ -52,9 +52,10 @@ export function getTokenStoragePath(): string {
|
|||||||
*/
|
*/
|
||||||
function queryStateDb(dbPath: string, key: string): string | null {
|
function queryStateDb(dbPath: string, key: string): string | null {
|
||||||
try {
|
try {
|
||||||
const result = execSync(
|
const result = execFileSync(
|
||||||
`sqlite3 "${dbPath}" "SELECT value FROM itemTable WHERE key='${key}'" 2>/dev/null`,
|
'sqlite3',
|
||||||
{ encoding: 'utf8', timeout: 5000 }
|
[dbPath, `SELECT value FROM itemTable WHERE key='${key}'`],
|
||||||
|
{ encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'ignore'] }
|
||||||
).trim();
|
).trim();
|
||||||
return result || null;
|
return result || null;
|
||||||
} catch {
|
} catch {
|
||||||
@@ -66,6 +67,15 @@ function queryStateDb(dbPath: string, key: string): string | null {
|
|||||||
* Auto-detect tokens from Cursor's SQLite database
|
* Auto-detect tokens from Cursor's SQLite database
|
||||||
*/
|
*/
|
||||||
export function autoDetectTokens(): AutoDetectResult {
|
export function autoDetectTokens(): AutoDetectResult {
|
||||||
|
// sqlite3 CLI is not bundled with Windows
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
return {
|
||||||
|
found: false,
|
||||||
|
error:
|
||||||
|
'Auto-detection is not supported on Windows. Please import tokens manually using ccs cursor auth --manual.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const dbPath = getTokenStoragePath();
|
const dbPath = getTokenStoragePath();
|
||||||
|
|
||||||
// Check if database exists
|
// Check if database exists
|
||||||
@@ -172,13 +182,16 @@ export function saveCredentials(credentials: CursorCredentials): void {
|
|||||||
const credPath = getCredentialsPath();
|
const credPath = getCredentialsPath();
|
||||||
const dir = path.dirname(credPath);
|
const dir = path.dirname(credPath);
|
||||||
|
|
||||||
// Ensure directory exists
|
// Ensure directory exists with restrictive permissions
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write credentials
|
// Write credentials with restrictive permissions
|
||||||
fs.writeFileSync(credPath, JSON.stringify(credentials, null, 2), 'utf8');
|
fs.writeFileSync(credPath, JSON.stringify(credentials, null, 2), {
|
||||||
|
encoding: 'utf8',
|
||||||
|
mode: 0o600,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -230,10 +243,14 @@ export function checkAuthStatus(): CursorAuthStatus {
|
|||||||
|
|
||||||
// Calculate token age in hours
|
// Calculate token age in hours
|
||||||
let tokenAge: number | undefined;
|
let tokenAge: number | undefined;
|
||||||
|
let expired = false;
|
||||||
|
const TOKEN_EXPIRY_HOURS = 24;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const importedDate = new Date(credentials.importedAt);
|
const importedDate = new Date(credentials.importedAt);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
tokenAge = Math.floor((now.getTime() - importedDate.getTime()) / (1000 * 60 * 60));
|
tokenAge = Math.floor((now.getTime() - importedDate.getTime()) / (1000 * 60 * 60));
|
||||||
|
expired = tokenAge >= TOKEN_EXPIRY_HOURS;
|
||||||
} catch {
|
} catch {
|
||||||
// Invalid date format
|
// Invalid date format
|
||||||
}
|
}
|
||||||
@@ -242,5 +259,6 @@ export function checkAuthStatus(): CursorAuthStatus {
|
|||||||
authenticated: true,
|
authenticated: true,
|
||||||
credentials,
|
credentials,
|
||||||
tokenAge,
|
tokenAge,
|
||||||
|
expired,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ export interface CursorAuthStatus {
|
|||||||
credentials?: CursorCredentials;
|
credentials?: CursorCredentials;
|
||||||
/** Hours since credentials were imported (if available) */
|
/** Hours since credentials were imported (if available) */
|
||||||
tokenAge?: number;
|
tokenAge?: number;
|
||||||
|
/** Whether token has expired (>24 hours old) */
|
||||||
|
expired?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user