fullcourtdefense-cli 1.21.18 → 1.21.20
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.
|
@@ -7,6 +7,8 @@ export interface DaemonArgs extends ProtectAllArgs {
|
|
|
7
7
|
/** Suppress OS toasts (still logs). */
|
|
8
8
|
quiet?: string;
|
|
9
9
|
}
|
|
10
|
+
/** Arguments shared by first-boot, stale catch-up, and web-triggered scans. */
|
|
11
|
+
export declare function daemonDiscoverSweepArgs(): string[];
|
|
10
12
|
/** Whether the daemon has been registered to start automatically. */
|
|
11
13
|
export declare function isDaemonAutostartInstalled(): boolean;
|
|
12
14
|
export declare function daemonCommand(args: DaemonArgs, config: BotGuardConfig): Promise<void>;
|
package/dist/commands/daemon.js
CHANGED
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.daemonDiscoverSweepArgs = daemonDiscoverSweepArgs;
|
|
36
37
|
exports.isDaemonAutostartInstalled = isDaemonAutostartInstalled;
|
|
37
38
|
exports.daemonCommand = daemonCommand;
|
|
38
39
|
const fs = __importStar(require("fs"));
|
|
@@ -142,12 +143,19 @@ function stopPid(pid) {
|
|
|
142
143
|
function logFile() {
|
|
143
144
|
return path.join(daemonDir(), 'daemon.log');
|
|
144
145
|
}
|
|
145
|
-
/**
|
|
146
|
+
/** Arguments shared by first-boot, stale catch-up, and web-triggered scans. */
|
|
147
|
+
function daemonDiscoverSweepArgs() {
|
|
148
|
+
return ['discover', '--upload', '--surface', 'all', '--deep', 'true', '--silent', '--scan-root', 'auto'];
|
|
149
|
+
}
|
|
150
|
+
/** Spawn a full known-client discovery + posture sweep and wait for it to finish. */
|
|
146
151
|
function runDiscoverSweep(timeoutMs = 300_000) {
|
|
147
152
|
return new Promise((resolve, reject) => {
|
|
148
153
|
// --scan-root auto sweeps the default dev roots; admin-selected folders from
|
|
149
154
|
// the console are merged in by discover itself (cached runtime bundle).
|
|
150
|
-
|
|
155
|
+
// --deep inventories tools from known configured clients (Claude Desktop,
|
|
156
|
+
// Cursor, etc.). We intentionally do NOT pass --deep-roots, so arbitrary
|
|
157
|
+
// stdio servers merely found in other project trees are never auto-spawned.
|
|
158
|
+
const child = (0, child_process_1.spawn)(process.execPath, [cliEntry(), ...daemonDiscoverSweepArgs()], {
|
|
151
159
|
windowsHide: true,
|
|
152
160
|
stdio: 'ignore',
|
|
153
161
|
// Daemon/scheduled-task cwd is C:\WINDOWS\system32 — scan from the user's
|
|
@@ -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.20",
|
|
4
4
|
"description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -32,9 +32,11 @@
|
|
|
32
32
|
"test:posture-blast": "npm run build && node scripts/test-posture-blast-radius.js",
|
|
33
33
|
"test:ping-e2e": "npm run build && node scripts/test-ping-e2e.js",
|
|
34
34
|
"test:daemon-e2e": "npm run build && node scripts/test-daemon-e2e.js",
|
|
35
|
+
"test:daemon-discovery-args": "npm run build && node scripts/test-daemon-discovery-args.js",
|
|
35
36
|
"test:machine-action-signing": "npm run build && node scripts/test-machine-action-signing.js",
|
|
36
37
|
"test:integrity": "npm run build && node scripts/test-integrity.js",
|
|
37
38
|
"test:posture-overhaul": "npm run build && node scripts/test-posture-overhaul.js",
|
|
39
|
+
"test:scan-root-compaction": "npm run build && node scripts/test-scan-root-compaction.js",
|
|
38
40
|
"test:dpapi-config": "npm run build && node scripts/test-dpapi-config.js",
|
|
39
41
|
"test:desktop-chat-guard": "npm run build && node scripts/test-desktop-chat-guard.js",
|
|
40
42
|
"test:clipboard-scan": "npm run build && node scripts/test-clipboard-scan.js",
|