fullcourtdefense-cli 1.21.34 → 1.21.36
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/cmdGuard.d.ts +12 -6
- package/dist/commands/cmdGuard.js +47 -8
- package/dist/commands/daemon.js +9 -0
- package/dist/commands/shellGuard.d.ts +7 -0
- package/dist/commands/shellGuard.js +49 -21
- package/dist/selfUpdate.d.ts +22 -0
- package/dist/selfUpdate.js +66 -3
- package/dist/version.json +1 -1
- package/package.json +2 -1
|
@@ -19,11 +19,17 @@ export declare function uninstallCmdGuardCommand(): Promise<void>;
|
|
|
19
19
|
/** Refresh shared rules JSON when cmd guard is installed. */
|
|
20
20
|
export declare function refreshCmdGuardRules(): void;
|
|
21
21
|
/**
|
|
22
|
-
* Self-heal a stale cmd-guard AutoRun
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
22
|
+
* Self-heal a stale/broken cmd-guard AutoRun. Two failure shapes are covered:
|
|
23
|
+
*
|
|
24
|
+
* 1. Registry references our autorun bat but the FILE was deleted (manual
|
|
25
|
+
* cleanup, interrupted uninstall) — every cmd.exe errors on startup and
|
|
26
|
+
* poisons `cmd /c` exit codes to 1.
|
|
27
|
+
* 2. The bat EXISTS but its doskey macros point at a node.exe or checker
|
|
28
|
+
* script that no longer exists (uninstall/upgrade removed the install
|
|
29
|
+
* root but left the per-user bat) — every wrapped command (docker, git,
|
|
30
|
+
* kubectl, …) fails with "cannot find the path specified".
|
|
31
|
+
*
|
|
32
|
+
* In both cases: strip OUR fragment from AutoRun (a customer's own entries
|
|
33
|
+
* survive) and delete our orphaned guard files. Returns true when repaired.
|
|
28
34
|
*/
|
|
29
35
|
export declare function repairStaleCmdAutorun(): boolean;
|
|
@@ -142,6 +142,11 @@ function buildAutorunBat(nodePath) {
|
|
|
142
142
|
'REM Remove permanently: fullcourtdefense uninstall-cmd-guard',
|
|
143
143
|
'if defined FCD_CMD_AUTORUN_DONE goto :eof',
|
|
144
144
|
'set FCD_CMD_AUTORUN_DONE=1',
|
|
145
|
+
// FAIL OPEN: if the runtime or checker is gone (uninstall/upgrade left this
|
|
146
|
+
// bat behind), register NO macros — wrapped commands (docker, git, kubectl…)
|
|
147
|
+
// must keep working natively. Never break the customer's tools.
|
|
148
|
+
`if not exist ${node} goto :eof`,
|
|
149
|
+
`if not exist ${guardJs} goto :eof`,
|
|
145
150
|
...doskeyLines,
|
|
146
151
|
'',
|
|
147
152
|
].join('\r\n');
|
|
@@ -266,12 +271,26 @@ function refreshCmdGuardRules() {
|
|
|
266
271
|
catch { /* best-effort */ }
|
|
267
272
|
}
|
|
268
273
|
/**
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
274
|
+
* The node.exe path baked into an installed autorun bat's doskey macros.
|
|
275
|
+
* Format written by buildAutorunBat: `doskey <cmd>="<node>" "<guardJs>" <cmd> $*`.
|
|
276
|
+
*/
|
|
277
|
+
function autorunBatNodePath(batContent) {
|
|
278
|
+
const match = batContent.match(/^doskey\s+\S+="([^"]+)"\s/m);
|
|
279
|
+
return match ? match[1] : undefined;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Self-heal a stale/broken cmd-guard AutoRun. Two failure shapes are covered:
|
|
283
|
+
*
|
|
284
|
+
* 1. Registry references our autorun bat but the FILE was deleted (manual
|
|
285
|
+
* cleanup, interrupted uninstall) — every cmd.exe errors on startup and
|
|
286
|
+
* poisons `cmd /c` exit codes to 1.
|
|
287
|
+
* 2. The bat EXISTS but its doskey macros point at a node.exe or checker
|
|
288
|
+
* script that no longer exists (uninstall/upgrade removed the install
|
|
289
|
+
* root but left the per-user bat) — every wrapped command (docker, git,
|
|
290
|
+
* kubectl, …) fails with "cannot find the path specified".
|
|
291
|
+
*
|
|
292
|
+
* In both cases: strip OUR fragment from AutoRun (a customer's own entries
|
|
293
|
+
* survive) and delete our orphaned guard files. Returns true when repaired.
|
|
275
294
|
*/
|
|
276
295
|
function repairStaleCmdAutorun() {
|
|
277
296
|
try {
|
|
@@ -280,8 +299,20 @@ function repairStaleCmdAutorun() {
|
|
|
280
299
|
const current = readCurrentAutorun();
|
|
281
300
|
if (!current.includes(AUTORUN_MARKER))
|
|
282
301
|
return false;
|
|
283
|
-
if (fs.existsSync(AUTORUN_BAT_PATH))
|
|
284
|
-
|
|
302
|
+
if (fs.existsSync(AUTORUN_BAT_PATH)) {
|
|
303
|
+
// Bat present — only stale when its macro targets are gone.
|
|
304
|
+
let bat = '';
|
|
305
|
+
try {
|
|
306
|
+
bat = fs.readFileSync(AUTORUN_BAT_PATH, 'utf8');
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
const nodePath = autorunBatNodePath(bat);
|
|
312
|
+
const runtimeAlive = (!nodePath || fs.existsSync(nodePath)) && fs.existsSync(GUARD_JS_PATH);
|
|
313
|
+
if (runtimeAlive)
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
285
316
|
const next = stripAutorunValue(current);
|
|
286
317
|
if (next) {
|
|
287
318
|
setRegString(CMD_AUTORUN_KEY, AUTORUN_VALUE, next);
|
|
@@ -291,6 +322,14 @@ function repairStaleCmdAutorun() {
|
|
|
291
322
|
windowsHide: true, timeout: 10000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
292
323
|
});
|
|
293
324
|
}
|
|
325
|
+
try {
|
|
326
|
+
fs.unlinkSync(AUTORUN_BAT_PATH);
|
|
327
|
+
}
|
|
328
|
+
catch { /* already gone */ }
|
|
329
|
+
try {
|
|
330
|
+
fs.unlinkSync(GUARD_JS_PATH);
|
|
331
|
+
}
|
|
332
|
+
catch { /* already gone */ }
|
|
294
333
|
return true;
|
|
295
334
|
}
|
|
296
335
|
catch {
|
package/dist/commands/daemon.js
CHANGED
|
@@ -63,6 +63,7 @@ const integrity_1 = require("../integrity");
|
|
|
63
63
|
const machineIdentity_1 = require("../machineIdentity");
|
|
64
64
|
const discoveryMarker_1 = require("../discoveryMarker");
|
|
65
65
|
const selfUpdate_1 = require("../selfUpdate");
|
|
66
|
+
const cmdGuard_1 = require("./cmdGuard");
|
|
66
67
|
const machineActionVerify_1 = require("../machineActionVerify");
|
|
67
68
|
const desktopChatGuard_1 = require("./desktopChatGuard");
|
|
68
69
|
const honeypot_1 = require("../honeypot");
|
|
@@ -1029,6 +1030,14 @@ async function runDaemon(args, config) {
|
|
|
1029
1030
|
// Self-heal autostart + watchdog tasks: machines installed by older versions
|
|
1030
1031
|
// (or where task creation failed once) must converge without a reinstall.
|
|
1031
1032
|
ensureWindowsAutostartHealthy(log);
|
|
1033
|
+
// Converge pre-Node-updater machines onto the PowerShell-free updater task
|
|
1034
|
+
// (best-effort; needs an elevated daemon to rewrite a SYSTEM task).
|
|
1035
|
+
(0, selfUpdate_1.modernizeUpdaterTask)(log);
|
|
1036
|
+
// Heal a stale/broken cmd-guard AutoRun (bat deleted, or macros pointing at
|
|
1037
|
+
// a dead node.exe) so wrapped commands (docker, git, …) never stay broken.
|
|
1038
|
+
if ((0, cmdGuard_1.repairStaleCmdAutorun)()) {
|
|
1039
|
+
log('Repaired a stale cmd.exe AutoRun / doskey guard left by an earlier install.');
|
|
1040
|
+
}
|
|
1032
1041
|
const watched = refreshWatchTargets();
|
|
1033
1042
|
log(`Watching ${watched} config file(s) across ${watchers.size} director${watchers.size === 1 ? 'y' : 'ies'}.`);
|
|
1034
1043
|
// First: if this boot IS the post-upgrade relaunch, confirm the pending
|
|
@@ -39,6 +39,13 @@ export declare function evaluateShellCommand(line: string, rules?: ShellGuardRul
|
|
|
39
39
|
* No-op unless the guard is installed. Never throws.
|
|
40
40
|
*/
|
|
41
41
|
export declare function refreshShellGuardRules(): void;
|
|
42
|
+
/**
|
|
43
|
+
* The user's Documents folder from the shell-folder registry entry — the one
|
|
44
|
+
* place that knows about OneDrive/folder redirection. reg.exe is a plain
|
|
45
|
+
* system utility (not a scripting engine), so EDRs that block powershell.exe
|
|
46
|
+
* do not block this.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveDocumentsFolder(): string;
|
|
42
49
|
export interface ShellGuardStatus {
|
|
43
50
|
supported: boolean;
|
|
44
51
|
installed: boolean;
|
|
@@ -37,6 +37,7 @@ exports.writeShellGuardRules = writeShellGuardRules;
|
|
|
37
37
|
exports.activeShellGuardRules = activeShellGuardRules;
|
|
38
38
|
exports.evaluateShellCommand = evaluateShellCommand;
|
|
39
39
|
exports.refreshShellGuardRules = refreshShellGuardRules;
|
|
40
|
+
exports.resolveDocumentsFolder = resolveDocumentsFolder;
|
|
40
41
|
exports.getShellGuardStatus = getShellGuardStatus;
|
|
41
42
|
exports.isShellGuardInstalled = isShellGuardInstalled;
|
|
42
43
|
exports.installShellGuardCommand = installShellGuardCommand;
|
|
@@ -510,30 +511,57 @@ function buildGuardPs1(nodePath, cliEntry) {
|
|
|
510
511
|
];
|
|
511
512
|
return lines.join('\r\n');
|
|
512
513
|
}
|
|
513
|
-
/**
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
514
|
+
/**
|
|
515
|
+
* The user's Documents folder from the shell-folder registry entry — the one
|
|
516
|
+
* place that knows about OneDrive/folder redirection. reg.exe is a plain
|
|
517
|
+
* system utility (not a scripting engine), so EDRs that block powershell.exe
|
|
518
|
+
* do not block this.
|
|
519
|
+
*/
|
|
520
|
+
function resolveDocumentsFolder() {
|
|
521
|
+
try {
|
|
522
|
+
const run = (0, child_process_1.spawnSync)('reg', [
|
|
523
|
+
'query', 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders', '/v', 'Personal',
|
|
524
|
+
], { encoding: 'utf8', windowsHide: true, timeout: 10_000 });
|
|
525
|
+
const raw = ((run.stdout || '').match(/Personal\s+REG_(?:EXPAND_)?SZ\s+(.+)/) || [])[1]?.trim();
|
|
526
|
+
if (run.status === 0 && raw) {
|
|
527
|
+
const expanded = raw.replace(/%([^%]+)%/g, (_, name) => process.env[name] ?? `%${name}%`);
|
|
528
|
+
if (!expanded.includes('%'))
|
|
529
|
+
return expanded;
|
|
525
530
|
}
|
|
526
|
-
catch { /* engine not installed */ }
|
|
527
531
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
532
|
+
catch { /* registry unreadable — fall through */ }
|
|
533
|
+
return path.join(os.homedir(), 'Documents');
|
|
534
|
+
}
|
|
535
|
+
function pwshInstalled() {
|
|
536
|
+
const programFiles = process.env.ProgramFiles || 'C:\\Program Files';
|
|
537
|
+
if (fs.existsSync(path.join(programFiles, 'PowerShell', '7', 'pwsh.exe'))
|
|
538
|
+
|| fs.existsSync(path.join(programFiles, 'PowerShell', '7-preview', 'pwsh.exe')))
|
|
535
539
|
return true;
|
|
536
|
-
|
|
540
|
+
for (const dir of (process.env.PATH || '').split(path.delimiter)) {
|
|
541
|
+
try {
|
|
542
|
+
if (dir && fs.existsSync(path.join(dir, 'pwsh.exe')))
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
catch { /* keep looking */ }
|
|
546
|
+
}
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Resolve each PowerShell engine's CurrentUserAllHosts profile path WITHOUT
|
|
551
|
+
* spawning PowerShell. $PROFILE.CurrentUserAllHosts is deterministic:
|
|
552
|
+
* <Documents>\WindowsPowerShell\profile.ps1 (Windows PowerShell, always
|
|
553
|
+
* present on Windows) and <Documents>\PowerShell\profile.ps1 (pwsh, when
|
|
554
|
+
* installed). Asking the engines themselves was both an EDR flag risk and
|
|
555
|
+
* wrong on machines where EDR blocks PowerShell — the guard then silently
|
|
556
|
+
* skipped profile installation on exactly the fleets that want it most.
|
|
557
|
+
*/
|
|
558
|
+
function resolveProfilePaths() {
|
|
559
|
+
const documents = resolveDocumentsFolder();
|
|
560
|
+
const out = [{ engine: 'powershell', profilePath: path.join(documents, 'WindowsPowerShell', 'profile.ps1') }];
|
|
561
|
+
if (pwshInstalled()) {
|
|
562
|
+
out.push({ engine: 'pwsh', profilePath: path.join(documents, 'PowerShell', 'profile.ps1') });
|
|
563
|
+
}
|
|
564
|
+
return out;
|
|
537
565
|
}
|
|
538
566
|
function profileSnippet() {
|
|
539
567
|
return [
|
package/dist/selfUpdate.d.ts
CHANGED
|
@@ -26,6 +26,28 @@ export interface SelfUpdateResult {
|
|
|
26
26
|
detail: string;
|
|
27
27
|
}
|
|
28
28
|
export declare function resolveNpmCommand(platform?: NodeJS.Platform, execPath?: string, exists?: typeof fs.existsSync): string;
|
|
29
|
+
/**
|
|
30
|
+
* The updater task command for an install root. Prefers the Node updater
|
|
31
|
+
* (runs on the MSI's own bundled runtime — no powershell.exe spawn, which
|
|
32
|
+
* EDR/AppLocker block on hardened fleets); the PowerShell script remains the
|
|
33
|
+
* command only for old payloads that predate the Node updater. Escaped for
|
|
34
|
+
* `schtasks /TR` (embedded quotes as \").
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildUpdaterTaskCommand(root: string, exists?: (p: string) => boolean): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* True when a registered task command still points at the PowerShell updater
|
|
39
|
+
* while this install carries the Node updater — i.e. the task predates the
|
|
40
|
+
* PowerShell-free updater and should be re-registered.
|
|
41
|
+
*/
|
|
42
|
+
export declare function updaterTaskNeedsModernization(taskToRun: string, root: string, exists?: (p: string) => boolean): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Upgrade an EXISTING updater task from the PowerShell command to the Node
|
|
45
|
+
* command. Machines that upgraded from a pre-Node-updater MSI keep their old
|
|
46
|
+
* task definition (the MSI re-registers it, but registration can be denied);
|
|
47
|
+
* this converges them whenever a daemon runs elevated. Best-effort: needs an
|
|
48
|
+
* elevated token to re-register a SYSTEM task, silent no-op otherwise.
|
|
49
|
+
*/
|
|
50
|
+
export declare function modernizeUpdaterTask(log: (message: string) => void): void;
|
|
29
51
|
/**
|
|
30
52
|
* Updater script log (%ProgramData%\FullCourtDefense\updater.log) — the
|
|
31
53
|
* elevated task's own words. Read by the diagnostics bundle and the update-
|
package/dist/selfUpdate.js
CHANGED
|
@@ -37,6 +37,9 @@ exports.MSI_UPDATER_TASK_NAME = void 0;
|
|
|
37
37
|
exports.detectInstallKind = detectInstallKind;
|
|
38
38
|
exports.compareCliVersions = compareCliVersions;
|
|
39
39
|
exports.resolveNpmCommand = resolveNpmCommand;
|
|
40
|
+
exports.buildUpdaterTaskCommand = buildUpdaterTaskCommand;
|
|
41
|
+
exports.updaterTaskNeedsModernization = updaterTaskNeedsModernization;
|
|
42
|
+
exports.modernizeUpdaterTask = modernizeUpdaterTask;
|
|
40
43
|
exports.readUpdaterLogTail = readUpdaterLogTail;
|
|
41
44
|
exports.maybeSelfUpdate = maybeSelfUpdate;
|
|
42
45
|
exports.installedMsiVersion = installedMsiVersion;
|
|
@@ -133,7 +136,38 @@ function msiInstallRoot() {
|
|
|
133
136
|
// dist\index.js → install root is the parent of dist.
|
|
134
137
|
const dist = path.dirname(entry);
|
|
135
138
|
const root = path.dirname(dist);
|
|
136
|
-
|
|
139
|
+
const hasUpdater = fs.existsSync(path.join(root, 'Update-FullCourtDefense.js'))
|
|
140
|
+
|| fs.existsSync(path.join(root, 'Update-FullCourtDefense.ps1'));
|
|
141
|
+
return hasUpdater ? root : undefined;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The updater task command for an install root. Prefers the Node updater
|
|
145
|
+
* (runs on the MSI's own bundled runtime — no powershell.exe spawn, which
|
|
146
|
+
* EDR/AppLocker block on hardened fleets); the PowerShell script remains the
|
|
147
|
+
* command only for old payloads that predate the Node updater. Escaped for
|
|
148
|
+
* `schtasks /TR` (embedded quotes as \").
|
|
149
|
+
*/
|
|
150
|
+
function buildUpdaterTaskCommand(root, exists = fs.existsSync) {
|
|
151
|
+
const nodeExe = path.join(root, 'runtime', 'node.exe');
|
|
152
|
+
const jsUpdater = path.join(root, 'Update-FullCourtDefense.js');
|
|
153
|
+
if (exists(nodeExe) && exists(jsUpdater)) {
|
|
154
|
+
return `\\"${nodeExe}\\" \\"${jsUpdater}\\"`;
|
|
155
|
+
}
|
|
156
|
+
const ps1Updater = path.join(root, 'Update-FullCourtDefense.ps1');
|
|
157
|
+
if (exists(ps1Updater)) {
|
|
158
|
+
return `powershell.exe -NoProfile -ExecutionPolicy Bypass -File \\"${ps1Updater}\\"`;
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* True when a registered task command still points at the PowerShell updater
|
|
164
|
+
* while this install carries the Node updater — i.e. the task predates the
|
|
165
|
+
* PowerShell-free updater and should be re-registered.
|
|
166
|
+
*/
|
|
167
|
+
function updaterTaskNeedsModernization(taskToRun, root, exists = fs.existsSync) {
|
|
168
|
+
if (!/powershell/i.test(taskToRun))
|
|
169
|
+
return false;
|
|
170
|
+
return exists(path.join(root, 'Update-FullCourtDefense.js')) && exists(path.join(root, 'runtime', 'node.exe'));
|
|
137
171
|
}
|
|
138
172
|
/**
|
|
139
173
|
* Self-heal a missing updater task. Machines installed from an older MSI (or
|
|
@@ -146,8 +180,9 @@ function tryRegisterMsiUpdaterTask(log) {
|
|
|
146
180
|
const root = msiInstallRoot();
|
|
147
181
|
if (!root)
|
|
148
182
|
return false;
|
|
149
|
-
const
|
|
150
|
-
|
|
183
|
+
const taskCommand = buildUpdaterTaskCommand(root);
|
|
184
|
+
if (!taskCommand)
|
|
185
|
+
return false;
|
|
151
186
|
const create = (0, child_process_1.spawnSync)('schtasks', [
|
|
152
187
|
'/Create', '/TN', exports.MSI_UPDATER_TASK_NAME, '/TR', taskCommand,
|
|
153
188
|
'/SC', 'DAILY', '/ST', '03:07', '/RU', 'SYSTEM', '/RL', 'HIGHEST', '/F',
|
|
@@ -158,6 +193,34 @@ function tryRegisterMsiUpdaterTask(log) {
|
|
|
158
193
|
}
|
|
159
194
|
return false;
|
|
160
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Upgrade an EXISTING updater task from the PowerShell command to the Node
|
|
198
|
+
* command. Machines that upgraded from a pre-Node-updater MSI keep their old
|
|
199
|
+
* task definition (the MSI re-registers it, but registration can be denied);
|
|
200
|
+
* this converges them whenever a daemon runs elevated. Best-effort: needs an
|
|
201
|
+
* elevated token to re-register a SYSTEM task, silent no-op otherwise.
|
|
202
|
+
*/
|
|
203
|
+
function modernizeUpdaterTask(log) {
|
|
204
|
+
if (process.platform !== 'win32')
|
|
205
|
+
return;
|
|
206
|
+
try {
|
|
207
|
+
const root = msiInstallRoot();
|
|
208
|
+
if (!root)
|
|
209
|
+
return;
|
|
210
|
+
const query = (0, child_process_1.spawnSync)('schtasks', ['/Query', '/TN', exports.MSI_UPDATER_TASK_NAME, '/V', '/FO', 'LIST'], {
|
|
211
|
+
encoding: 'utf8', windowsHide: true, timeout: 15_000,
|
|
212
|
+
});
|
|
213
|
+
if (query.status !== 0 || !query.stdout)
|
|
214
|
+
return;
|
|
215
|
+
const taskToRun = (query.stdout.match(/^Task To Run:\s*(.*)$/m) || [])[1] || '';
|
|
216
|
+
if (!updaterTaskNeedsModernization(taskToRun, root))
|
|
217
|
+
return;
|
|
218
|
+
if (tryRegisterMsiUpdaterTask(log)) {
|
|
219
|
+
log('Self-update: updater task modernized to the PowerShell-free Node updater.');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch { /* diagnostics-only convergence — never disturb the daemon */ }
|
|
223
|
+
}
|
|
161
224
|
function startMsiSelfUpdate(targetVersion, log) {
|
|
162
225
|
const query = (0, child_process_1.spawnSync)('schtasks', ['/Query', '/TN', exports.MSI_UPDATER_TASK_NAME], { stdio: 'ignore', windowsHide: true, timeout: 10_000 });
|
|
163
226
|
if (query.status !== 0 && !tryRegisterMsiUpdaterTask(log)) {
|
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.36",
|
|
4
4
|
"description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"test:bricked-rescue": "npm run build && node scripts/test-bricked-machine-rescue.js",
|
|
50
50
|
"test:native-credstore": "npm run build && node scripts/test-native-credential-store.js",
|
|
51
51
|
"test:real-life": "npm run build && node scripts/test-real-life-scenarios.js",
|
|
52
|
+
"test:node-updater": "npm run build && node scripts/test-node-updater.js",
|
|
52
53
|
"test:desktop-chat-guard": "npm run build && node scripts/test-desktop-chat-guard.js",
|
|
53
54
|
"test:clipboard-scan": "npm run build && node scripts/test-clipboard-scan.js",
|
|
54
55
|
"build:msi": "powershell -NoProfile -ExecutionPolicy Bypass -File installer/windows/Build-Msi.ps1",
|