fullcourtdefense-cli 1.15.9 → 1.15.10

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.
@@ -824,9 +824,13 @@ async function discoverCommand(args, config) {
824
824
  // and behavior is exactly the known-locations scan.
825
825
  // Admin-chosen folders (picked in the console per machine, delivered via the
826
826
  // runtime bundle) are always merged in, so every scheduled/daemon/manual scan
827
- // covers them without extra flags.
828
- const adminScanRoots = (0, runtimeConfig_1.getCachedExtraScanRoots)();
829
- const scanRoots = [...new Set([...(0, discoverPaths_1.resolveScanRoots)(args.scanRoot), ...adminScanRoots])];
827
+ // covers them without extra flags. Default folders the admin explicitly
828
+ // removed in the console are dropped from the sweep.
829
+ const { extraScanRoots: adminScanRoots, disabledScanRoots } = (0, runtimeConfig_1.getCachedScanRootOverrides)();
830
+ const disabledRootSet = new Set(disabledScanRoots.map(root => path.resolve(root).toLowerCase()));
831
+ const isDisabledRoot = (root) => disabledRootSet.has(path.resolve(root).toLowerCase());
832
+ const scanRoots = [...new Set([...(0, discoverPaths_1.resolveScanRoots)(args.scanRoot), ...adminScanRoots])]
833
+ .filter(root => !isDisabledRoot(root));
830
834
  const sweep = scanRoots.length > 0 ? (0, discoverPaths_1.scanRootsForProjectConfigs)(scanRoots) : { candidates: [], scannedDirs: 0 };
831
835
  // Admin roots are scanned for env/secrets even when no MCP config lives there.
832
836
  const sweepProjectRoots = [...new Set([...deriveProjectRoots(sweep.candidates), ...adminScanRoots])];
@@ -877,7 +881,7 @@ async function discoverCommand(args, config) {
877
881
  }
878
882
  }
879
883
  const secrets = surfaces.has('secrets') || surfaces.has('posture')
880
- ? (0, discoverSecrets_1.scanSecrets)({ cwd, extraRoots: sweepProjectRoots, configFiles: secretConfigFiles })
884
+ ? (0, discoverSecrets_1.scanSecrets)({ cwd, extraRoots: sweepProjectRoots, disabledRoots: disabledScanRoots, configFiles: secretConfigFiles })
881
885
  : undefined;
882
886
  const agentFiles = surfaces.has('agent-files') || surfaces.has('posture')
883
887
  ? (0, discoverAgentFiles_1.scanAgentFiles)({ cwd, extraRoots: sweepProjectRoots })
@@ -893,15 +897,16 @@ async function discoverCommand(args, config) {
893
897
  const host = buildHostMetadata(args.userEmail, deep && found.some(s => s.probeMode === 'deep') ? 'deep' : 'config');
894
898
  const home = os.homedir();
895
899
  // Effective roots the posture scan covered — proof of coverage for the console.
900
+ // Admin-removed defaults are excluded here too, so the console mirrors reality.
896
901
  const defaultEnvRoots = [home, cwd, path.join(home, 'dev'), path.join(home, 'repos'), path.join(home, 'projects'), path.join(home, 'Documents'), path.join(home, 'code')]
897
902
  .map(root => path.resolve(root));
898
903
  const adminRootSet = new Set(adminScanRoots.map(root => path.resolve(root).toLowerCase()));
899
904
  const scannedRoots = [
900
- ...[...new Set(defaultEnvRoots)].filter(root => fs.existsSync(root)).map(root => ({ path: root, source: 'default' })),
905
+ ...[...new Set(defaultEnvRoots)].filter(root => fs.existsSync(root) && !isDisabledRoot(root)).map(root => ({ path: root, source: 'default' })),
901
906
  ...adminScanRoots.map(root => path.resolve(root)).filter(root => fs.existsSync(root)).map(root => ({ path: root, source: 'admin' })),
902
907
  ...(0, discoverPaths_1.resolveScanRoots)(args.scanRoot)
903
908
  .map(root => path.resolve(root))
904
- .filter(root => fs.existsSync(root) && !adminRootSet.has(root.toLowerCase()))
909
+ .filter(root => fs.existsSync(root) && !adminRootSet.has(root.toLowerCase()) && !isDisabledRoot(root))
905
910
  .map(root => ({ path: root, source: 'default' })),
906
911
  ].filter((entry, index, list) => list.findIndex(other => other.path.toLowerCase() === entry.path.toLowerCase()) === index);
907
912
  // Shallow folder tree for the console folder picker (names only, no contents).
@@ -22,5 +22,6 @@ export declare function scanSecrets(options?: {
22
22
  cwd?: string;
23
23
  maxEnvDepth?: number;
24
24
  extraRoots?: string[];
25
+ disabledRoots?: string[];
25
26
  configFiles?: string[];
26
27
  }): SecretsScanResult;
@@ -478,7 +478,8 @@ function scanSecrets(options = {}) {
478
478
  const home = os.homedir();
479
479
  const cwd = options.cwd || process.cwd();
480
480
  const maxDepth = options.maxEnvDepth ?? 4;
481
- const extraRoots = options.extraRoots ?? [];
481
+ const disabledRoots = new Set((options.disabledRoots ?? []).map(root => path.resolve(root).toLowerCase()));
482
+ const extraRoots = (options.extraRoots ?? []).filter(root => !disabledRoots.has(path.resolve(root).toLowerCase()));
482
483
  const findings = [];
483
484
  const seen = new Set();
484
485
  let scannedFiles = 0;
@@ -513,7 +514,7 @@ function scanSecrets(options = {}) {
513
514
  path.join(home, 'projects'),
514
515
  path.join(home, 'Documents'),
515
516
  path.join(home, 'code'),
516
- ].filter(root => fs.existsSync(root))));
517
+ ].filter(root => fs.existsSync(root) && !disabledRoots.has(path.resolve(root).toLowerCase()))));
517
518
  for (const envFile of collectEnvFiles(envRoots, maxDepth)) {
518
519
  if (findings.length >= MAX_FINDINGS)
519
520
  break;
@@ -26,6 +26,8 @@ export interface RuntimeBundle {
26
26
  suspended?: boolean;
27
27
  /** Admin-chosen extra posture scan folders for this machine (from the console). */
28
28
  extraScanRoots?: string[];
29
+ /** Default scan folders the admin explicitly removed from this machine's posture sweep. */
30
+ disabledScanRoots?: string[];
29
31
  /** One constrained, auditable action queued for the resident daemon. */
30
32
  machineAction?: {
31
33
  id: string;
@@ -58,8 +60,14 @@ export interface EffectiveBundle extends RuntimeBundle {
58
60
  */
59
61
  export declare function getRuntimeBundle(input: FetchBundleInput): Promise<EffectiveBundle>;
60
62
  /**
61
- * Admin-chosen extra posture scan folders from the most recent cached bundle
62
- * (any shield). Read-only and offline used by `discover` so scheduled/daemon
63
- * scans include console-selected folders without needing credentials plumbed in.
63
+ * Admin scan-folder choices from the most recent cached bundle (any shield):
64
+ * extra folders to add and default folders the admin removed. Read-only and
65
+ * offline used by `discover` so scheduled/daemon scans honor the console
66
+ * selection without needing credentials plumbed in.
64
67
  */
68
+ export declare function getCachedScanRootOverrides(): {
69
+ extraScanRoots: string[];
70
+ disabledScanRoots: string[];
71
+ };
72
+ /** @deprecated Use getCachedScanRootOverrides(). */
65
73
  export declare function getCachedExtraScanRoots(): string[];
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.getRuntimeBundle = getRuntimeBundle;
37
+ exports.getCachedScanRootOverrides = getCachedScanRootOverrides;
37
38
  exports.getCachedExtraScanRoots = getCachedExtraScanRoots;
38
39
  const fs = __importStar(require("fs"));
39
40
  const os = __importStar(require("os"));
@@ -107,6 +108,9 @@ async function getRuntimeBundle(input) {
107
108
  extraScanRoots: Array.isArray(body.data.extraScanRoots)
108
109
  ? body.data.extraScanRoots.filter((root) => typeof root === 'string').slice(0, 20)
109
110
  : undefined,
111
+ disabledScanRoots: Array.isArray(body.data.disabledScanRoots)
112
+ ? body.data.disabledScanRoots.filter((root) => typeof root === 'string').slice(0, 20)
113
+ : undefined,
110
114
  machineAction: body.data.machineAction,
111
115
  fetchedAt: Date.now(),
112
116
  };
@@ -123,18 +127,28 @@ async function getRuntimeBundle(input) {
123
127
  return { mode: 'block', version: '', source: 'default' };
124
128
  }
125
129
  /**
126
- * Admin-chosen extra posture scan folders from the most recent cached bundle
127
- * (any shield). Read-only and offline used by `discover` so scheduled/daemon
128
- * scans include console-selected folders without needing credentials plumbed in.
130
+ * Admin scan-folder choices from the most recent cached bundle (any shield):
131
+ * extra folders to add and default folders the admin removed. Read-only and
132
+ * offline used by `discover` so scheduled/daemon scans honor the console
133
+ * selection without needing credentials plumbed in.
129
134
  */
130
- function getCachedExtraScanRoots() {
135
+ function getCachedScanRootOverrides() {
131
136
  const cache = readCacheFile();
132
- const roots = new Set();
137
+ const extra = new Set();
138
+ const disabled = new Set();
133
139
  for (const entry of Object.values(cache)) {
134
140
  for (const root of entry.extraScanRoots || []) {
135
141
  if (typeof root === 'string' && root.trim())
136
- roots.add(root.trim());
142
+ extra.add(root.trim());
143
+ }
144
+ for (const root of entry.disabledScanRoots || []) {
145
+ if (typeof root === 'string' && root.trim())
146
+ disabled.add(root.trim());
137
147
  }
138
148
  }
139
- return [...roots];
149
+ return { extraScanRoots: [...extra], disabledScanRoots: [...disabled] };
150
+ }
151
+ /** @deprecated Use getCachedScanRootOverrides(). */
152
+ function getCachedExtraScanRoots() {
153
+ return getCachedScanRootOverrides().extraScanRoots;
140
154
  }
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.15.9"
2
+ "version": "1.15.10"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.15.9",
3
+ "version": "1.15.10",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {