fullcourtdefense-cli 1.34.21 → 1.34.25
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/README.md +1 -0
- package/dist/actionIdentity.d.ts +1 -0
- package/dist/actionIdentity.js +2 -0
- package/dist/actionPolicyEngine.d.ts +5 -0
- package/dist/actionPolicyEngine.js +273 -30
- package/dist/agentTerminal.d.ts +1 -3
- package/dist/agentTerminal.js +14 -29
- package/dist/approvalPrompt.d.ts +7 -0
- package/dist/approvalPrompt.js +46 -0
- package/dist/askDialog.d.ts +87 -0
- package/dist/askDialog.js +209 -0
- package/dist/blockExplanation.js +18 -6
- package/dist/boundaryVerification.d.ts +20 -0
- package/dist/boundaryVerification.js +147 -0
- package/dist/commands/daemon.js +198 -70
- package/dist/commands/deterministicGuard.d.ts +2 -0
- package/dist/commands/deterministicGuard.js +161 -9
- package/dist/commands/hook.js +81 -29
- package/dist/commands/installClaudeHook.js +11 -5
- package/dist/commands/installCursorHook.js +5 -4
- package/dist/commands/mcpGateway.d.ts +2 -0
- package/dist/commands/mcpGateway.js +163 -56
- package/dist/commands/onboard.js +6 -5
- package/dist/commands/onboardingSummary.d.ts +5 -0
- package/dist/commands/onboardingSummary.js +11 -0
- package/dist/commands/protectedRun.d.ts +14 -0
- package/dist/commands/protectedRun.js +207 -0
- package/dist/containment-runtime/index.mjs +30118 -0
- package/dist/containment-runtime/licenses/_anthropic-ai_sandbox-runtime.txt +201 -0
- package/dist/containment-runtime/licenses/_pondwader_socks5-server.txt +20 -0
- package/dist/containment-runtime/licenses/commander.txt +22 -0
- package/dist/containment-runtime/licenses/node-forge.txt +331 -0
- package/dist/containment-runtime/licenses/zod.txt +21 -0
- package/dist/containment-runtime/runtime-version.json +1 -0
- package/dist/containment-runtime/vendor/java-proxy-agent/build.ts +95 -0
- package/dist/containment-runtime/vendor/java-proxy-agent/srt-proxy-agent.jar +0 -0
- package/dist/containment-runtime/vendor/seccomp/arm64/apply-seccomp +0 -0
- package/dist/containment-runtime/vendor/seccomp/build.ts +69 -0
- package/dist/containment-runtime/vendor/seccomp/x64/apply-seccomp +0 -0
- package/dist/containment-runtime/vendor/srt-win/arm64/srt-win.exe +0 -0
- package/dist/containment-runtime/vendor/srt-win/build.ts +21 -0
- package/dist/containment-runtime/vendor/srt-win/x64/srt-win.exe +0 -0
- package/dist/containmentLease.d.ts +5 -0
- package/dist/containmentLease.js +72 -0
- package/dist/detectionUpdates.d.ts +23 -3
- package/dist/detectionUpdates.js +45 -14
- package/dist/detectorArtifact.d.ts +17 -0
- package/dist/detectorArtifact.js +35 -0
- package/dist/detectorBaseline.d.ts +2 -0
- package/dist/detectorBaseline.js +5 -0
- package/dist/detectorModule.d.ts +9 -0
- package/dist/detectorModule.js +84 -0
- package/dist/detectorRuntime.d.ts +21 -0
- package/dist/detectorRuntime.js +49 -0
- package/dist/detectorUpdates.d.ts +21 -0
- package/dist/detectorUpdates.js +86 -0
- package/dist/devConfirm.d.ts +97 -15
- package/dist/devConfirm.js +447 -117
- package/dist/distress.d.ts +2 -0
- package/dist/distress.js +2 -0
- package/dist/frictionRollout.js +103 -0
- package/dist/gatewayRuntime.d.ts +19 -0
- package/dist/gatewayRuntime.js +93 -0
- package/dist/hookSlim.js +17 -18
- package/dist/index.js +35 -0
- package/dist/localDetectionUpdates.d.ts +3 -1
- package/dist/localDetectionUpdates.js +8 -1
- package/dist/localSafetySnapshot.d.ts +9 -5
- package/dist/localSafetySnapshot.js +42 -15
- package/dist/mcpToolReview.d.ts +17 -0
- package/dist/mcpToolReview.js +127 -0
- package/dist/mcpToolTrust.d.ts +22 -0
- package/dist/mcpToolTrust.js +66 -0
- package/dist/nativeHook.d.ts +4 -0
- package/dist/nativeHook.js +31 -17
- package/dist/policyRefresh.d.ts +3 -0
- package/dist/policyRefresh.js +27 -0
- package/dist/protectionMaintenance.d.ts +10 -0
- package/dist/protectionMaintenance.js +64 -0
- package/dist/protectionProfile.d.ts +26 -0
- package/dist/protectionProfile.js +63 -0
- package/dist/runtimeConfig.d.ts +2 -0
- package/dist/runtimeConfig.js +65 -26
- package/dist/selfTest.d.ts +2 -0
- package/dist/selfTest.js +9 -1
- package/dist/sessionLimits.d.ts +2 -0
- package/dist/sessionLimits.js +24 -0
- package/dist/telemetry.d.ts +5 -0
- package/dist/telemetry.js +8 -0
- package/dist/version.json +1 -1
- package/package.json +10 -6
package/dist/runtimeConfig.js
CHANGED
|
@@ -71,9 +71,13 @@ function readCacheFile() {
|
|
|
71
71
|
}
|
|
72
72
|
function writeCacheFile(data) {
|
|
73
73
|
try {
|
|
74
|
-
|
|
74
|
+
// Keep the last complete policy set if writing the replacement is interrupted.
|
|
75
|
+
writeFileAtomic(CACHE_PATH, JSON.stringify(data), 0o600);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
75
80
|
}
|
|
76
|
-
catch { /* cache is best-effort */ }
|
|
77
81
|
}
|
|
78
82
|
function isMode(value) {
|
|
79
83
|
return value === 'block' || value === 'monitor' || value === 'shadow';
|
|
@@ -185,31 +189,37 @@ function cachedToEffective(cached) {
|
|
|
185
189
|
return { ...bundle, suspended, source: 'cache', serverValidatedAt: fetchedAt };
|
|
186
190
|
}
|
|
187
191
|
/** Keep only a well-formed machine-role block (limits must be numbers or null; legacy keys normalized). */
|
|
188
|
-
function sanitizeMachineRole(value) {
|
|
189
|
-
if (
|
|
190
|
-
return undefined;
|
|
192
|
+
function sanitizeMachineRole(value, previous) {
|
|
193
|
+
if (value === undefined && !previous)
|
|
194
|
+
return undefined; // legacy server compatibility
|
|
195
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
196
|
+
throw new Error('Invalid or missing machine role');
|
|
191
197
|
const role = value;
|
|
192
|
-
if (typeof role.roleId !== 'string' || typeof role.name !== 'string'
|
|
193
|
-
|
|
194
|
-
|
|
198
|
+
if (typeof role.roleId !== 'string' || !role.roleId || typeof role.name !== 'string'
|
|
199
|
+
|| (role.stage !== undefined && role.stage !== 'monitor' && role.stage !== 'enforce')) {
|
|
200
|
+
throw new Error('Invalid machine role configuration');
|
|
201
|
+
}
|
|
195
202
|
return {
|
|
196
203
|
roleId: role.roleId,
|
|
197
204
|
name: role.name,
|
|
198
205
|
summary: typeof role.summary === 'string' ? role.summary : undefined,
|
|
199
206
|
stage: role.stage === 'monitor' ? 'monitor' : 'enforce',
|
|
200
|
-
limits: (0, sessionLimits_1.
|
|
207
|
+
limits: (0, sessionLimits_1.parseDeliveredSessionLimits)(role.limits, previous?.limits),
|
|
201
208
|
};
|
|
202
209
|
}
|
|
203
|
-
/**
|
|
210
|
+
/** Reject incomplete policy sets before any cache or suspension marker is changed. */
|
|
204
211
|
function sanitizeBundlePolicies(value) {
|
|
205
|
-
if (
|
|
206
|
-
return undefined;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
212
|
+
if (value === undefined)
|
|
213
|
+
return undefined; // pre-action-policy server compatibility
|
|
214
|
+
if (!Array.isArray(value) || value.length > 300 || value.some(policy => !policy || typeof policy !== 'object' || typeof policy.id !== 'string' || !policy.id
|
|
215
|
+
|| typeof policy.name !== 'string' || !Array.isArray(policy.rules)
|
|
216
|
+
|| (policy.enforcement !== undefined && !['standard', 'hard'].includes(policy.enforcement))
|
|
217
|
+
|| policy.rules.some((rule) => !rule || !Array.isArray(rule.operations)
|
|
218
|
+
|| !rule.operations.length || rule.operations.some((op) => typeof op !== 'string' || !op)
|
|
219
|
+
|| !['allow', 'block', 'require_approval', 'log'].includes(rule.verdict)))) {
|
|
220
|
+
throw new Error('Invalid or incomplete action policy bundle');
|
|
221
|
+
}
|
|
222
|
+
return value;
|
|
213
223
|
}
|
|
214
224
|
/**
|
|
215
225
|
* Resolve the effective runtime bundle for a Shield, using the local cache when
|
|
@@ -248,6 +258,8 @@ async function getRuntimeBundle(input) {
|
|
|
248
258
|
}
|
|
249
259
|
catch { /* best-effort */ }
|
|
250
260
|
}
|
|
261
|
+
let responseReceived = false;
|
|
262
|
+
let cacheWriteFailed = false;
|
|
251
263
|
try {
|
|
252
264
|
const headers = { 'Content-Type': 'application/json' };
|
|
253
265
|
if (input.shieldKey)
|
|
@@ -262,6 +274,7 @@ async function getRuntimeBundle(input) {
|
|
|
262
274
|
if (input.machineId)
|
|
263
275
|
params.set('machineId', input.machineId);
|
|
264
276
|
const resp = await fetch(`${input.apiUrl}/api/cli/bundle?${params.toString()}`, { method: 'GET', headers, signal: AbortSignal.timeout(input.timeoutMs ?? REFRESH_TIMEOUT_MS) });
|
|
277
|
+
responseReceived = true;
|
|
265
278
|
// 304 Not Modified — cache is still valid; refresh its timestamp.
|
|
266
279
|
// Re-assert the marker when the cache says suspended, but NEVER clear it
|
|
267
280
|
// here: a 304 validates the version string, not the (locally editable)
|
|
@@ -270,9 +283,13 @@ async function getRuntimeBundle(input) {
|
|
|
270
283
|
if (resp.status === 304 && cached) {
|
|
271
284
|
const validated = { ...cached, fetchedAt: Date.now() };
|
|
272
285
|
cache[input.shieldId] = validated;
|
|
273
|
-
writeCacheFile(cache);
|
|
286
|
+
const persisted = writeCacheFile(cache);
|
|
274
287
|
if (cached.suspended === true)
|
|
275
288
|
assertSuspendMarker(input.shieldId);
|
|
289
|
+
if (!persisted && input.requireServerValidation) {
|
|
290
|
+
cacheWriteFailed = true;
|
|
291
|
+
throw new Error('Runtime policy cache could not be saved');
|
|
292
|
+
}
|
|
276
293
|
return cachedToEffective(validated);
|
|
277
294
|
}
|
|
278
295
|
if (resp.status === 401 || resp.status === 403) {
|
|
@@ -280,9 +297,23 @@ async function getRuntimeBundle(input) {
|
|
|
280
297
|
// not a network one. The two need opposite fixes; code them apart.
|
|
281
298
|
(0, distress_1.reportDistress)('bundle', distress_1.DISTRESS.AUTH_BROKEN, `bundle fetch HTTP ${resp.status}${input.shieldKey ? '' : ' (no shield key available)'}`);
|
|
282
299
|
}
|
|
300
|
+
else if (!resp.ok) {
|
|
301
|
+
(0, distress_1.reportDistress)('bundle', distress_1.DISTRESS.BACKEND_REJECTED, `bundle fetch HTTP ${resp.status}`);
|
|
302
|
+
}
|
|
283
303
|
if (resp.ok) {
|
|
284
304
|
const body = await resp.json().catch(() => ({}));
|
|
285
|
-
if (body.success && body.data && isMode(body.data.mode)) {
|
|
305
|
+
if (body.success === true && body.data && isMode(body.data.mode) && typeof body.data.version === 'string' && body.data.version.trim()) {
|
|
306
|
+
if (cached?.actionPolicies !== undefined && body.data.actionPolicies === undefined) {
|
|
307
|
+
throw new Error('Action policies missing from a previously supported bundle');
|
|
308
|
+
}
|
|
309
|
+
if ((body.data.suspended !== undefined && typeof body.data.suspended !== 'boolean')
|
|
310
|
+
|| (body.data.suspended === undefined && (cached?.suspended === true || hasValidSuspendMarker()))) {
|
|
311
|
+
throw new Error('Invalid or missing suspension status');
|
|
312
|
+
}
|
|
313
|
+
if ((body.data.failClosed !== undefined && typeof body.data.failClosed !== 'boolean')
|
|
314
|
+
|| (cached?.failClosed !== undefined && body.data.failClosed === undefined)) {
|
|
315
|
+
throw new Error('Invalid or missing offline enforcement setting');
|
|
316
|
+
}
|
|
286
317
|
const entry = {
|
|
287
318
|
mode: body.data.mode,
|
|
288
319
|
version: body.data.version,
|
|
@@ -307,27 +338,35 @@ async function getRuntimeBundle(input) {
|
|
|
307
338
|
? { enabled: body.data.honeypot.enabled === true }
|
|
308
339
|
: undefined,
|
|
309
340
|
machineAction: body.data.machineAction,
|
|
341
|
+
mcpToolTrust: body.data.mcpToolTrust,
|
|
342
|
+
protectionProfile: body.data.protectionProfile,
|
|
310
343
|
actionPolicies: sanitizeBundlePolicies(body.data.actionPolicies),
|
|
311
|
-
machineRole: sanitizeMachineRole(body.data.machineRole),
|
|
344
|
+
machineRole: sanitizeMachineRole(body.data.machineRole, cached?.machineRole),
|
|
312
345
|
fetchedAt: Date.now(),
|
|
313
346
|
};
|
|
314
347
|
cache[input.shieldId] = entry;
|
|
315
|
-
writeCacheFile(cache);
|
|
348
|
+
const persisted = writeCacheFile(cache);
|
|
316
349
|
// Sync the tamper-resistant marker from the ONLY trustworthy source —
|
|
317
350
|
// a live server response. Suspended asserts it; not-suspended is the
|
|
318
351
|
// sole path that clears it.
|
|
319
352
|
if (entry.suspended)
|
|
320
353
|
assertSuspendMarker(input.shieldId);
|
|
321
|
-
else
|
|
354
|
+
else if (persisted)
|
|
322
355
|
clearSuspendMarker(input.shieldId);
|
|
356
|
+
if (!persisted && input.requireServerValidation) {
|
|
357
|
+
cacheWriteFailed = true;
|
|
358
|
+
throw new Error('Runtime policy cache could not be saved');
|
|
359
|
+
}
|
|
323
360
|
const { fetchedAt, revalidateAttemptAt: _attemptAt, ...bundle } = entry;
|
|
324
|
-
return { ...bundle, source: 'server', serverValidatedAt: fetchedAt };
|
|
361
|
+
return { ...bundle, suspended: bundle.suspended === true || hasValidSuspendMarker(), source: 'server', serverValidatedAt: fetchedAt };
|
|
325
362
|
}
|
|
363
|
+
(0, distress_1.reportDistress)('bundle', distress_1.DISTRESS.BACKEND_REJECTED, 'Bundle response did not contain a valid mode and version');
|
|
326
364
|
}
|
|
327
365
|
}
|
|
328
366
|
catch (error) {
|
|
329
|
-
//
|
|
330
|
-
|
|
367
|
+
// Separate transport failure, invalid server data and local disk failure:
|
|
368
|
+
// each requires a different repair, and reaches fleet diagnostics as such.
|
|
369
|
+
(0, distress_1.reportDistress)('bundle', cacheWriteFailed ? distress_1.DISTRESS.POLICY_CACHE_WRITE_FAILED : responseReceived ? distress_1.DISTRESS.BACKEND_REJECTED : distress_1.DISTRESS.NETWORK_DOWN, error instanceof Error ? error.message : String(error));
|
|
331
370
|
/* fall through to cache / default */
|
|
332
371
|
}
|
|
333
372
|
if (input.requireServerValidation) {
|
package/dist/selfTest.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ResolvedCliCredentials } from './config';
|
|
2
|
+
import { type BoundaryVerification } from './boundaryVerification';
|
|
2
3
|
/**
|
|
3
4
|
* Deep self-test — the upgraded `health_check` remote action.
|
|
4
5
|
*
|
|
@@ -33,6 +34,7 @@ export interface SelfTestReport {
|
|
|
33
34
|
ok: boolean;
|
|
34
35
|
checks: SelfTestCheck[];
|
|
35
36
|
ranAt: string;
|
|
37
|
+
boundaryVerification?: BoundaryVerification;
|
|
36
38
|
}
|
|
37
39
|
export declare function runDeepSelfTest(input: {
|
|
38
40
|
creds: ResolvedCliCredentials;
|
package/dist/selfTest.js
CHANGED
|
@@ -10,6 +10,7 @@ const runtimeConfig_1 = require("./runtimeConfig");
|
|
|
10
10
|
const integrity_1 = require("./integrity");
|
|
11
11
|
const selfUpdate_1 = require("./selfUpdate");
|
|
12
12
|
const windowsTaskState_1 = require("./windowsTaskState");
|
|
13
|
+
const boundaryVerification_1 = require("./boundaryVerification");
|
|
13
14
|
/** Windows scheduled-task probe: exists + (when readable) its last result code. */
|
|
14
15
|
function queryScheduledTask(taskName) {
|
|
15
16
|
const query = (0, child_process_1.spawnSync)('schtasks', ['/Query', '/TN', taskName, '/V', '/FO', 'LIST'], {
|
|
@@ -175,7 +176,14 @@ async function runDeepSelfTest(input) {
|
|
|
175
176
|
// PowerShell blocked on a hardened bank machine, or one of the redundant
|
|
176
177
|
// credential stores unavailable while auth demonstrably works) used to fail
|
|
177
178
|
// the whole report, training operators to ignore red self-tests entirely.
|
|
178
|
-
|
|
179
|
+
let boundaryVerification;
|
|
180
|
+
try {
|
|
181
|
+
boundaryVerification = await (0, boundaryVerification_1.runBoundaryVerification)();
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
log('Boundary verification unavailable — checker could not complete.');
|
|
185
|
+
}
|
|
186
|
+
return { ok: checks.every(check => check.ok || check.optional === true), checks, ranAt: new Date().toISOString(), boundaryVerification };
|
|
179
187
|
}
|
|
180
188
|
/** FCD per-user tasks that could flash a console window on every trigger. */
|
|
181
189
|
const WINDOWED_TASK_CANDIDATES = ['FullCourtDefense Daemon', 'FullCourtDefense Daemon Watchdog', 'FullCourtDefense Auto-Protect', 'FullCourtDefenseDesktopDiscover'];
|
package/dist/sessionLimits.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface SessionRoleLimits {
|
|
|
11
11
|
}
|
|
12
12
|
/** Normalize a raw bundle limits object (new or legacy keys) into the split shape. */
|
|
13
13
|
export declare function normalizeSessionLimits(raw: Record<string, unknown> | undefined | null): SessionRoleLimits;
|
|
14
|
+
/** Validate a delivered limit set before replacing cached enforcement state. */
|
|
15
|
+
export declare function parseDeliveredSessionLimits(raw: unknown, previous?: SessionRoleLimits): SessionRoleLimits;
|
|
14
16
|
export interface SessionUsage {
|
|
15
17
|
sessionId: string;
|
|
16
18
|
dbReads: number;
|
package/dist/sessionLimits.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.RESPONSE_VOLUME_LIMITS = void 0;
|
|
37
37
|
exports.normalizeSessionLimits = normalizeSessionLimits;
|
|
38
|
+
exports.parseDeliveredSessionLimits = parseDeliveredSessionLimits;
|
|
38
39
|
exports.getSessionUsage = getSessionUsage;
|
|
39
40
|
exports.recordSessionOp = recordSessionOp;
|
|
40
41
|
exports.recordSessionResponse = recordSessionResponse;
|
|
@@ -117,6 +118,29 @@ function normalizeSessionLimits(raw) {
|
|
|
117
118
|
}
|
|
118
119
|
return limits;
|
|
119
120
|
}
|
|
121
|
+
/** Validate a delivered limit set before replacing cached enforcement state. */
|
|
122
|
+
function parseDeliveredSessionLimits(raw, previous) {
|
|
123
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
124
|
+
throw new Error('Invalid machine role limits');
|
|
125
|
+
const values = raw;
|
|
126
|
+
for (const key of [...LIMIT_KEYS, ...Object.keys(LEGACY_LIMIT_KEY_MAP)]) {
|
|
127
|
+
const value = values[key];
|
|
128
|
+
if (value !== undefined && value !== null
|
|
129
|
+
&& !(typeof value === 'number' && Number.isFinite(value) && value >= 0)) {
|
|
130
|
+
throw new Error(`Invalid machine role limit: ${key}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const limits = normalizeSessionLimits(values);
|
|
134
|
+
for (const key of LIMIT_KEYS) {
|
|
135
|
+
// A missing field is not a request to remove a previously finite limit.
|
|
136
|
+
// Explicit null is the supported Unlimited value. Legacy numeric keys
|
|
137
|
+
// remain valid because normalization maps them to their successors first.
|
|
138
|
+
if (typeof previous?.[key] === 'number' && limits[key] === null && values[key] !== null) {
|
|
139
|
+
throw new Error(`Machine role limit missing from bundle: ${key}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return limits;
|
|
143
|
+
}
|
|
120
144
|
const SESSION_FILE_TTL_MS = 48 * 60 * 60 * 1000;
|
|
121
145
|
function sessionsDir() {
|
|
122
146
|
return path.join(os.homedir(), '.fullcourtdefense', 'sessions');
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { type ActionIdentity } from './actionIdentity';
|
|
2
2
|
export interface SpoolEvent extends ActionIdentity {
|
|
3
|
+
cliVersion?: string;
|
|
4
|
+
detectorRevision?: number;
|
|
5
|
+
detectorSha256?: string;
|
|
6
|
+
/** Loaded module state when recording, not proof this module decided the action. */
|
|
7
|
+
detectorActive?: boolean;
|
|
3
8
|
agentClient?: string;
|
|
4
9
|
eventId: string;
|
|
5
10
|
type: 'verdict';
|
package/dist/telemetry.js
CHANGED
|
@@ -52,6 +52,8 @@ const path = __importStar(require("path"));
|
|
|
52
52
|
const machineIdentity_1 = require("./machineIdentity");
|
|
53
53
|
const actionIdentity_1 = require("./actionIdentity");
|
|
54
54
|
const localDetectionUpdates_1 = require("./localDetectionUpdates");
|
|
55
|
+
const cliVersion_1 = require("./cliVersion");
|
|
56
|
+
const installedCliVersion = (0, cliVersion_1.cliVersion)();
|
|
55
57
|
/**
|
|
56
58
|
* Local-first telemetry: every enforcement decision is appended to an on-disk
|
|
57
59
|
* spool (instant, offline-safe) and flushed to the backend in batches. Critical
|
|
@@ -194,7 +196,12 @@ function spoolEvent(event) {
|
|
|
194
196
|
// took. Absent when a caller spools outside a hook evaluation (guards,
|
|
195
197
|
// onboard) — the backend treats missing fields as "no sample".
|
|
196
198
|
const timing = verdictTiming.getStore();
|
|
199
|
+
const detector = localDetectionUpdates_1.localDetectorUpdates.getDetectorStatus();
|
|
197
200
|
const full = {
|
|
201
|
+
cliVersion: installedCliVersion,
|
|
202
|
+
detectorRevision: detector.revision,
|
|
203
|
+
detectorSha256: detector.sha256,
|
|
204
|
+
detectorActive: detector.active,
|
|
198
205
|
...(0, actionIdentity_1.captureActionIdentity)(),
|
|
199
206
|
...timing?.identity,
|
|
200
207
|
eventId: crypto.randomUUID(),
|
|
@@ -366,6 +373,7 @@ async function flushSpoolLocked(input) {
|
|
|
366
373
|
? {
|
|
367
374
|
agentVersion: input.agentVersion,
|
|
368
375
|
detectionUpdates: localDetectionUpdates_1.localDetectionUpdates.getStatus(),
|
|
376
|
+
detectorUpdates: localDetectionUpdates_1.localDetectorUpdates.getDetectorStatus(),
|
|
369
377
|
integrityOk: input.integrityOk,
|
|
370
378
|
integrityReasons: input.integrityReasons,
|
|
371
379
|
integrityCheckedAt: input.integrityCheckedAt,
|
package/dist/version.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fullcourtdefense-cli",
|
|
3
|
-
"version": "1.34.
|
|
3
|
+
"version": "1.34.25",
|
|
4
4
|
"description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,10 +13,13 @@
|
|
|
13
13
|
"README.md"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
|
+
"test:action-benchmark-report": "node scripts/test-action-benchmark-report.js && node scripts/test-action-benchmark-isolation.js",
|
|
17
|
+
"test:boundary-verification": "npm run build && node scripts/test-boundary-verification.js",
|
|
18
|
+
"test:detector-updates": "npm run build && node scripts/test-detector-artifacts.js && node scripts/test-detector-publication.js && node scripts/test-detection-updates.js && node scripts/test-detection-cache.js",
|
|
16
19
|
"test:native-hooks": "npm run build && node scripts/test-codex-activation.js && node scripts/test-native-hook-launch.js && node scripts/test-native-hook-edges.js && node scripts/test-client-telemetry.js && node scripts/test-native-hook-clients.js",
|
|
17
20
|
"test:endpoint-approvals": "npm run build && node scripts/audit-endpoint-mcp-shell.js",
|
|
18
21
|
"test:endpoint-clients": "npm run build && node scripts/test-endpoint-client-matrix.js && node scripts/test-copilot-hook-install.js",
|
|
19
|
-
"build": "tsc && node scripts/bundle-policy-engine.js && node scripts/copy-attack-corpus.js",
|
|
22
|
+
"build": "tsc && node scripts/bundle-policy-engine.js && node scripts/copy-attack-corpus.js && node scripts/bundle-containment-runtime.js",
|
|
20
23
|
"test:deterministic-guard": "npm run build && node scripts/test-deterministic-guard.js",
|
|
21
24
|
"test:drive-delete-guard": "npm run build && node scripts/test-drive-delete-guard.js",
|
|
22
25
|
"test:msi-stop-filter": "node scripts/test-msi-stop-filter.js",
|
|
@@ -44,7 +47,7 @@
|
|
|
44
47
|
"test:user-objective-labels": "npm run build && node scripts/test-user-objective-labels.js",
|
|
45
48
|
"test:remote-mcp-gateway": "npm run build && node scripts/test-remote-mcp-gateway.js",
|
|
46
49
|
"test:e2e-onboarding-personas": "npm run build && node scripts/test-e2e-onboarding-personas.js",
|
|
47
|
-
"test:onboarding-transaction": "npm run build && node scripts/test-onboarding-transaction.js",
|
|
50
|
+
"test:onboarding-transaction": "npm run build && node scripts/test-onboarding-transaction.js && node scripts/test-onboarding-summary.js",
|
|
48
51
|
"test:posture-blast": "npm run build && node scripts/test-posture-blast-radius.js",
|
|
49
52
|
"test:ping-e2e": "npm run build && node scripts/test-ping-e2e.js",
|
|
50
53
|
"test:daemon-e2e": "npm run build && node scripts/test-daemon-e2e.js",
|
|
@@ -58,7 +61,7 @@
|
|
|
58
61
|
"test:scan-root-compaction": "npm run build && node scripts/test-scan-root-compaction.js",
|
|
59
62
|
"test:dpapi-config": "npm run build && node scripts/test-dpapi-config.js",
|
|
60
63
|
"test:distress-ledger": "npm run build && node scripts/test-distress-ledger.js",
|
|
61
|
-
"test:poll-loop": "npm run build && node scripts/test-poll-loop-resilience.js && node scripts/test-background-bundle.js",
|
|
64
|
+
"test:poll-loop": "npm run build && node scripts/test-poll-loop-resilience.js && node scripts/test-background-bundle.js && node scripts/test-policy-refresh.js",
|
|
62
65
|
"test:slim-hook": "npm run build && node scripts/test-slim-hook.js",
|
|
63
66
|
"test:killswitch": "npm run build && node scripts/test-killswitch-integrity.js",
|
|
64
67
|
"test:restart-guarantees": "npm run build && node scripts/test-daemon-restart-guarantees.js",
|
|
@@ -80,7 +83,7 @@
|
|
|
80
83
|
"test:failopen-killswitch": "npm run build && node scripts/test-failopen-killswitch.js",
|
|
81
84
|
"test:enroll-cache-reset": "npm run build && node scripts/test-enroll-cache-reset.js",
|
|
82
85
|
"test:offline-policy": "npm run build && node scripts/test-offline-policy-enforcement.js",
|
|
83
|
-
"test:developer-confirmation": "npm run build && node scripts/test-developer-confirmation.js",
|
|
86
|
+
"test:developer-confirmation": "npm run build && node scripts/test-developer-confirmation.js && node scripts/test-approval-scope.js && node scripts/test-approval-prompt.js",
|
|
84
87
|
"test:local-safety-ask": "npm run build && node scripts/test-local-safety-ask.js",
|
|
85
88
|
"test:terminal-mcp-ask": "npm run build && node scripts/test-terminal-mcp-ask.js",
|
|
86
89
|
"test:demo-actions": "npm run build && node scripts/test-demo-actions.js",
|
|
@@ -96,7 +99,7 @@
|
|
|
96
99
|
"test:ide-fp-corpus:hook": "npm run build && node scripts/test-ide-fp-corpus.js --hook",
|
|
97
100
|
"test:msi-payload-deps": "npm run build && node scripts/test-msi-payload-deps.js",
|
|
98
101
|
"build:msi": "powershell -NoProfile -ExecutionPolicy Bypass -File installer/windows/Build-Msi.ps1",
|
|
99
|
-
"prepublishOnly": "npm run build && node scripts/check-detection-baseline.js && node scripts/test-detection-updates.js && node scripts/test-detection-cache.js && node scripts/test-msi-payload-deps.js && node scripts/test-shell-parity.js && node scripts/test-terminal-mcp-ask.js"
|
|
102
|
+
"prepublishOnly": "npm run build && node scripts/check-detection-baseline.js && node scripts/check-detector-baseline.js && node scripts/test-detector-artifacts.js && node scripts/test-detector-publication.js && node scripts/test-detection-updates.js && node scripts/test-detection-cache.js && node scripts/test-msi-payload-deps.js && node scripts/test-shell-parity.js && node scripts/test-terminal-mcp-ask.js"
|
|
100
103
|
},
|
|
101
104
|
"keywords": [
|
|
102
105
|
"llm",
|
|
@@ -112,6 +115,7 @@
|
|
|
112
115
|
],
|
|
113
116
|
"license": "MIT",
|
|
114
117
|
"devDependencies": {
|
|
118
|
+
"@anthropic-ai/sandbox-runtime": "0.0.76",
|
|
115
119
|
"@types/node": "^25.3.0",
|
|
116
120
|
"acorn": "8.18.0",
|
|
117
121
|
"esbuild": "0.28.2",
|