fullcourtdefense-cli 1.10.0 → 1.11.0
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 +20 -0
- package/dist/commands/cmdGuard.js +245 -0
- package/dist/commands/installAll.d.ts +1 -0
- package/dist/commands/installAll.js +11 -0
- package/dist/commands/shellGuard.js +4 -2
- package/dist/index.js +27 -0
- package/dist/telemetry.js +4 -2
- package/dist/version.json +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface CmdGuardStatus {
|
|
2
|
+
supported: boolean;
|
|
3
|
+
installed: boolean;
|
|
4
|
+
autorunValue?: string;
|
|
5
|
+
rulesPresent: boolean;
|
|
6
|
+
ruleCount?: number;
|
|
7
|
+
mode?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function isCmdGuardInstalled(): boolean;
|
|
10
|
+
export declare function getCmdGuardStatus(): CmdGuardStatus;
|
|
11
|
+
export interface InstallCmdGuardArgs {
|
|
12
|
+
/** Reserved for future use. */
|
|
13
|
+
profiles?: string;
|
|
14
|
+
}
|
|
15
|
+
/** `fullcourtdefense install-cmd-guard` — enable interactive cmd.exe blocking. */
|
|
16
|
+
export declare function installCmdGuardCommand(_args?: InstallCmdGuardArgs): Promise<void>;
|
|
17
|
+
/** `fullcourtdefense uninstall-cmd-guard` */
|
|
18
|
+
export declare function uninstallCmdGuardCommand(): Promise<void>;
|
|
19
|
+
/** Refresh shared rules JSON when cmd guard is installed. */
|
|
20
|
+
export declare function refreshCmdGuardRules(): void;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isCmdGuardInstalled = isCmdGuardInstalled;
|
|
37
|
+
exports.getCmdGuardStatus = getCmdGuardStatus;
|
|
38
|
+
exports.installCmdGuardCommand = installCmdGuardCommand;
|
|
39
|
+
exports.uninstallCmdGuardCommand = uninstallCmdGuardCommand;
|
|
40
|
+
exports.refreshCmdGuardRules = refreshCmdGuardRules;
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const os = __importStar(require("os"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const shellGuard_1 = require("./shellGuard");
|
|
46
|
+
/**
|
|
47
|
+
* Interactive cmd.exe guard — blocks dangerous commands TYPED in Command Prompt.
|
|
48
|
+
*
|
|
49
|
+
* cmd.exe has no PSReadLine/preexec hook. We use the supported AutoRun registry
|
|
50
|
+
* value + doskey macros: every new cmd session registers macros for high-risk
|
|
51
|
+
* command names (del, erase, rd, curl, …) that delegate to a Node checker
|
|
52
|
+
* sharing the same rules JSON as the PowerShell guard.
|
|
53
|
+
*
|
|
54
|
+
* Allowed commands run via `cmd /d /c` so AutoRun is not re-triggered (no loop).
|
|
55
|
+
* Interactive cmd only — `cmd /c del ...` from scripts bypasses doskey by design.
|
|
56
|
+
*/
|
|
57
|
+
const GUARD_RULES_PATH = path.join(os.homedir(), '.fullcourtdefense-shell-guard.json');
|
|
58
|
+
const GUARD_JS_PATH = path.join(os.homedir(), '.fullcourtdefense-cmd-guard.js');
|
|
59
|
+
const AUTORUN_BAT_PATH = path.join(os.homedir(), '.fullcourtdefense-cmd-autorun.bat');
|
|
60
|
+
const CMD_AUTORUN_KEY = 'HKCU\\Software\\Microsoft\\Command Processor';
|
|
61
|
+
const AUTORUN_VALUE = 'AutoRun';
|
|
62
|
+
const AUTORUN_MARKER = 'fullcourtdefense-cmd-autorun.bat';
|
|
63
|
+
/** cmd builtins / exes we intercept via doskey (first token of the typed line). */
|
|
64
|
+
const INTERCEPTED_COMMANDS = [
|
|
65
|
+
'del', 'erase', 'rd', 'rmdir', 'format',
|
|
66
|
+
'curl', 'wget', 'powershell', 'pwsh',
|
|
67
|
+
'terraform', 'kubectl', 'aws', 'gcloud', 'az',
|
|
68
|
+
'vssadmin', 'certutil', 'bitsadmin', 'reg', 'net', 'sc',
|
|
69
|
+
];
|
|
70
|
+
function regString(key, value) {
|
|
71
|
+
try {
|
|
72
|
+
const out = (0, child_process_1.execFileSync)('reg', ['query', key, '/v', value], {
|
|
73
|
+
encoding: 'utf8', windowsHide: true, timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
74
|
+
});
|
|
75
|
+
const match = out.match(new RegExp(`${value}\\s+REG_(?:EXPAND_)?SZ\\s+(.+)`));
|
|
76
|
+
return match ? match[1].trim() : undefined;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function setRegString(key, valueName, data) {
|
|
83
|
+
(0, child_process_1.execFileSync)('reg', ['add', key, '/v', valueName, '/t', 'REG_SZ', '/d', data, '/f'], {
|
|
84
|
+
windowsHide: true, timeout: 10000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function batQuote(value) {
|
|
88
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
89
|
+
}
|
|
90
|
+
function buildGuardJs(nodePath, cliEntry) {
|
|
91
|
+
return [
|
|
92
|
+
`'use strict';`,
|
|
93
|
+
`const fs=require('fs');const os=require('os');const path=require('path');const{spawnSync}=require('child_process');const crypto=require('crypto');`,
|
|
94
|
+
`const RULES_PATH=${JSON.stringify(GUARD_RULES_PATH)};`,
|
|
95
|
+
`const SPOOL_PATH=${JSON.stringify(path.join(os.homedir(), '.fullcourtdefense-spool.jsonl'))};`,
|
|
96
|
+
`const NODE_PATH=${JSON.stringify(nodePath)};`,
|
|
97
|
+
`const CLI_ENTRY=${JSON.stringify(cliEntry)};`,
|
|
98
|
+
`function loadRules(){try{const raw=JSON.parse(fs.readFileSync(RULES_PATH,'utf8'));const rules=(raw.rules||[]).map(r=>{try{return{id:r.id,category:r.category,reason:r.reason,source:r.source,re:new RegExp(r.pattern,'i')};}catch{return null;}}).filter(Boolean);return{mode:raw.mode==='monitor'?'monitor':'block',rules};}catch{return{mode:'block',rules:[]};}}`,
|
|
99
|
+
`function matchLine(line,rules){const n=line.replace(/\\s+/g,' ').trim();for(const r of rules){try{if(r.re.test(n)||r.re.test(line))return r;}catch{}}return null;}`,
|
|
100
|
+
`function spoolEvent(rule,line,decision){try{const ev=line.trim();const evidence=ev.length>180?ev.slice(0,180)+'...':ev;const event={eventId:crypto.randomUUID(),type:'verdict',decision,toolName:'cmd_terminal',operation:'shell_command',reason:'Shell guard: '+rule.reason,ruleId:rule.id,category:rule.category,source:rule.source,evidence,occurredAt:new Date().toISOString()};fs.appendFileSync(SPOOL_PATH,JSON.stringify(event)+'\\n',{encoding:'utf8',mode:0o600});if(fs.existsSync(NODE_PATH)&&fs.existsSync(CLI_ENTRY)){spawnSync(NODE_PATH,[CLI_ENTRY,'flush-spool','--heartbeat','true'],{stdio:'ignore',windowsHide:true});}}catch{}}`,
|
|
101
|
+
`function delegate(args){const r=spawnSync(process.env.ComSpec||'cmd.exe',['/d','/c',...args],{stdio:'inherit',windowsHide:true});process.exit(typeof r.status==='number'?r.status:1);}`,
|
|
102
|
+
`const args=process.argv.slice(2);if(!args.length)process.exit(0);if(process.env.FCD_CMD_GUARD==='off')delegate(args);`,
|
|
103
|
+
`const line=args.join(' ');const{mode,rules}=loadRules();const hit=matchLine(line,rules);`,
|
|
104
|
+
`if(!hit)delegate(args);`,
|
|
105
|
+
`if(mode==='monitor'){console.error('[FullCourtDefense] monitor: would block - '+hit.reason+' (rule '+hit.id+')');spoolEvent(hit,line,'allow');delegate(args);}`,
|
|
106
|
+
`console.error('[FullCourtDefense] BLOCKED: '+hit.reason+' (rule '+hit.id+')');`,
|
|
107
|
+
`console.error('This command was not executed. Reported to your security dashboard.');`,
|
|
108
|
+
`spoolEvent(hit,line,'block');process.exit(1);`,
|
|
109
|
+
``,
|
|
110
|
+
].join('\n');
|
|
111
|
+
}
|
|
112
|
+
function buildAutorunBat(nodePath) {
|
|
113
|
+
const guardJs = batQuote(GUARD_JS_PATH);
|
|
114
|
+
const node = batQuote(nodePath);
|
|
115
|
+
const doskeyLines = INTERCEPTED_COMMANDS.map(cmd => `doskey ${cmd}=${node} ${guardJs} ${cmd} $*`);
|
|
116
|
+
return [
|
|
117
|
+
'@echo off',
|
|
118
|
+
'REM FullCourtDefense cmd.exe guard (installed by fullcourtdefense-cli).',
|
|
119
|
+
'REM Disable for one session: set FCD_CMD_GUARD=off',
|
|
120
|
+
'REM Remove permanently: fullcourtdefense uninstall-cmd-guard',
|
|
121
|
+
'if defined FCD_CMD_AUTORUN_DONE goto :eof',
|
|
122
|
+
'set FCD_CMD_AUTORUN_DONE=1',
|
|
123
|
+
...doskeyLines,
|
|
124
|
+
'',
|
|
125
|
+
].join('\r\n');
|
|
126
|
+
}
|
|
127
|
+
function readCurrentAutorun() {
|
|
128
|
+
return regString(CMD_AUTORUN_KEY, AUTORUN_VALUE) || '';
|
|
129
|
+
}
|
|
130
|
+
function mergeAutorunValue(current) {
|
|
131
|
+
const ours = batQuote(AUTORUN_BAT_PATH);
|
|
132
|
+
const cleaned = stripAutorunValue(current);
|
|
133
|
+
if (!cleaned.trim())
|
|
134
|
+
return ours;
|
|
135
|
+
if (cleaned.includes(AUTORUN_MARKER))
|
|
136
|
+
return ours;
|
|
137
|
+
return `${ours} & ${cleaned}`;
|
|
138
|
+
}
|
|
139
|
+
function stripAutorunValue(current) {
|
|
140
|
+
const ours = batQuote(AUTORUN_BAT_PATH);
|
|
141
|
+
let next = current
|
|
142
|
+
.replace(new RegExp(`${ours.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*&\\s*`, 'gi'), '')
|
|
143
|
+
.replace(new RegExp(`\\s*&\\s*${ours.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'gi'), '')
|
|
144
|
+
.replace(ours, '')
|
|
145
|
+
.replace(/^\s*&\s*|\s*&\s*$/g, '')
|
|
146
|
+
.trim();
|
|
147
|
+
return next;
|
|
148
|
+
}
|
|
149
|
+
function isCmdGuardInstalled() {
|
|
150
|
+
try {
|
|
151
|
+
if (process.platform !== 'win32')
|
|
152
|
+
return false;
|
|
153
|
+
if (!fs.existsSync(GUARD_JS_PATH) || !fs.existsSync(AUTORUN_BAT_PATH))
|
|
154
|
+
return false;
|
|
155
|
+
return readCurrentAutorun().includes(AUTORUN_MARKER);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function getCmdGuardStatus() {
|
|
162
|
+
if (process.platform !== 'win32') {
|
|
163
|
+
return { supported: false, installed: false, rulesPresent: false };
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
let ruleCount;
|
|
167
|
+
let mode;
|
|
168
|
+
let rulesPresent = false;
|
|
169
|
+
try {
|
|
170
|
+
const parsed = JSON.parse(fs.readFileSync(GUARD_RULES_PATH, 'utf8'));
|
|
171
|
+
rulesPresent = Array.isArray(parsed?.rules) && parsed.rules.length > 0;
|
|
172
|
+
ruleCount = Array.isArray(parsed?.rules) ? parsed.rules.length : undefined;
|
|
173
|
+
mode = typeof parsed?.mode === 'string' ? parsed.mode : undefined;
|
|
174
|
+
}
|
|
175
|
+
catch { /* not written yet */ }
|
|
176
|
+
const autorunValue = readCurrentAutorun();
|
|
177
|
+
return {
|
|
178
|
+
supported: true,
|
|
179
|
+
installed: isCmdGuardInstalled(),
|
|
180
|
+
autorunValue: autorunValue || undefined,
|
|
181
|
+
rulesPresent,
|
|
182
|
+
ruleCount,
|
|
183
|
+
mode,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return { supported: true, installed: false, rulesPresent: false };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/** `fullcourtdefense install-cmd-guard` — enable interactive cmd.exe blocking. */
|
|
191
|
+
async function installCmdGuardCommand(_args = {}) {
|
|
192
|
+
if (process.platform !== 'win32') {
|
|
193
|
+
console.log('The cmd.exe guard currently supports Windows only.');
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const rules = (0, shellGuard_1.writeShellGuardRules)();
|
|
197
|
+
const nodePath = process.execPath;
|
|
198
|
+
const cliEntry = process.argv[1] || '';
|
|
199
|
+
fs.writeFileSync(GUARD_JS_PATH, buildGuardJs(nodePath, cliEntry), 'utf8');
|
|
200
|
+
fs.writeFileSync(AUTORUN_BAT_PATH, buildAutorunBat(nodePath), 'utf8');
|
|
201
|
+
console.log(`Guard rules written: ${rules.rules.length} rule(s), mode "${rules.mode}".`);
|
|
202
|
+
const current = readCurrentAutorun();
|
|
203
|
+
const merged = mergeAutorunValue(current);
|
|
204
|
+
setRegString(CMD_AUTORUN_KEY, AUTORUN_VALUE, merged);
|
|
205
|
+
console.log(`\x1b[32m✓ cmd.exe guard installed\x1b[0m \x1b[2m(AutoRun + doskey macros)\x1b[0m`);
|
|
206
|
+
console.log('\x1b[2mOpen a NEW Command Prompt window (prompt must be C:\\...> not PS C:\\...>). Dangerous commands like del /s /q C:\\ are blocked and reported.\x1b[0m');
|
|
207
|
+
}
|
|
208
|
+
/** `fullcourtdefense uninstall-cmd-guard` */
|
|
209
|
+
async function uninstallCmdGuardCommand() {
|
|
210
|
+
if (process.platform !== 'win32') {
|
|
211
|
+
console.log('Nothing to remove on this OS.');
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const current = readCurrentAutorun();
|
|
215
|
+
const next = stripAutorunValue(current);
|
|
216
|
+
if (next) {
|
|
217
|
+
setRegString(CMD_AUTORUN_KEY, AUTORUN_VALUE, next);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
try {
|
|
221
|
+
(0, child_process_1.execFileSync)('reg', ['delete', CMD_AUTORUN_KEY, '/v', AUTORUN_VALUE, '/f'], {
|
|
222
|
+
windowsHide: true, timeout: 10000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
catch { /* value may already be absent */ }
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
fs.unlinkSync(GUARD_JS_PATH);
|
|
229
|
+
}
|
|
230
|
+
catch { /* not present */ }
|
|
231
|
+
try {
|
|
232
|
+
fs.unlinkSync(AUTORUN_BAT_PATH);
|
|
233
|
+
}
|
|
234
|
+
catch { /* not present */ }
|
|
235
|
+
console.log('cmd.exe guard uninstalled. Open cmd windows keep doskey until closed.');
|
|
236
|
+
}
|
|
237
|
+
/** Refresh shared rules JSON when cmd guard is installed. */
|
|
238
|
+
function refreshCmdGuardRules() {
|
|
239
|
+
try {
|
|
240
|
+
if (!isCmdGuardInstalled())
|
|
241
|
+
return;
|
|
242
|
+
(0, shellGuard_1.writeShellGuardRules)();
|
|
243
|
+
}
|
|
244
|
+
catch { /* best-effort */ }
|
|
245
|
+
}
|
|
@@ -11,6 +11,7 @@ const autoProtect_1 = require("./autoProtect");
|
|
|
11
11
|
const restartNotice_1 = require("./restartNotice");
|
|
12
12
|
const windowsAudit_1 = require("./windowsAudit");
|
|
13
13
|
const shellGuard_1 = require("./shellGuard");
|
|
14
|
+
const cmdGuard_1 = require("./cmdGuard");
|
|
14
15
|
/**
|
|
15
16
|
* One-click install: discover/wrap every existing MCP server, install Cursor
|
|
16
17
|
* hooks for built-in Cursor actions, optional self-heal and fleet upload.
|
|
@@ -106,6 +107,16 @@ async function installAllCommand(args, config) {
|
|
|
106
107
|
console.log(`\x1b[33m⚠ Warning: shell guard install failed (${error instanceof Error ? error.message : String(error)}). Installation continues.\x1b[0m`);
|
|
107
108
|
}
|
|
108
109
|
}
|
|
110
|
+
// Interactive cmd.exe guard — same rules, doskey + AutoRun hook (no admin).
|
|
111
|
+
if (process.platform === 'win32' && args.cmdGuard !== 'false') {
|
|
112
|
+
console.log('\n\x1b[1mInstalling interactive cmd.exe shell guard…\x1b[0m');
|
|
113
|
+
try {
|
|
114
|
+
await (0, cmdGuard_1.installCmdGuardCommand)();
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.log(`\x1b[33m⚠ Warning: cmd guard install failed (${error instanceof Error ? error.message : String(error)}). Installation continues.\x1b[0m`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
109
120
|
if (args.autoProtect === 'true') {
|
|
110
121
|
console.log('\n\x1b[1mScheduling self-healing MCP protection…\x1b[0m');
|
|
111
122
|
await (0, autoProtect_1.autoProtectCommand)({
|
|
@@ -67,7 +67,7 @@ const path = __importStar(require("path"));
|
|
|
67
67
|
*
|
|
68
68
|
* Scope honesty: this covers INTERACTIVE PowerShell (PSReadLine hosts, incl.
|
|
69
69
|
* IDE-integrated terminals). Non-interactive scripts are covered by ScriptBlock
|
|
70
|
-
* Logging (windowsAudit.ts)
|
|
70
|
+
* Logging (windowsAudit.ts). Native cmd.exe is covered separately (cmdGuard.ts).
|
|
71
71
|
*/
|
|
72
72
|
const GUARD_RULES_PATH = path.join(os.homedir(), '.fullcourtdefense-shell-guard.json');
|
|
73
73
|
const GUARD_PS1_PATH = path.join(os.homedir(), '.fullcourtdefense-shell-guard.ps1');
|
|
@@ -87,7 +87,9 @@ const BUILTIN_RULES = [
|
|
|
87
87
|
{ id: 'rm_rf_root', category: 'destructive_command', reason: 'recursive force delete of filesystem root', source: 'builtin',
|
|
88
88
|
pattern: String.raw `\b(?:sudo\s+)?rm\s+-[a-z]*r[a-z]*f[a-z]*\s+(?:["']?/["']?|\*)(?:\s|$|[;&|])` },
|
|
89
89
|
{ id: 'windows_drive_delete', category: 'destructive_command', reason: 'recursive Windows drive delete', source: 'builtin',
|
|
90
|
-
pattern: String.raw `\b(?:del|erase)\s+/[a-z]*s[a-z]*\s+/[a-z]*q[a-z]*\s+[a-z]
|
|
90
|
+
pattern: String.raw `\b(?:del|erase)\s+/[a-z]*s[a-z]*\s+/[a-z]*q[a-z]*\s+[a-z]:\\?(?:\s|$|[\\/])` },
|
|
91
|
+
{ id: 'windows_drive_rmdir', category: 'destructive_command', reason: 'recursive Windows drive remove directory', source: 'builtin',
|
|
92
|
+
pattern: String.raw `\b(?:rd|rmdir)\s+/[a-z]*s[a-z]*\s+/[a-z]*q[a-z]*\s+[a-z]:\\?(?:\s|$|[\\/])` },
|
|
91
93
|
{ id: 'windows_drive_delete', category: 'destructive_command', reason: 'recursive Remove-Item on a drive root', source: 'builtin',
|
|
92
94
|
pattern: String.raw `\bremove-item\b(?=.*-recurse)(?=.*-force)(?=.*\s[a-z]:[\\/]?(?:\s|$))` },
|
|
93
95
|
{ id: 'format_drive', category: 'destructive_command', reason: 'drive format command', source: 'builtin',
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,7 @@ const installAll_1 = require("./commands/installAll");
|
|
|
52
52
|
const autoProtect_1 = require("./commands/autoProtect");
|
|
53
53
|
const windowsAudit_1 = require("./commands/windowsAudit");
|
|
54
54
|
const shellGuard_1 = require("./commands/shellGuard");
|
|
55
|
+
const cmdGuard_1 = require("./commands/cmdGuard");
|
|
55
56
|
const fs = __importStar(require("fs"));
|
|
56
57
|
const path = __importStar(require("path"));
|
|
57
58
|
function readCliVersion() {
|
|
@@ -130,6 +131,8 @@ function printHelp() {
|
|
|
130
131
|
install-shell-guard Block dangerous commands typed in PowerShell terminals
|
|
131
132
|
(real-time, offline rule cache). uninstall-shell-guard removes it;
|
|
132
133
|
shell-guard-status shows coverage.
|
|
134
|
+
install-cmd-guard Block dangerous commands typed in cmd.exe (Command Prompt).
|
|
135
|
+
uninstall-cmd-guard removes it; cmd-guard-status shows coverage.
|
|
133
136
|
scan Runs the scan. Use --local for inside-organization scans.
|
|
134
137
|
discover Finds MCP servers, local secrets, agent rules/skills, and machine
|
|
135
138
|
blast radius on this laptop. Use --surface all --upload for AI Fleet.
|
|
@@ -600,6 +603,8 @@ async function main() {
|
|
|
600
603
|
autoProtect: flags['auto-protect'],
|
|
601
604
|
intervalMinutes: flags['interval-minutes'],
|
|
602
605
|
windowsAudit: flags['windows-audit'],
|
|
606
|
+
shellGuard: flags['shell-guard'],
|
|
607
|
+
cmdGuard: flags['cmd-guard'],
|
|
603
608
|
};
|
|
604
609
|
await (0, installAll_1.installAllCommand)(args, config);
|
|
605
610
|
break;
|
|
@@ -632,6 +637,28 @@ async function main() {
|
|
|
632
637
|
console.log('');
|
|
633
638
|
break;
|
|
634
639
|
}
|
|
640
|
+
case 'install-cmd-guard': {
|
|
641
|
+
await (0, cmdGuard_1.installCmdGuardCommand)();
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
case 'uninstall-cmd-guard': {
|
|
645
|
+
await (0, cmdGuard_1.uninstallCmdGuardCommand)();
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
case 'cmd-guard-status': {
|
|
649
|
+
const status = (0, cmdGuard_1.getCmdGuardStatus)();
|
|
650
|
+
if (!status.supported) {
|
|
651
|
+
console.log('cmd guard supports Windows Command Prompt only.');
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
console.log(`\nInteractive cmd.exe shell guard: ${status.installed ? '\x1b[32minstalled\x1b[0m' : '\x1b[31mnot installed\x1b[0m'}`);
|
|
655
|
+
if (status.autorunValue)
|
|
656
|
+
console.log(` AutoRun: \x1b[2m${status.autorunValue}\x1b[0m`);
|
|
657
|
+
if (status.rulesPresent)
|
|
658
|
+
console.log(` Rules: ${status.ruleCount} (mode: ${status.mode || 'block'})`);
|
|
659
|
+
console.log('');
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
635
662
|
case 'install-mcp-gateway': {
|
|
636
663
|
await (0, mcpGateway_1.installMcpGatewayCommand)(buildInstallArgs(), config);
|
|
637
664
|
break;
|
package/dist/telemetry.js
CHANGED
|
@@ -44,6 +44,7 @@ const path = __importStar(require("path"));
|
|
|
44
44
|
const machineIdentity_1 = require("./machineIdentity");
|
|
45
45
|
const windowsAudit_1 = require("./commands/windowsAudit");
|
|
46
46
|
const shellGuard_1 = require("./commands/shellGuard");
|
|
47
|
+
const cmdGuard_1 = require("./commands/cmdGuard");
|
|
47
48
|
/**
|
|
48
49
|
* Local-first telemetry: every enforcement decision is appended to an on-disk
|
|
49
50
|
* spool (instant, offline-safe) and flushed to the backend in batches. Critical
|
|
@@ -110,9 +111,10 @@ function rewriteSpool(remaining) {
|
|
|
110
111
|
}
|
|
111
112
|
/** Drain the spool to the backend in one batch (+ optional heartbeat). Returns accepted count. */
|
|
112
113
|
async function flushSpool(input) {
|
|
113
|
-
// Piggyback on the flush cycle to re-sync
|
|
114
|
-
//
|
|
114
|
+
// Piggyback on the flush cycle to re-sync terminal guard rule caches (mode flips
|
|
115
|
+
// + new custom rules reach open shells within ~30s).
|
|
115
116
|
(0, shellGuard_1.refreshShellGuardRules)();
|
|
117
|
+
(0, cmdGuard_1.refreshCmdGuardRules)();
|
|
116
118
|
const all = readSpool();
|
|
117
119
|
const batch = all.slice(0, MAX_BATCH);
|
|
118
120
|
const overflow = all.slice(MAX_BATCH);
|
package/dist/version.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fullcourtdefense-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"test:taint-ledger": "npm run build && node scripts/test-taint-ledger.js",
|
|
19
19
|
"test:shell-audit": "npm run build && node scripts/test-shell-audit.js",
|
|
20
20
|
"test:shell-guard": "npm run build && node scripts/test-shell-guard.js",
|
|
21
|
+
"test:cmd-guard": "npm run build && node scripts/test-cmd-guard.js",
|
|
21
22
|
"test:ping-e2e": "npm run build && node scripts/test-ping-e2e.js",
|
|
22
23
|
"prepublishOnly": "npm run build"
|
|
23
24
|
},
|