fullcourtdefense-cli 1.21.18 → 1.21.19
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.
|
@@ -840,13 +840,13 @@ async function discoverCommand(args, config) {
|
|
|
840
840
|
const { extraScanRoots: adminScanRoots, disabledScanRoots } = (0, runtimeConfig_1.getCachedScanRootOverrides)();
|
|
841
841
|
const normalizedDisabledRoots = (0, discoverPaths_1.uniqueNormalizedPaths)(disabledScanRoots);
|
|
842
842
|
const isDisabledRoot = (root) => (0, discoverPaths_1.isExcludedPath)(root, normalizedDisabledRoots);
|
|
843
|
-
const scanRoots = (0, discoverPaths_1.
|
|
843
|
+
const scanRoots = (0, discoverPaths_1.compactScanRoots)([...(0, discoverPaths_1.resolveScanRoots)(args.scanRoot), ...adminScanRoots])
|
|
844
844
|
.filter(root => !isDisabledRoot(root));
|
|
845
845
|
const sweep = scanRoots.length > 0
|
|
846
846
|
? (0, discoverPaths_1.scanRootsForProjectConfigs)(scanRoots, { excludedRoots: normalizedDisabledRoots })
|
|
847
847
|
: { candidates: [], scannedDirs: 0, excludedDirs: 0, truncated: false, maxDepth: 4, maxDirs: 25000 };
|
|
848
848
|
// Admin roots are scanned for env/secrets even when no MCP config lives there.
|
|
849
|
-
const sweepProjectRoots = (0, discoverPaths_1.
|
|
849
|
+
const sweepProjectRoots = (0, discoverPaths_1.compactScanRoots)([...deriveProjectRoots(sweep.candidates), ...adminScanRoots])
|
|
850
850
|
.filter(root => !isDisabledRoot(root));
|
|
851
851
|
const knownConfigCandidates = candidateConfigPaths(cwd, args.extraPath)
|
|
852
852
|
.filter(candidate => !isDisabledRoot(candidate.path));
|
|
@@ -914,17 +914,19 @@ async function discoverCommand(args, config) {
|
|
|
914
914
|
const home = os.homedir();
|
|
915
915
|
// Effective roots the posture scan covered — proof of coverage for the console.
|
|
916
916
|
// Admin-removed defaults are excluded here too, so the console mirrors reality.
|
|
917
|
-
const defaultEnvRoots = [home, cwd, path.join(home, 'dev'), path.join(home, 'repos'), path.join(home, 'projects'), path.join(home, 'Documents'), path.join(home, 'code')]
|
|
918
|
-
.map(root => path.resolve(root));
|
|
917
|
+
const defaultEnvRoots = (0, discoverPaths_1.compactScanRoots)([home, cwd, path.join(home, 'dev'), path.join(home, 'repos'), path.join(home, 'projects'), path.join(home, 'Documents'), path.join(home, 'code')]
|
|
918
|
+
.map(root => path.resolve(root)));
|
|
919
919
|
const adminRootSet = new Set(adminScanRoots.map(root => path.resolve(root).toLowerCase()));
|
|
920
|
-
const
|
|
920
|
+
const scannedRootCandidates = [
|
|
921
921
|
...[...new Set(defaultEnvRoots)].filter(root => fs.existsSync(root) && !isDisabledRoot(root)).map(root => ({ path: root, source: 'default' })),
|
|
922
922
|
...adminScanRoots.map(root => path.resolve(root)).filter(root => fs.existsSync(root)).map(root => ({ path: root, source: 'admin' })),
|
|
923
923
|
...(0, discoverPaths_1.resolveScanRoots)(args.scanRoot)
|
|
924
924
|
.map(root => path.resolve(root))
|
|
925
925
|
.filter(root => fs.existsSync(root) && !adminRootSet.has(root.toLowerCase()) && !isDisabledRoot(root))
|
|
926
926
|
.map(root => ({ path: root, source: 'default' })),
|
|
927
|
-
]
|
|
927
|
+
];
|
|
928
|
+
const compactedScannedPaths = (0, discoverPaths_1.compactScanRoots)(scannedRootCandidates.map(entry => entry.path));
|
|
929
|
+
const scannedRoots = compactedScannedPaths.map(root => scannedRootCandidates.find(entry => path.resolve(entry.path).toLowerCase() === path.resolve(root).toLowerCase()));
|
|
928
930
|
// Shallow folder tree for the console folder picker (names only, no contents).
|
|
929
931
|
const folderCatalog = surfaces.has('posture') || surfaces.has('secrets')
|
|
930
932
|
? (0, discoverPaths_1.buildFolderCatalog)({ extraRoots: adminScanRoots })
|
|
@@ -14,6 +14,11 @@ export declare function normalizedPath(value: string): string;
|
|
|
14
14
|
export declare function pathIsWithin(candidate: string, root: string): boolean;
|
|
15
15
|
export declare function isExcludedPath(candidate: string, excludedRoots?: string[]): boolean;
|
|
16
16
|
export declare function uniqueNormalizedPaths(values: string[]): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Keep only the shallowest non-overlapping scan roots. If C:\dev is present,
|
|
19
|
+
* C:\dev\repo is redundant: the bounded parent walk already covers it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function compactScanRoots(values: string[]): string[];
|
|
17
22
|
export declare function claudeDesktopMsixCandidates(): ClaudeDesktopConfigCandidate[];
|
|
18
23
|
export declare function claudeDesktopConfigCandidates(): ClaudeDesktopConfigCandidate[];
|
|
19
24
|
export declare function claudeDesktopLikelyInstalled(): boolean;
|
|
@@ -37,6 +37,7 @@ exports.normalizedPath = normalizedPath;
|
|
|
37
37
|
exports.pathIsWithin = pathIsWithin;
|
|
38
38
|
exports.isExcludedPath = isExcludedPath;
|
|
39
39
|
exports.uniqueNormalizedPaths = uniqueNormalizedPaths;
|
|
40
|
+
exports.compactScanRoots = compactScanRoots;
|
|
40
41
|
exports.claudeDesktopMsixCandidates = claudeDesktopMsixCandidates;
|
|
41
42
|
exports.claudeDesktopConfigCandidates = claudeDesktopConfigCandidates;
|
|
42
43
|
exports.claudeDesktopLikelyInstalled = claudeDesktopLikelyInstalled;
|
|
@@ -75,6 +76,22 @@ function uniqueNormalizedPaths(values) {
|
|
|
75
76
|
}
|
|
76
77
|
return [...out.values()];
|
|
77
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Keep only the shallowest non-overlapping scan roots. If C:\dev is present,
|
|
81
|
+
* C:\dev\repo is redundant: the bounded parent walk already covers it.
|
|
82
|
+
*/
|
|
83
|
+
function compactScanRoots(values) {
|
|
84
|
+
const roots = uniqueNormalizedPaths(values)
|
|
85
|
+
.sort((a, b) => normalizedPath(a).split(path.sep).length - normalizedPath(b).split(path.sep).length
|
|
86
|
+
|| normalizedPath(a).length - normalizedPath(b).length);
|
|
87
|
+
const compacted = [];
|
|
88
|
+
for (const root of roots) {
|
|
89
|
+
if (compacted.some(parent => pathIsWithin(root, parent)))
|
|
90
|
+
continue;
|
|
91
|
+
compacted.push(root);
|
|
92
|
+
}
|
|
93
|
+
return compacted;
|
|
94
|
+
}
|
|
78
95
|
function claudeDesktopDocumentedPath() {
|
|
79
96
|
const home = os.homedir();
|
|
80
97
|
if (process.platform === 'win32') {
|
|
@@ -269,7 +286,7 @@ function scanRootsForProjectConfigs(roots, options = {}) {
|
|
|
269
286
|
let excludedDirs = 0;
|
|
270
287
|
let depthLimited = false;
|
|
271
288
|
const queue = [];
|
|
272
|
-
for (const root of
|
|
289
|
+
for (const root of compactScanRoots(roots)) {
|
|
273
290
|
try {
|
|
274
291
|
const resolved = path.resolve(root);
|
|
275
292
|
if (!isExcludedPath(resolved, options.excludedRoots) && fs.statSync(resolved).isDirectory())
|
package/dist/version.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fullcourtdefense-cli",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.19",
|
|
4
4
|
"description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"test:machine-action-signing": "npm run build && node scripts/test-machine-action-signing.js",
|
|
36
36
|
"test:integrity": "npm run build && node scripts/test-integrity.js",
|
|
37
37
|
"test:posture-overhaul": "npm run build && node scripts/test-posture-overhaul.js",
|
|
38
|
+
"test:scan-root-compaction": "npm run build && node scripts/test-scan-root-compaction.js",
|
|
38
39
|
"test:dpapi-config": "npm run build && node scripts/test-dpapi-config.js",
|
|
39
40
|
"test:desktop-chat-guard": "npm run build && node scripts/test-desktop-chat-guard.js",
|
|
40
41
|
"test:clipboard-scan": "npm run build && node scripts/test-clipboard-scan.js",
|