fullcourtdefense-cli 1.15.2 → 1.15.3
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/dist/commands/daemon.js +59 -31
- package/dist/config.js +22 -16
- package/dist/localSafetySnapshot.d.ts +1 -0
- package/dist/localSafetySnapshot.js +7 -0
- package/dist/version.json +1 -1
- package/package.json +1 -1
package/dist/commands/daemon.js
CHANGED
|
@@ -42,6 +42,7 @@ const child_process_1 = require("child_process");
|
|
|
42
42
|
const config_1 = require("../config");
|
|
43
43
|
const mcpGateway_1 = require("./mcpGateway");
|
|
44
44
|
const runtimeConfig_1 = require("../runtimeConfig");
|
|
45
|
+
const localSafetySnapshot_1 = require("../localSafetySnapshot");
|
|
45
46
|
const telemetry_1 = require("../telemetry");
|
|
46
47
|
const notify_1 = require("../notify");
|
|
47
48
|
const integrity_1 = require("../integrity");
|
|
@@ -288,7 +289,28 @@ async function runDaemon(args, config) {
|
|
|
288
289
|
throw new Error(resultSummary);
|
|
289
290
|
}
|
|
290
291
|
else if (action.type === 'policy_refresh') {
|
|
291
|
-
|
|
292
|
+
if (!creds.shieldId)
|
|
293
|
+
throw new Error('Shield not configured on this machine.');
|
|
294
|
+
const shieldId = creds.shieldId;
|
|
295
|
+
const identity = (0, machineIdentity_1.getMachineIdentity)();
|
|
296
|
+
const bundle = await (0, runtimeConfig_1.getRuntimeBundle)({
|
|
297
|
+
apiUrl: creds.apiUrl,
|
|
298
|
+
shieldId,
|
|
299
|
+
shieldKey: creds.shieldKey,
|
|
300
|
+
developerName: identity.developerName,
|
|
301
|
+
machineName: identity.hostname,
|
|
302
|
+
force: true,
|
|
303
|
+
ttlMs: 0,
|
|
304
|
+
});
|
|
305
|
+
(0, localSafetySnapshot_1.clearLocalSafetySnapshotCache)({
|
|
306
|
+
apiUrl: creds.apiUrl,
|
|
307
|
+
shieldId,
|
|
308
|
+
developerName: identity.developerName,
|
|
309
|
+
machineName: identity.hostname,
|
|
310
|
+
});
|
|
311
|
+
resultSummary = bundle.policyHash
|
|
312
|
+
? `Policy bundle refreshed (hash ${bundle.policyHash.slice(0, 12)}…).`
|
|
313
|
+
: 'Policy bundle refreshed from the control plane.';
|
|
292
314
|
}
|
|
293
315
|
else if (action.type === 'repair_protection') {
|
|
294
316
|
if (suspended)
|
|
@@ -301,15 +323,18 @@ async function runDaemon(args, config) {
|
|
|
301
323
|
resultSummary = 'AgentGuard hooks, gateways, and protection configuration were repaired and verified.';
|
|
302
324
|
}
|
|
303
325
|
else if (action.type === 'discovery_scan') {
|
|
304
|
-
|
|
326
|
+
// Full surface sweep (MCP + secrets + agent-files + posture) so a remote
|
|
327
|
+
// "Run discovery" refreshes BOTH the discovery and the posture scan
|
|
328
|
+
// timestamps in the dashboard — not just the MCP inventory.
|
|
329
|
+
const result = (0, child_process_1.spawnSync)(process.execPath, [cliEntry(), 'discover', '--upload', '--surface', 'all', '--silent'], {
|
|
305
330
|
encoding: 'utf8',
|
|
306
331
|
windowsHide: true,
|
|
307
|
-
timeout:
|
|
332
|
+
timeout: 300_000,
|
|
308
333
|
});
|
|
309
334
|
if (result.status !== 0) {
|
|
310
335
|
throw new Error((result.stderr || result.stdout || 'Discovery command failed').trim().slice(0, 500));
|
|
311
336
|
}
|
|
312
|
-
resultSummary = 'Discovery scan completed and uploaded.';
|
|
337
|
+
resultSummary = 'Discovery + posture scan completed and uploaded.';
|
|
313
338
|
}
|
|
314
339
|
await reportMachineAction(action.id, 'succeeded', { resultSummary });
|
|
315
340
|
log(`Remote action succeeded: ${action.type}.`);
|
|
@@ -323,11 +348,14 @@ async function runDaemon(args, config) {
|
|
|
323
348
|
const pollBundle = async () => {
|
|
324
349
|
if (!creds.shieldId)
|
|
325
350
|
return;
|
|
351
|
+
const identity = (0, machineIdentity_1.getMachineIdentity)();
|
|
326
352
|
try {
|
|
327
353
|
const bundle = await (0, runtimeConfig_1.getRuntimeBundle)({
|
|
328
354
|
apiUrl: creds.apiUrl,
|
|
329
355
|
shieldId: creds.shieldId,
|
|
330
356
|
shieldKey: creds.shieldKey,
|
|
357
|
+
developerName: identity.developerName,
|
|
358
|
+
machineName: identity.hostname,
|
|
331
359
|
force: true,
|
|
332
360
|
});
|
|
333
361
|
if (bundle.suspended && !suspended) {
|
|
@@ -485,23 +513,23 @@ function launchdPlistPath() {
|
|
|
485
513
|
return path.join(os.homedir(), 'Library', 'LaunchAgents', `${LAUNCHD_LABEL}.plist`);
|
|
486
514
|
}
|
|
487
515
|
function installMacos() {
|
|
488
|
-
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
489
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
490
|
-
<plist version="1.0">
|
|
491
|
-
<dict>
|
|
492
|
-
<key>Label</key><string>${LAUNCHD_LABEL}</string>
|
|
493
|
-
<key>ProgramArguments</key>
|
|
494
|
-
<array>
|
|
495
|
-
<string>${process.execPath}</string>
|
|
496
|
-
<string>${cliEntry()}</string>
|
|
497
|
-
<string>daemon</string>
|
|
498
|
-
</array>
|
|
499
|
-
<key>RunAtLoad</key><true/>
|
|
500
|
-
<key>KeepAlive</key><true/>
|
|
501
|
-
<key>StandardOutPath</key><string>${logFile()}</string>
|
|
502
|
-
<key>StandardErrorPath</key><string>${logFile()}</string>
|
|
503
|
-
</dict>
|
|
504
|
-
</plist>
|
|
516
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
517
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
518
|
+
<plist version="1.0">
|
|
519
|
+
<dict>
|
|
520
|
+
<key>Label</key><string>${LAUNCHD_LABEL}</string>
|
|
521
|
+
<key>ProgramArguments</key>
|
|
522
|
+
<array>
|
|
523
|
+
<string>${process.execPath}</string>
|
|
524
|
+
<string>${cliEntry()}</string>
|
|
525
|
+
<string>daemon</string>
|
|
526
|
+
</array>
|
|
527
|
+
<key>RunAtLoad</key><true/>
|
|
528
|
+
<key>KeepAlive</key><true/>
|
|
529
|
+
<key>StandardOutPath</key><string>${logFile()}</string>
|
|
530
|
+
<key>StandardErrorPath</key><string>${logFile()}</string>
|
|
531
|
+
</dict>
|
|
532
|
+
</plist>
|
|
505
533
|
`;
|
|
506
534
|
const file = launchdPlistPath();
|
|
507
535
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -539,16 +567,16 @@ function isDaemonAutostartInstalled() {
|
|
|
539
567
|
return fs.existsSync(systemdUnitPath());
|
|
540
568
|
}
|
|
541
569
|
function installLinux() {
|
|
542
|
-
const unit = `[Unit]
|
|
543
|
-
Description=FullCourtDefense resident daemon (config watch + heartbeat)
|
|
544
|
-
|
|
545
|
-
[Service]
|
|
546
|
-
ExecStart=${JSON.stringify(process.execPath)} ${JSON.stringify(cliEntry())} daemon
|
|
547
|
-
Restart=always
|
|
548
|
-
RestartSec=10
|
|
549
|
-
|
|
550
|
-
[Install]
|
|
551
|
-
WantedBy=default.target
|
|
570
|
+
const unit = `[Unit]
|
|
571
|
+
Description=FullCourtDefense resident daemon (config watch + heartbeat)
|
|
572
|
+
|
|
573
|
+
[Service]
|
|
574
|
+
ExecStart=${JSON.stringify(process.execPath)} ${JSON.stringify(cliEntry())} daemon
|
|
575
|
+
Restart=always
|
|
576
|
+
RestartSec=10
|
|
577
|
+
|
|
578
|
+
[Install]
|
|
579
|
+
WantedBy=default.target
|
|
552
580
|
`;
|
|
553
581
|
const file = systemdUnitPath();
|
|
554
582
|
try {
|
package/dist/config.js
CHANGED
|
@@ -157,23 +157,29 @@ function readConfigFile(filePath) {
|
|
|
157
157
|
function powershellDpapi(script, value) {
|
|
158
158
|
if (process.platform !== 'win32' || !value)
|
|
159
159
|
return undefined;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
160
|
+
// Windows PowerShell 5.1 first, then PowerShell 7 — both use DPAPI for
|
|
161
|
+
// SecureString on Windows. Some locked-down/service contexts (e.g. CI
|
|
162
|
+
// runners) break one shell but not the other.
|
|
163
|
+
for (const shell of ['powershell.exe', 'pwsh.exe']) {
|
|
164
|
+
try {
|
|
165
|
+
const output = (0, child_process_1.execFileSync)(shell, [
|
|
166
|
+
'-NoProfile',
|
|
167
|
+
'-NonInteractive',
|
|
168
|
+
'-ExecutionPolicy', 'Bypass',
|
|
169
|
+
'-Command', script,
|
|
170
|
+
], {
|
|
171
|
+
encoding: 'utf8',
|
|
172
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
173
|
+
env: { ...process.env, FCD_DPAPI_VALUE: value },
|
|
174
|
+
timeout: 15_000,
|
|
175
|
+
windowsHide: true,
|
|
176
|
+
}).trim();
|
|
177
|
+
if (output)
|
|
178
|
+
return output;
|
|
179
|
+
}
|
|
180
|
+
catch { /* try the next shell */ }
|
|
176
181
|
}
|
|
182
|
+
return undefined;
|
|
177
183
|
}
|
|
178
184
|
function protectShieldKeyForCurrentWindowsUser(value) {
|
|
179
185
|
return powershellDpapi('$secure=ConvertTo-SecureString -String $env:FCD_DPAPI_VALUE -AsPlainText -Force; ConvertFrom-SecureString -SecureString $secure', value);
|
|
@@ -14,5 +14,6 @@ export interface LocalSafetySnapshotInput {
|
|
|
14
14
|
expectedPolicyHash?: string;
|
|
15
15
|
timeoutMs?: number;
|
|
16
16
|
}
|
|
17
|
+
export declare function clearLocalSafetySnapshotCache(input: Pick<LocalSafetySnapshotInput, 'apiUrl' | 'shieldId' | 'developerName' | 'machineName'>): void;
|
|
17
18
|
export declare function loadLocalSafetySnapshot(input: LocalSafetySnapshotInput): Promise<LocalSafetySnapshot | undefined>;
|
|
18
19
|
export declare function snapshotToScanOptions(snapshot: LocalSafetySnapshot | undefined, extra?: Pick<LocalSafetyScanOptions, 'cwd' | 'inspectScripts'>): LocalSafetyScanOptions;
|
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.clearLocalSafetySnapshotCache = clearLocalSafetySnapshotCache;
|
|
36
37
|
exports.loadLocalSafetySnapshot = loadLocalSafetySnapshot;
|
|
37
38
|
exports.snapshotToScanOptions = snapshotToScanOptions;
|
|
38
39
|
const crypto = __importStar(require("crypto"));
|
|
@@ -86,6 +87,12 @@ function toSnapshot(data) {
|
|
|
86
87
|
updatedAt: typeof data.updatedAt === 'string' ? data.updatedAt : undefined,
|
|
87
88
|
};
|
|
88
89
|
}
|
|
90
|
+
function clearLocalSafetySnapshotCache(input) {
|
|
91
|
+
try {
|
|
92
|
+
fs.unlinkSync(cachePath(input));
|
|
93
|
+
}
|
|
94
|
+
catch { /* cache may not exist */ }
|
|
95
|
+
}
|
|
89
96
|
async function loadLocalSafetySnapshot(input) {
|
|
90
97
|
const file = cachePath(input);
|
|
91
98
|
const cached = readCache(file);
|
package/dist/version.json
CHANGED