fullcourtdefense-cli 1.14.16 → 1.14.17
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.
|
@@ -867,19 +867,6 @@ async function discoverCommand(args, config) {
|
|
|
867
867
|
})
|
|
868
868
|
: undefined;
|
|
869
869
|
const host = buildHostMetadata(args.userEmail, deep && found.some(s => s.probeMode === 'deep') ? 'deep' : 'config');
|
|
870
|
-
if (args.json === 'true') {
|
|
871
|
-
console.log(JSON.stringify({
|
|
872
|
-
host,
|
|
873
|
-
surfaces: [...surfaces],
|
|
874
|
-
scannedFiles: scanned.map(s => `${s.source} → ${s.path}`),
|
|
875
|
-
clientCoverage,
|
|
876
|
-
servers: found,
|
|
877
|
-
secrets,
|
|
878
|
-
agentFiles,
|
|
879
|
-
posture,
|
|
880
|
-
}, null, 2));
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
870
|
const home = os.homedir();
|
|
884
871
|
const uploadExtras = {
|
|
885
872
|
secrets,
|
|
@@ -919,6 +906,20 @@ async function discoverCommand(args, config) {
|
|
|
919
906
|
}
|
|
920
907
|
await upload(found, host, clientCoverage, creds.apiUrl, { apiKey: creds.apiKey, shieldId: creds.shieldId, shieldKey: creds.shieldKey, preferApiKey: !!args.apiKey }, args.connectorName, uploadExtras);
|
|
921
908
|
}
|
|
909
|
+
if (args.json === 'true') {
|
|
910
|
+
await maybeUpload();
|
|
911
|
+
console.log(JSON.stringify({
|
|
912
|
+
host,
|
|
913
|
+
surfaces: [...surfaces],
|
|
914
|
+
scannedFiles: scanned.map(s => `${s.source} → ${s.path}`),
|
|
915
|
+
clientCoverage,
|
|
916
|
+
servers: found,
|
|
917
|
+
secrets,
|
|
918
|
+
agentFiles,
|
|
919
|
+
posture,
|
|
920
|
+
}, null, 2));
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
922
923
|
if (silent) {
|
|
923
924
|
if (uploadRequested) {
|
|
924
925
|
const creds = (0, config_1.resolveCliCredentials)(config, { apiKey: args.apiKey, apiUrl: args.apiUrl });
|
|
@@ -402,6 +402,48 @@ function collectEnvFiles(roots, maxDepth) {
|
|
|
402
402
|
}
|
|
403
403
|
return found;
|
|
404
404
|
}
|
|
405
|
+
function collectProjectCredentialFiles(roots, maxDepth) {
|
|
406
|
+
const found = [];
|
|
407
|
+
const queue = roots.map(dir => ({ dir, depth: 0 }));
|
|
408
|
+
const isCredentialFile = (filePath) => {
|
|
409
|
+
const normalized = filePath.replace(/\\/g, '/').toLowerCase();
|
|
410
|
+
const base = path.basename(normalized);
|
|
411
|
+
return base === '.npmrc'
|
|
412
|
+
|| base === '.pypirc'
|
|
413
|
+
|| base === '.netrc'
|
|
414
|
+
|| normalized.endsWith('/.docker/config.json')
|
|
415
|
+
|| normalized.endsWith('/.kube/config')
|
|
416
|
+
|| normalized.endsWith('/gcloud/application_default_credentials.json');
|
|
417
|
+
};
|
|
418
|
+
while (queue.length > 0 && found.length < MAX_ENV_FILES) {
|
|
419
|
+
const { dir, depth } = queue.shift();
|
|
420
|
+
let entries;
|
|
421
|
+
try {
|
|
422
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
for (const entry of entries) {
|
|
428
|
+
if (found.length >= MAX_ENV_FILES)
|
|
429
|
+
break;
|
|
430
|
+
const full = path.join(dir, entry.name);
|
|
431
|
+
if (entry.isDirectory()) {
|
|
432
|
+
if (depth >= maxDepth)
|
|
433
|
+
continue;
|
|
434
|
+
if (SKIP_DIR_NAMES.has(entry.name))
|
|
435
|
+
continue;
|
|
436
|
+
if (entry.name.startsWith('.') && !['.cursor', '.claude', '.codex', '.vscode', '.docker', '.kube', '.config'].includes(entry.name))
|
|
437
|
+
continue;
|
|
438
|
+
queue.push({ dir: full, depth: depth + 1 });
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (entry.isFile() && isCredentialFile(full))
|
|
442
|
+
found.push(full);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return found;
|
|
446
|
+
}
|
|
405
447
|
function scanSecrets(options = {}) {
|
|
406
448
|
const home = os.homedir();
|
|
407
449
|
const cwd = options.cwd || process.cwd();
|
|
@@ -451,6 +493,16 @@ function scanSecrets(options = {}) {
|
|
|
451
493
|
scannedFiles += 1;
|
|
452
494
|
scanTextLines(envFile, content, 'env_file', findings, seen);
|
|
453
495
|
}
|
|
496
|
+
for (const credentialFile of collectProjectCredentialFiles([cwd, ...extraRoots], maxDepth)) {
|
|
497
|
+
if (findings.length >= MAX_FINDINGS)
|
|
498
|
+
break;
|
|
499
|
+
const content = readTextFile(credentialFile);
|
|
500
|
+
if (!content)
|
|
501
|
+
continue;
|
|
502
|
+
scannedFiles += 1;
|
|
503
|
+
scanTextLines(credentialFile, content, 'credential_store', findings, seen);
|
|
504
|
+
scanCredentialStorePosture(credentialFile, content, 'credential_store', findings, seen);
|
|
505
|
+
}
|
|
454
506
|
// MCP/client config files are already discovered by the MCP inventory pass.
|
|
455
507
|
// Scan only those known files for embedded env/header/arg secrets; this catches
|
|
456
508
|
// real-world mistakes like GitHub/Slack tokens in MCP `env`, DB URLs in `args`,
|
|
@@ -759,6 +759,7 @@ class McpGatewayServer {
|
|
|
759
759
|
shieldKey: this.gatewayConfig.shieldKey,
|
|
760
760
|
developerName: this.gatewayConfig.developerName,
|
|
761
761
|
machineName: os.hostname(),
|
|
762
|
+
force: true,
|
|
762
763
|
});
|
|
763
764
|
if (bundle.source !== 'default')
|
|
764
765
|
expectedPolicyHash = bundle.policyHash || bundle.version;
|
|
@@ -1159,7 +1160,7 @@ async function installMcpGatewayCommand(args, config) {
|
|
|
1159
1160
|
shieldId: args.shieldId,
|
|
1160
1161
|
shieldKey: args.shieldKey,
|
|
1161
1162
|
apiUrl: args.apiUrl,
|
|
1162
|
-
}, { requireOrganizationId: false });
|
|
1163
|
+
}, { requireApiKey: false, requireOrganizationId: false });
|
|
1163
1164
|
const cursorProject = args.cursorProject === 'true';
|
|
1164
1165
|
const selection = parseClientList(args.clients);
|
|
1165
1166
|
const clients = selection === 'auto'
|
|
@@ -1748,7 +1749,7 @@ function protectAllTargetFiles(extra, allowedClientKeys) {
|
|
|
1748
1749
|
return files;
|
|
1749
1750
|
}
|
|
1750
1751
|
async function protectAllCommand(args, config) {
|
|
1751
|
-
(0, config_1.requireCliSetup)(config, { shieldId: args.shieldId, shieldKey: args.shieldKey, apiUrl: args.apiUrl }, { requireOrganizationId: false });
|
|
1752
|
+
(0, config_1.requireCliSetup)(config, { shieldId: args.shieldId, shieldKey: args.shieldKey, apiUrl: args.apiUrl }, { requireApiKey: false, requireOrganizationId: false });
|
|
1752
1753
|
const gatewayConfig = resolveGatewayConfig(args, config);
|
|
1753
1754
|
const dryRun = args.dryRun === 'true';
|
|
1754
1755
|
const allowedClientKeys = resolveProtectClientKeys(args.clients);
|
package/dist/version.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fullcourtdefense-cli",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.17",
|
|
4
4
|
"description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"test:discover-stdio-mcp": "npm run build && node scripts/test-discover-stdio-mcp-tools.js",
|
|
27
27
|
"test:per-server-agent-name": "npm run build && node scripts/test-per-server-agent-name.js",
|
|
28
28
|
"test:remote-mcp-gateway": "npm run build && node scripts/test-remote-mcp-gateway.js",
|
|
29
|
+
"test:e2e-onboarding-personas": "npm run build && node scripts/test-e2e-onboarding-personas.js",
|
|
29
30
|
"test:posture-blast": "npm run build && node scripts/test-posture-blast-radius.js",
|
|
30
31
|
"test:ping-e2e": "npm run build && node scripts/test-ping-e2e.js",
|
|
31
32
|
"prepublishOnly": "npm run build"
|