hedgequantx 2.5.7 → 2.5.8
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.
- package/package.json +1 -1
- package/src/services/ai/token-scanner.js +324 -2
package/package.json
CHANGED
|
@@ -518,6 +518,318 @@ const getFileModTime = (filePath) => {
|
|
|
518
518
|
}
|
|
519
519
|
};
|
|
520
520
|
|
|
521
|
+
/**
|
|
522
|
+
* Known credential entries for AI providers (used by all OS)
|
|
523
|
+
* Organized by IDE/App
|
|
524
|
+
*/
|
|
525
|
+
const CREDENTIAL_ENTRIES = [
|
|
526
|
+
// VS Code
|
|
527
|
+
{ service: 'Claude Code-credentials', provider: 'anthropic', name: 'CLAUDE CODE (VS CODE)', ide: 'vscode' },
|
|
528
|
+
{ service: 'vscode.anthropic-credentials', provider: 'anthropic', name: 'VS CODE ANTHROPIC', ide: 'vscode' },
|
|
529
|
+
{ service: 'vscode-openai', provider: 'openai', name: 'VS CODE OPENAI', ide: 'vscode' },
|
|
530
|
+
{ service: 'copilot-credentials', provider: 'openai', name: 'GITHUB COPILOT', ide: 'vscode' },
|
|
531
|
+
|
|
532
|
+
// VS Code Insiders
|
|
533
|
+
{ service: 'Claude Code-credentials-insiders', provider: 'anthropic', name: 'CLAUDE CODE (INSIDERS)', ide: 'vscode-insiders' },
|
|
534
|
+
|
|
535
|
+
// Cursor
|
|
536
|
+
{ service: 'Cursor-credentials', provider: 'anthropic', name: 'CURSOR (CLAUDE)', ide: 'cursor' },
|
|
537
|
+
{ service: 'cursor.anthropic-credentials', provider: 'anthropic', name: 'CURSOR ANTHROPIC', ide: 'cursor' },
|
|
538
|
+
{ service: 'cursor.openai-credentials', provider: 'openai', name: 'CURSOR OPENAI', ide: 'cursor' },
|
|
539
|
+
|
|
540
|
+
// Windsurf
|
|
541
|
+
{ service: 'Windsurf-credentials', provider: 'anthropic', name: 'WINDSURF (CLAUDE)', ide: 'windsurf' },
|
|
542
|
+
{ service: 'windsurf.anthropic-credentials', provider: 'anthropic', name: 'WINDSURF ANTHROPIC', ide: 'windsurf' },
|
|
543
|
+
|
|
544
|
+
// Zed
|
|
545
|
+
{ service: 'Zed-credentials', provider: 'anthropic', name: 'ZED (CLAUDE)', ide: 'zed' },
|
|
546
|
+
{ service: 'zed.anthropic-credentials', provider: 'anthropic', name: 'ZED ANTHROPIC', ide: 'zed' },
|
|
547
|
+
{ service: 'zed.openai-credentials', provider: 'openai', name: 'ZED OPENAI', ide: 'zed' },
|
|
548
|
+
|
|
549
|
+
// Claude CLI / App
|
|
550
|
+
{ service: 'Claude Safe Storage', provider: 'anthropic', name: 'CLAUDE CLI', ide: 'claude-cli' },
|
|
551
|
+
{ service: 'claude-cli-credentials', provider: 'anthropic', name: 'CLAUDE CLI', ide: 'claude-cli' },
|
|
552
|
+
|
|
553
|
+
// Continue.dev
|
|
554
|
+
{ service: 'Continue-credentials', provider: 'anthropic', name: 'CONTINUE.DEV', ide: 'continue' },
|
|
555
|
+
{ service: 'continue.anthropic-credentials', provider: 'anthropic', name: 'CONTINUE ANTHROPIC', ide: 'continue' },
|
|
556
|
+
{ service: 'continue.openai-credentials', provider: 'openai', name: 'CONTINUE OPENAI', ide: 'continue' },
|
|
557
|
+
|
|
558
|
+
// Cline
|
|
559
|
+
{ service: 'Cline-credentials', provider: 'anthropic', name: 'CLINE', ide: 'cline' },
|
|
560
|
+
{ service: 'saoudrizwan.claude-dev-credentials', provider: 'anthropic', name: 'CLINE (CLAUDE DEV)', ide: 'cline' },
|
|
561
|
+
|
|
562
|
+
// OpenCode
|
|
563
|
+
{ service: 'OpenCode-credentials', provider: 'anthropic', name: 'OPENCODE', ide: 'opencode' },
|
|
564
|
+
{ service: 'opencode.anthropic-credentials', provider: 'anthropic', name: 'OPENCODE ANTHROPIC', ide: 'opencode' },
|
|
565
|
+
|
|
566
|
+
// Aider
|
|
567
|
+
{ service: 'Aider-credentials', provider: 'anthropic', name: 'AIDER', ide: 'aider' },
|
|
568
|
+
{ service: 'aider.anthropic-credentials', provider: 'anthropic', name: 'AIDER ANTHROPIC', ide: 'aider' },
|
|
569
|
+
{ service: 'aider.openai-credentials', provider: 'openai', name: 'AIDER OPENAI', ide: 'aider' },
|
|
570
|
+
|
|
571
|
+
// Generic OpenAI
|
|
572
|
+
{ service: 'openai-credentials', provider: 'openai', name: 'OPENAI', ide: 'generic' },
|
|
573
|
+
|
|
574
|
+
// Generic OpenRouter
|
|
575
|
+
{ service: 'openrouter-credentials', provider: 'openrouter', name: 'OPENROUTER', ide: 'generic' },
|
|
576
|
+
];
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Parse credential JSON and extract tokens
|
|
580
|
+
*/
|
|
581
|
+
const parseCredentialJson = (output, entry) => {
|
|
582
|
+
const results = [];
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
const data = JSON.parse(output);
|
|
586
|
+
|
|
587
|
+
// Extract Claude OAuth access token
|
|
588
|
+
if (data.claudeAiOauth?.accessToken) {
|
|
589
|
+
results.push({
|
|
590
|
+
source: `SECURE STORAGE - ${entry.name}`,
|
|
591
|
+
sourceId: 'secureStorage',
|
|
592
|
+
icon: '🔐',
|
|
593
|
+
type: data.claudeAiOauth.subscriptionType === 'max' ? 'session' : 'api_key',
|
|
594
|
+
provider: entry.provider,
|
|
595
|
+
providerName: PROVIDER_PATTERNS[entry.provider]?.displayName || entry.provider.toUpperCase(),
|
|
596
|
+
token: data.claudeAiOauth.accessToken,
|
|
597
|
+
refreshToken: data.claudeAiOauth.refreshToken,
|
|
598
|
+
expiresAt: data.claudeAiOauth.expiresAt,
|
|
599
|
+
subscriptionType: data.claudeAiOauth.subscriptionType,
|
|
600
|
+
lastUsed: new Date()
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Extract OpenAI token
|
|
605
|
+
if (data.accessToken && entry.provider === 'openai') {
|
|
606
|
+
results.push({
|
|
607
|
+
source: `SECURE STORAGE - ${entry.name}`,
|
|
608
|
+
sourceId: 'secureStorage',
|
|
609
|
+
icon: '🔐',
|
|
610
|
+
type: 'session',
|
|
611
|
+
provider: entry.provider,
|
|
612
|
+
providerName: PROVIDER_PATTERNS[entry.provider]?.displayName || entry.provider.toUpperCase(),
|
|
613
|
+
token: data.accessToken,
|
|
614
|
+
lastUsed: new Date()
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Generic API key in JSON
|
|
619
|
+
if (data.apiKey) {
|
|
620
|
+
results.push({
|
|
621
|
+
source: `SECURE STORAGE - ${entry.name}`,
|
|
622
|
+
sourceId: 'secureStorage',
|
|
623
|
+
icon: '🔐',
|
|
624
|
+
type: 'api_key',
|
|
625
|
+
provider: entry.provider,
|
|
626
|
+
providerName: PROVIDER_PATTERNS[entry.provider]?.displayName || entry.provider.toUpperCase(),
|
|
627
|
+
token: data.apiKey,
|
|
628
|
+
lastUsed: new Date()
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
} catch {
|
|
632
|
+
// Not JSON, treat as raw token
|
|
633
|
+
if (output.length > 20) {
|
|
634
|
+
for (const [providerId, provider] of Object.entries(PROVIDER_PATTERNS)) {
|
|
635
|
+
for (const pattern of provider.keyPatterns) {
|
|
636
|
+
pattern.lastIndex = 0;
|
|
637
|
+
if (pattern.test(output)) {
|
|
638
|
+
results.push({
|
|
639
|
+
source: `SECURE STORAGE - ${entry.name}`,
|
|
640
|
+
sourceId: 'secureStorage',
|
|
641
|
+
icon: '🔐',
|
|
642
|
+
type: 'api_key',
|
|
643
|
+
provider: providerId,
|
|
644
|
+
providerName: provider.displayName,
|
|
645
|
+
token: output,
|
|
646
|
+
lastUsed: new Date()
|
|
647
|
+
});
|
|
648
|
+
break;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
return results;
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Read tokens from macOS Keychain
|
|
660
|
+
*/
|
|
661
|
+
const readMacOSKeychain = () => {
|
|
662
|
+
if (platform !== 'darwin') return [];
|
|
663
|
+
|
|
664
|
+
const results = [];
|
|
665
|
+
const { execSync } = require('child_process');
|
|
666
|
+
|
|
667
|
+
for (const entry of CREDENTIAL_ENTRIES) {
|
|
668
|
+
try {
|
|
669
|
+
const output = execSync(
|
|
670
|
+
`security find-generic-password -s "${entry.service}" -w 2>/dev/null`,
|
|
671
|
+
{ encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
672
|
+
).trim();
|
|
673
|
+
|
|
674
|
+
if (output) {
|
|
675
|
+
results.push(...parseCredentialJson(output, entry));
|
|
676
|
+
}
|
|
677
|
+
} catch {
|
|
678
|
+
// Entry not found or access denied
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return results;
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Read tokens from Linux Secret Service (libsecret/gnome-keyring)
|
|
687
|
+
*/
|
|
688
|
+
const readLinuxSecretService = () => {
|
|
689
|
+
if (platform !== 'linux') return [];
|
|
690
|
+
|
|
691
|
+
const results = [];
|
|
692
|
+
const { execSync } = require('child_process');
|
|
693
|
+
|
|
694
|
+
// Check if secret-tool is available
|
|
695
|
+
try {
|
|
696
|
+
execSync('which secret-tool', { stdio: 'pipe' });
|
|
697
|
+
} catch {
|
|
698
|
+
return results; // secret-tool not available
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
for (const entry of CREDENTIAL_ENTRIES) {
|
|
702
|
+
try {
|
|
703
|
+
// Try different attribute combinations
|
|
704
|
+
const commands = [
|
|
705
|
+
`secret-tool lookup service "${entry.service}" 2>/dev/null`,
|
|
706
|
+
`secret-tool lookup application "${entry.service}" 2>/dev/null`,
|
|
707
|
+
`secret-tool lookup xdg:schema "org.freedesktop.Secret.Generic" service "${entry.service}" 2>/dev/null`,
|
|
708
|
+
];
|
|
709
|
+
|
|
710
|
+
for (const cmd of commands) {
|
|
711
|
+
try {
|
|
712
|
+
const output = execSync(cmd, {
|
|
713
|
+
encoding: 'utf8',
|
|
714
|
+
timeout: 5000,
|
|
715
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
716
|
+
}).trim();
|
|
717
|
+
|
|
718
|
+
if (output) {
|
|
719
|
+
results.push(...parseCredentialJson(output, entry));
|
|
720
|
+
break; // Found it, no need to try other commands
|
|
721
|
+
}
|
|
722
|
+
} catch {
|
|
723
|
+
// Command failed, try next
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
} catch {
|
|
727
|
+
// Entry not found
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Also check VS Code's pass-based storage on Linux
|
|
732
|
+
const passEntries = [
|
|
733
|
+
'vscode/Claude Code-credentials',
|
|
734
|
+
'vscode/Cursor-credentials',
|
|
735
|
+
'vscode/openai-credentials',
|
|
736
|
+
];
|
|
737
|
+
|
|
738
|
+
for (const passPath of passEntries) {
|
|
739
|
+
try {
|
|
740
|
+
const output = execSync(`pass show "${passPath}" 2>/dev/null`, {
|
|
741
|
+
encoding: 'utf8',
|
|
742
|
+
timeout: 5000,
|
|
743
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
744
|
+
}).trim();
|
|
745
|
+
|
|
746
|
+
if (output) {
|
|
747
|
+
const entry = CREDENTIAL_ENTRIES.find(e => passPath.includes(e.service)) ||
|
|
748
|
+
{ service: passPath, provider: 'unknown', name: passPath };
|
|
749
|
+
results.push(...parseCredentialJson(output, entry));
|
|
750
|
+
}
|
|
751
|
+
} catch {
|
|
752
|
+
// pass entry not found
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
return results;
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Read tokens from Windows Credential Manager
|
|
761
|
+
*/
|
|
762
|
+
const readWindowsCredentialManager = () => {
|
|
763
|
+
if (platform !== 'win32') return [];
|
|
764
|
+
|
|
765
|
+
const results = [];
|
|
766
|
+
const { execSync } = require('child_process');
|
|
767
|
+
|
|
768
|
+
for (const entry of CREDENTIAL_ENTRIES) {
|
|
769
|
+
try {
|
|
770
|
+
// Use PowerShell to read from Credential Manager
|
|
771
|
+
const psCommand = `
|
|
772
|
+
$cred = Get-StoredCredential -Target "${entry.service}" -ErrorAction SilentlyContinue
|
|
773
|
+
if ($cred) {
|
|
774
|
+
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
|
|
775
|
+
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($cred.Password)
|
|
776
|
+
)
|
|
777
|
+
}
|
|
778
|
+
`;
|
|
779
|
+
|
|
780
|
+
const output = execSync(`powershell -Command "${psCommand.replace(/\n/g, ' ')}"`, {
|
|
781
|
+
encoding: 'utf8',
|
|
782
|
+
timeout: 10000,
|
|
783
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
784
|
+
}).trim();
|
|
785
|
+
|
|
786
|
+
if (output) {
|
|
787
|
+
results.push(...parseCredentialJson(output, entry));
|
|
788
|
+
}
|
|
789
|
+
} catch {
|
|
790
|
+
// Try cmdkey as fallback (less reliable for getting password)
|
|
791
|
+
try {
|
|
792
|
+
// cmdkey can list but not retrieve passwords directly
|
|
793
|
+
// VS Code on Windows often uses DPAPI-encrypted files instead
|
|
794
|
+
} catch {
|
|
795
|
+
// Entry not found
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Also check VS Code's DPAPI-encrypted storage on Windows
|
|
801
|
+
const vscodeCredPath = path.join(
|
|
802
|
+
process.env.APPDATA || '',
|
|
803
|
+
'Code',
|
|
804
|
+
'User',
|
|
805
|
+
'globalStorage',
|
|
806
|
+
'state.vscdb'
|
|
807
|
+
);
|
|
808
|
+
|
|
809
|
+
if (pathExists(vscodeCredPath)) {
|
|
810
|
+
const dbResults = readVSCodeStateDb(vscodeCredPath);
|
|
811
|
+
results.push(...dbResults);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return results;
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Read tokens from OS secure storage (unified function)
|
|
819
|
+
*/
|
|
820
|
+
const readSecureStorage = () => {
|
|
821
|
+
switch (platform) {
|
|
822
|
+
case 'darwin':
|
|
823
|
+
return readMacOSKeychain();
|
|
824
|
+
case 'linux':
|
|
825
|
+
return readLinuxSecretService();
|
|
826
|
+
case 'win32':
|
|
827
|
+
return readWindowsCredentialManager();
|
|
828
|
+
default:
|
|
829
|
+
return [];
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
|
|
521
833
|
/**
|
|
522
834
|
* Try to read VS Code SQLite state database
|
|
523
835
|
*/
|
|
@@ -935,10 +1247,18 @@ const scanSource = (sourceId) => {
|
|
|
935
1247
|
const scanAllSources = () => {
|
|
936
1248
|
const allResults = [];
|
|
937
1249
|
|
|
938
|
-
// First, scan
|
|
1250
|
+
// First, scan OS secure storage (Keychain, libsecret, Credential Manager)
|
|
1251
|
+
// This is the most reliable source for IDE tokens
|
|
1252
|
+
try {
|
|
1253
|
+
allResults.push(...readSecureStorage());
|
|
1254
|
+
} catch (err) {
|
|
1255
|
+
// Silent fail
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// Then scan environment variables
|
|
939
1259
|
allResults.push(...scanEnvironmentVariables());
|
|
940
1260
|
|
|
941
|
-
// Then scan all tool sources
|
|
1261
|
+
// Then scan all tool sources (config files)
|
|
942
1262
|
for (const sourceId of Object.keys(TOKEN_SOURCES)) {
|
|
943
1263
|
if (sourceId === 'envVars') continue; // Already scanned
|
|
944
1264
|
|
|
@@ -1056,10 +1376,12 @@ const getSystemInfo = () => {
|
|
|
1056
1376
|
module.exports = {
|
|
1057
1377
|
TOKEN_SOURCES,
|
|
1058
1378
|
PROVIDER_PATTERNS,
|
|
1379
|
+
CREDENTIAL_ENTRIES,
|
|
1059
1380
|
scanAllSources,
|
|
1060
1381
|
scanForProvider,
|
|
1061
1382
|
scanSource,
|
|
1062
1383
|
scanEnvironmentVariables,
|
|
1384
|
+
readSecureStorage,
|
|
1063
1385
|
formatResults,
|
|
1064
1386
|
timeAgo,
|
|
1065
1387
|
hasExistingTokens,
|