agent-security-scanner-mcp 3.9.0 → 3.10.1

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.
@@ -0,0 +1,443 @@
1
+ # ClawHavoc Malware Signature Rules
2
+ # ===================================
3
+ # ClawHavoc is a threat intelligence database for detecting known malicious
4
+ # patterns in OpenClaw skills. These signatures cover reverse shells, crypto
5
+ # miners, info stealers, keyloggers, C2 beacons, and OpenClaw-specific attacks
6
+ # observed in the wild. Each rule maps to a MITRE ATT&CK technique or known
7
+ # malware family. Patterns are JavaScript-compatible regex (used with RegExp).
8
+ #
9
+ # Maintained by the ClawProof security team.
10
+ # Last updated: 2026-02-18
11
+
12
+ rules:
13
+ # ==========================================================================
14
+ # CATEGORY 1: REVERSE SHELLS
15
+ # ==========================================================================
16
+
17
+ - id: clawhavoc.revshell.bash
18
+ severity: CRITICAL
19
+ message: "Bash reverse shell detected — opens interactive shell over TCP"
20
+ patterns:
21
+ - "bash\\s+-i\\s+>&\\s*/dev/tcp/"
22
+ - "/dev/tcp/\\d+\\.\\d+\\.\\d+\\.\\d+/\\d+"
23
+ - "bash\\s+.*>\\s*/dev/tcp/.*<&"
24
+ - "0<&\\d+;\\s*exec\\s+\\d+<>/dev/tcp/"
25
+ metadata:
26
+ category: reverse_shell
27
+ action: BLOCK
28
+ confidence: HIGH
29
+ malware_family: generic
30
+
31
+ - id: clawhavoc.revshell.netcat
32
+ severity: CRITICAL
33
+ message: "Netcat reverse shell detected — executes shell via nc/ncat"
34
+ patterns:
35
+ - "\\bnc\\s+-e\\s+/bin/(ba)?sh"
36
+ - "\\bncat\\s+-e\\s+/bin/(ba)?sh"
37
+ - "\\bnetcat\\s+-e\\s+/bin/(ba)?sh"
38
+ - "\\bnc\\b.*\\|\\s*/bin/(ba)?sh"
39
+ - "mkfifo\\s+/tmp/.*\\bnc\\b"
40
+ metadata:
41
+ category: reverse_shell
42
+ action: BLOCK
43
+ confidence: HIGH
44
+ malware_family: generic
45
+
46
+ - id: clawhavoc.revshell.python
47
+ severity: CRITICAL
48
+ message: "Python reverse shell detected — socket connect with dup2/subprocess"
49
+ patterns:
50
+ - "socket\\.connect\\s*\\(\\s*\\([\"']\\d+\\.\\d+\\.\\d+\\.\\d+[\"']"
51
+ - "os\\.dup2\\s*\\(\\s*s\\.fileno\\(\\)"
52
+ - "subprocess\\.call\\s*\\(\\s*\\[.*?/bin/(ba)?sh.*?\\].*?stdin\\s*=\\s*s"
53
+ - "socket\\.socket\\(.*?SOCK_STREAM\\).*?\\.connect\\(.*?\\).*?os\\.dup2"
54
+ metadata:
55
+ category: reverse_shell
56
+ action: BLOCK
57
+ confidence: HIGH
58
+ malware_family: generic
59
+
60
+ - id: clawhavoc.revshell.perl
61
+ severity: CRITICAL
62
+ message: "Perl reverse shell detected — socket INET with exec"
63
+ patterns:
64
+ - "perl\\s+-e\\s+.*socket\\s*\\(\\s*S.*INET"
65
+ - "perl\\s+-e\\s+.*exec\\s*\\(.*?/bin/(ba)?sh"
66
+ - "IO::Socket::INET.*?\\bexec\\b.*?/bin/(ba)?sh"
67
+ - "perl\\s+.*?socket.*?connect.*?open.*?STDIN"
68
+ metadata:
69
+ category: reverse_shell
70
+ action: BLOCK
71
+ confidence: HIGH
72
+ malware_family: generic
73
+
74
+ - id: clawhavoc.revshell.ruby
75
+ severity: CRITICAL
76
+ message: "Ruby reverse shell detected — TCPSocket with exec"
77
+ patterns:
78
+ - "TCPSocket\\.(new|open)\\s*\\(.*?\\d+\\.\\d+\\.\\d+\\.\\d+"
79
+ - "TCPSocket\\.open.*?exec\\s+.*?/bin/(ba)?sh"
80
+ - "require\\s+[\"']socket[\"'].*?TCPSocket.*?exec"
81
+ metadata:
82
+ category: reverse_shell
83
+ action: BLOCK
84
+ confidence: HIGH
85
+ malware_family: generic
86
+
87
+ # ==========================================================================
88
+ # CATEGORY 2: CRYPTO MINERS
89
+ # ==========================================================================
90
+
91
+ - id: clawhavoc.miner.xmrig
92
+ severity: CRITICAL
93
+ message: "XMRig crypto miner detected — mines Monero using victim resources"
94
+ patterns:
95
+ - "\\bxmrig\\b"
96
+ - "stratum\\+tcp://"
97
+ - "pool\\.minexmr\\.com"
98
+ - "pool\\.hashvault\\.pro"
99
+ - "\\bRandomX\\b.*?--donate-level"
100
+ metadata:
101
+ category: crypto_miner
102
+ action: BLOCK
103
+ confidence: HIGH
104
+ malware_family: xmrig
105
+
106
+ - id: clawhavoc.miner.coinhive
107
+ severity: CRITICAL
108
+ message: "Browser-based crypto miner detected — mines via WebAssembly in browser"
109
+ patterns:
110
+ - "\\bcoinhive\\b"
111
+ - "CoinHive\\.Anonymous"
112
+ - "\\bwebminepool\\b"
113
+ - "coin-hive\\.com/lib"
114
+ - "\\bCryptoLoot\\b"
115
+ metadata:
116
+ category: crypto_miner
117
+ action: BLOCK
118
+ confidence: HIGH
119
+ malware_family: coinhive
120
+
121
+ # ==========================================================================
122
+ # CATEGORY 3: INFO STEALERS
123
+ # ==========================================================================
124
+
125
+ - id: clawhavoc.stealer.browser-cookies
126
+ severity: CRITICAL
127
+ message: "Browser cookie/credential theft detected — reads stored login data"
128
+ patterns:
129
+ - "Chrome/User\\s*Data/Default/Cookies"
130
+ - "Firefox/Profiles/.*?cookies\\.sqlite"
131
+ - "Chrome/User\\s*Data/Default/Login\\s*Data"
132
+ - "Google/Chrome.*?(Cookies|Login\\s*Data)"
133
+ - "Application\\s*Support/(Google/Chrome|Firefox).*?(cookies|login)"
134
+ metadata:
135
+ category: info_stealer
136
+ action: BLOCK
137
+ confidence: HIGH
138
+ malware_family: generic
139
+
140
+ - id: clawhavoc.stealer.keychain
141
+ severity: CRITICAL
142
+ message: "macOS Keychain theft detected — dumps stored credentials"
143
+ patterns:
144
+ - "security\\s+find-generic-password"
145
+ - "security\\s+find-internet-password"
146
+ - "security\\s+dump-keychain"
147
+ - "security\\s+export\\s+-k\\s+.*?keychain"
148
+ metadata:
149
+ category: info_stealer
150
+ action: BLOCK
151
+ confidence: HIGH
152
+ malware_family: generic
153
+
154
+ - id: clawhavoc.stealer.atomic
155
+ severity: CRITICAL
156
+ message: "Atomic Stealer pattern detected — osascript password prompt"
157
+ patterns:
158
+ - "osascript.*?display\\s+dialog.*?password"
159
+ - "osascript.*?display\\s+dialog.*?with\\s+hidden\\s+answer"
160
+ - "osascript\\s+-e.*?text\\s+returned.*?password"
161
+ metadata:
162
+ category: info_stealer
163
+ action: BLOCK
164
+ confidence: HIGH
165
+ malware_family: atomic_stealer
166
+ campaign: macos_atomic
167
+
168
+ - id: clawhavoc.stealer.redline
169
+ severity: CRITICAL
170
+ message: "RedLine stealer pattern detected — targets browser and system credentials"
171
+ patterns:
172
+ - "\\\\Browsers\\\\.*?Login\\s*Data"
173
+ - "\\bvaultcli\\.dll\\b"
174
+ - "\\bCredentialManager\\b"
175
+ - "VaultOpenVault|VaultEnumerateItems"
176
+ - "\\\\User\\s*Data\\\\.*?(Web\\s*Data|Login\\s*Data)"
177
+ metadata:
178
+ category: info_stealer
179
+ action: BLOCK
180
+ confidence: HIGH
181
+ malware_family: redline
182
+
183
+ - id: clawhavoc.stealer.lumma
184
+ severity: CRITICAL
185
+ message: "Lumma/wallet stealer pattern detected — targets crypto wallets"
186
+ patterns:
187
+ - "\\bwallet\\.dat\\b"
188
+ - "Exodus.*?passphrase"
189
+ - "metamask.*?vault"
190
+ - "\\bsollet\\b.*?keystore"
191
+ - "Ethereum/keystore"
192
+ metadata:
193
+ category: info_stealer
194
+ action: BLOCK
195
+ confidence: MEDIUM
196
+ malware_family: lumma
197
+
198
+ # ==========================================================================
199
+ # CATEGORY 4: KEYLOGGERS
200
+ # ==========================================================================
201
+
202
+ - id: clawhavoc.keylogger.macos
203
+ severity: CRITICAL
204
+ message: "macOS keylogger detected — captures keystrokes via system APIs"
205
+ patterns:
206
+ - "\\bCGEventTapCreate\\b"
207
+ - "\\bkCGEventKeyDown\\b"
208
+ - "NSEvent\\.addGlobalMonitor"
209
+ - "CGEventMaskBit\\(.*?kCGEventKeyDown"
210
+ metadata:
211
+ category: keylogger
212
+ action: BLOCK
213
+ confidence: HIGH
214
+ malware_family: generic
215
+
216
+ - id: clawhavoc.keylogger.generic
217
+ severity: HIGH
218
+ message: "Generic keylogger pattern detected — captures keyboard input"
219
+ patterns:
220
+ - "\\bkeylogger\\b"
221
+ - "pynput\\.keyboard\\.Listener"
222
+ - "addEventListener\\s*\\(\\s*[\"']keydown[\"']"
223
+ - "on_press.*?keyboard.*?listener"
224
+ - "SetWindowsHookEx.*?WH_KEYBOARD"
225
+ metadata:
226
+ category: keylogger
227
+ action: BLOCK
228
+ confidence: MEDIUM
229
+ malware_family: generic
230
+
231
+ # ==========================================================================
232
+ # CATEGORY 5: SCREEN CAPTURE
233
+ # ==========================================================================
234
+
235
+ - id: clawhavoc.screencap.capture-upload
236
+ severity: HIGH
237
+ message: "Screenshot capture with upload detected — exfiltrates screen content"
238
+ patterns:
239
+ - "screenshot.*?upload"
240
+ - "screencapture.*?curl"
241
+ - "ImageGrab.*?(send|post|upload|requests)"
242
+ - "pyautogui\\.screenshot.*?(requests|urllib|http)"
243
+ - "screen\\s*capture.*?(webhook|http|curl)"
244
+ metadata:
245
+ category: screen_capture
246
+ action: BLOCK
247
+ confidence: MEDIUM
248
+ malware_family: generic
249
+
250
+ # ==========================================================================
251
+ # CATEGORY 6: DNS EXFILTRATION
252
+ # ==========================================================================
253
+
254
+ - id: clawhavoc.exfil.dns
255
+ severity: CRITICAL
256
+ message: "DNS exfiltration detected — encodes data in DNS queries"
257
+ patterns:
258
+ - "nslookup\\s+\\$\\("
259
+ - "dig\\s+\\$\\("
260
+ - "base64.*?nslookup"
261
+ - "nslookup.*?base64"
262
+ - "\\$\\(.*?\\)\\..*?\\.\\w{2,6}\\b"
263
+ metadata:
264
+ category: dns_exfiltration
265
+ action: BLOCK
266
+ confidence: HIGH
267
+ malware_family: generic
268
+
269
+ # ==========================================================================
270
+ # CATEGORY 7: C2 BEACONS
271
+ # ==========================================================================
272
+
273
+ - id: clawhavoc.c2.periodic-http
274
+ severity: CRITICAL
275
+ message: "C2 beacon detected — periodic HTTP callbacks for command and control"
276
+ patterns:
277
+ - "setInterval\\s*\\(.*?fetch\\s*\\("
278
+ - "while\\s+(True|true|1).*?requests\\.(get|post).*?sleep"
279
+ - "setInterval\\s*\\(.*?(axios|http|XMLHttpRequest)"
280
+ - "schedule\\.every\\(.*?\\)\\.(get|post|fetch)"
281
+ metadata:
282
+ category: c2_beacon
283
+ action: BLOCK
284
+ confidence: HIGH
285
+ malware_family: generic
286
+
287
+ - id: clawhavoc.c2.encoded-payload
288
+ severity: CRITICAL
289
+ message: "Encoded payload execution detected — decodes and executes hidden code"
290
+ patterns:
291
+ - "eval\\s*\\(\\s*atob\\s*\\("
292
+ - "exec\\s*\\(\\s*base64\\.b64decode\\s*\\("
293
+ - "eval\\s*\\(\\s*Buffer\\.from\\s*\\(.*?[\"']base64[\"']"
294
+ - "new\\s+Function\\s*\\(\\s*atob\\s*\\("
295
+ - "exec\\s*\\(\\s*codecs\\.decode\\s*\\("
296
+ metadata:
297
+ category: c2_beacon
298
+ action: BLOCK
299
+ confidence: HIGH
300
+ malware_family: generic
301
+
302
+ # ==========================================================================
303
+ # CATEGORY 8: OPENCLAW-SPECIFIC ATTACKS
304
+ # ==========================================================================
305
+
306
+ - id: clawhavoc.openclaw.soul-tampering
307
+ severity: CRITICAL
308
+ message: "SOUL.md tampering detected — modifies the agent's core identity/instructions"
309
+ patterns:
310
+ - "(write|echo|cat|tee)\\s+.*?>\\s*SOUL\\.md"
311
+ - "modify\\s+.*?SOUL\\.md"
312
+ - "sed\\s+-i.*?SOUL\\.md"
313
+ - "echo\\s+.*?>>\\s*SOUL\\.md"
314
+ - "(open|fs\\.writeFile).*?SOUL\\.md"
315
+ metadata:
316
+ category: openclaw_attack
317
+ action: BLOCK
318
+ confidence: HIGH
319
+ malware_family: openclaw_specific
320
+
321
+ - id: clawhavoc.openclaw.config-theft
322
+ severity: CRITICAL
323
+ message: "OpenClaw config theft detected — reads private agent configuration"
324
+ patterns:
325
+ - "~/\\.openclaw/"
326
+ - "\\$HOME/\\.openclaw"
327
+ - "cat\\s+.*?\\.openclaw"
328
+ - "read.*?\\.openclaw/config"
329
+ - "\\.openclaw/(credentials|tokens?|auth)"
330
+ metadata:
331
+ category: openclaw_attack
332
+ action: BLOCK
333
+ confidence: HIGH
334
+ malware_family: openclaw_specific
335
+
336
+ - id: clawhavoc.openclaw.session-hijack
337
+ severity: CRITICAL
338
+ message: "OpenClaw session hijack detected — steals authentication tokens"
339
+ patterns:
340
+ - "openclaw\\s+session\\s+token"
341
+ - "openclaw.*?auth\\s*cookie"
342
+ - "OPENCLAW_SESSION"
343
+ - "openclaw.*?bearer\\s+token"
344
+ - "steal.*?openclaw.*?(session|token|auth)"
345
+ metadata:
346
+ category: openclaw_attack
347
+ action: BLOCK
348
+ confidence: HIGH
349
+ malware_family: openclaw_specific
350
+
351
+ - id: clawhavoc.openclaw.skill-mutation
352
+ severity: CRITICAL
353
+ message: "SKILL.md self-mutation detected — skill modifies its own definition"
354
+ patterns:
355
+ - "(write|echo|cat|tee)\\s+.*?>\\s*SKILL\\.md"
356
+ - "echo\\s+.*?>>\\s*SKILL\\.md"
357
+ - "sed\\s+-i.*?SKILL\\.md"
358
+ - "modify\\s+.*?SKILL\\.md"
359
+ - "(open|fs\\.writeFile).*?SKILL\\.md"
360
+ metadata:
361
+ category: openclaw_attack
362
+ action: BLOCK
363
+ confidence: HIGH
364
+ malware_family: openclaw_specific
365
+
366
+ - id: clawhavoc.openclaw.gateway-token
367
+ severity: CRITICAL
368
+ message: "OpenClaw gateway token theft detected — targets API authentication"
369
+ patterns:
370
+ - "OPENCLAW_GATEWAY_TOKEN"
371
+ - "OPENCLAW_API_KEY"
372
+ - "OPENCLAW_SECRET"
373
+ - "openclaw.*?gateway.*?token"
374
+ - "export\\s+OPENCLAW_(GATEWAY_TOKEN|API_KEY)"
375
+ metadata:
376
+ category: openclaw_attack
377
+ action: BLOCK
378
+ confidence: HIGH
379
+ malware_family: openclaw_specific
380
+
381
+ # ==========================================================================
382
+ # CATEGORY 9: CAMPAIGN PATTERNS
383
+ # ==========================================================================
384
+
385
+ - id: clawhavoc.campaign.osascript-dialog
386
+ severity: HIGH
387
+ message: "osascript dialog social engineering — tricks user into entering credentials"
388
+ patterns:
389
+ - "osascript.*?display\\s+dialog"
390
+ - "osascript\\s+-e\\s+.*?display\\s+alert"
391
+ - "osascript.*?text\\s+returned"
392
+ metadata:
393
+ category: campaign
394
+ action: WARN
395
+ confidence: MEDIUM
396
+ campaign: macos_social_engineering
397
+
398
+ - id: clawhavoc.campaign.xattr-quarantine
399
+ severity: CRITICAL
400
+ message: "Quarantine bypass detected — removes macOS Gatekeeper protection"
401
+ patterns:
402
+ - "xattr\\s+-d\\s+com\\.apple\\.quarantine"
403
+ - "xattr\\s+-c\\s+com\\.apple\\.quarantine"
404
+ - "xattr\\s+-r\\s+-d.*?quarantine"
405
+ - "spctl\\s+--master-disable"
406
+ metadata:
407
+ category: campaign
408
+ action: BLOCK
409
+ confidence: HIGH
410
+ campaign: macos_gatekeeper_bypass
411
+
412
+ - id: clawhavoc.campaign.clickfix
413
+ severity: CRITICAL
414
+ message: "ClickFix social engineering detected — instructs user to paste commands"
415
+ patterns:
416
+ - "press\\s+(Win|Windows|Cmd|Command)\\s*\\+\\s*R.*?paste"
417
+ - "open\\s+(terminal|cmd|powershell).*?paste\\s+(the\\s+)?following"
418
+ - "copy\\s+and\\s+paste\\s+(this|the\\s+following)\\s+(into|in)\\s+(terminal|cmd|powershell|shell)"
419
+ - "run\\s+this\\s+(in|from)\\s+(your\\s+)?(terminal|command\\s+prompt)"
420
+ metadata:
421
+ category: campaign
422
+ action: BLOCK
423
+ confidence: HIGH
424
+ campaign: clickfix
425
+
426
+ # ==========================================================================
427
+ # CATEGORY 10: EXFIL ENDPOINTS
428
+ # ==========================================================================
429
+
430
+ - id: clawhavoc.campaign.webhook-exfil
431
+ severity: CRITICAL
432
+ message: "Known exfiltration endpoint detected — data sent to attacker-controlled webhook"
433
+ patterns:
434
+ - "webhook\\.site"
435
+ - "discord\\.com/api/webhooks"
436
+ - "hooks\\.slack\\.com"
437
+ - "pipedream\\.net"
438
+ - "requestbin\\.(com|net)"
439
+ metadata:
440
+ category: exfil_endpoint
441
+ action: BLOCK
442
+ confidence: HIGH
443
+ malware_family: generic
@@ -0,0 +1,18 @@
1
+ // src/cli/audit.js — OpenClaw configuration security audit (stub)
2
+
3
+ export async function runAudit(args) {
4
+ console.log('\n Security Audit [EXPERIMENTAL STUB]\n');
5
+ console.log(' This command will check your OpenClaw configuration for security issues.');
6
+ console.log(' Full implementation coming in Sprint 3.\n');
7
+ console.log(' WARNING: This is an experimental stub. No actual checks are performed.\n');
8
+ console.log(' Planned checks (60+):');
9
+ console.log(' - Gateway: bind mode, auth, token strength, HTTPS, CORS');
10
+ console.log(' - Permissions: config files, credentials, session transcripts');
11
+ console.log(' - Tool Policy: allowlist, sandbox, elevated tools');
12
+ console.log(' - DM/Group: open access, pairing bypass, mention gating');
13
+ console.log(' - Hooks: weak tokens, unsafe external content');
14
+ console.log(' - mDNS: exposure, metadata leaks');
15
+ console.log(' - Plugins: unsigned, permissive, outdated');
16
+ console.log(' - Credentials: plaintext secrets, exposed API keys\n');
17
+ console.log(' OWASP ASI Top 10 mapping for all findings.\n');
18
+ }
@@ -0,0 +1,15 @@
1
+ // src/cli/harden.js — OpenClaw auto-hardening (stub)
2
+
3
+ export async function runHarden(args) {
4
+ console.log('\n Auto-Hardening [EXPERIMENTAL STUB]\n');
5
+ console.log(' This command will automatically fix security issues in your OpenClaw config.');
6
+ console.log(' Full implementation coming in Sprint 3.\n');
7
+ console.log(' WARNING: This is an experimental stub. No actions are performed.\n');
8
+ console.log(' Planned actions:');
9
+ console.log(' - Bind gateway to 127.0.0.1');
10
+ console.log(' - Enable token authentication');
11
+ console.log(' - Set config file permissions to 600');
12
+ console.log(' - Disable mDNS discovery');
13
+ console.log(' - Remove plaintext credentials\n');
14
+ console.log(' Usage: agent-security-scanner-mcp harden --fix [--dry-run]\n');
15
+ }
package/src/cli/init.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { readFileSync, existsSync, writeFileSync, copyFileSync, mkdirSync } from "fs";
2
+ import { spawnSync } from "child_process";
2
3
  import { dirname, join } from "path";
3
4
  import { homedir, platform } from "os";
4
5
  import { createInterface } from "readline";
@@ -79,6 +80,10 @@ const CLIENT_CONFIGS = {
79
80
  isSkillBased: true, // OpenClaw uses skills, not MCP config
80
81
  skillPath: () => join(homedir(), '.openclaw', 'workspace', 'skills', 'security-scanner'),
81
82
  configPath: () => join(homedir(), '.openclaw', 'workspace', 'skills', 'security-scanner', 'SKILL.md')
83
+ },
84
+ 'codex': {
85
+ name: 'Codex',
86
+ isCLIBased: true // Codex uses 'codex mcp add' CLI, not a JSON config
82
87
  }
83
88
  };
84
89
 
@@ -237,6 +242,51 @@ async function installOpenClawSkill(client, flags) {
237
242
  console.log(` - Or ask: "scan this prompt for security issues"\n`);
238
243
  }
239
244
 
245
+ // Installer for Codex (CLI-based, uses 'codex mcp add')
246
+ async function installCodexMCP(flags, serverName) {
247
+ console.log(`\n Client: Codex`);
248
+ console.log(` Config: ~/.codex/config.toml (managed by codex CLI)`);
249
+ console.log(` OS: ${platform()} (${process.arch})\n`);
250
+
251
+ // Check codex CLI is available
252
+ const which = spawnSync('which', ['codex'], { encoding: 'utf-8' });
253
+ if (which.status !== 0) {
254
+ console.error(` ERROR: 'codex' CLI not found in PATH.`);
255
+ console.error(` Install it first: https://github.com/openai/codex\n`);
256
+ process.exit(1);
257
+ }
258
+
259
+ if (flags.dryRun) {
260
+ console.log(` [dry-run] Would run:`);
261
+ console.log(` codex mcp add ${serverName} -- npx -y agent-security-scanner-mcp`);
262
+ console.log(` No changes made.\n`);
263
+ process.exit(0);
264
+ }
265
+
266
+ console.log(` Running: codex mcp add ${serverName} -- npx -y agent-security-scanner-mcp\n`);
267
+
268
+ const result = spawnSync(
269
+ 'codex',
270
+ ['mcp', 'add', serverName, '--', 'npx', '-y', 'agent-security-scanner-mcp'],
271
+ { encoding: 'utf-8', stdio: 'inherit' }
272
+ );
273
+
274
+ if (result.status !== 0) {
275
+ console.error(`\n ERROR: 'codex mcp add' failed (exit ${result.status}).`);
276
+ console.error(` You can add it manually to ~/.codex/config.toml:\n`);
277
+ console.error(` [mcp_servers.${serverName}]`);
278
+ console.error(` command = "npx"`);
279
+ console.error(` args = ["-y", "agent-security-scanner-mcp"]\n`);
280
+ process.exit(1);
281
+ }
282
+
283
+ console.log(`\n Codex MCP server '${serverName}' registered successfully!`);
284
+ console.log(`\n Next steps:`);
285
+ console.log(` 1. Start a Codex session`);
286
+ console.log(` 2. Run /mcp to verify 'agentic-security' is listed`);
287
+ console.log(` 3. Quick test: ask Codex to run scan_security on any code file\n`);
288
+ }
289
+
240
290
  export async function runInit(args) {
241
291
  const flags = parseInitFlags(args);
242
292
  let clientName = flags.client;
@@ -264,6 +314,12 @@ export async function runInit(args) {
264
314
  return;
265
315
  }
266
316
 
317
+ // Special handling for Codex (CLI-based, uses 'codex mcp add')
318
+ if (client.isCLIBased) {
319
+ await installCodexMCP(flags, flags.name);
320
+ return;
321
+ }
322
+
267
323
  const configPath = flags.path || client.configPath();
268
324
  const serverName = flags.name;
269
325
  const entry = client.buildEntry();
package/src/context.js CHANGED
@@ -163,7 +163,11 @@ const TEST_FILE_PATTERNS = [
163
163
  /[._](?:test|spec)\.[^.]+$/i,
164
164
  /[/\\]test[-_]?files?[/\\]/i,
165
165
  /[/\\]fixtures?[/\\]/i,
166
+ /[/\\]__fixtures__[/\\]/i,
167
+ /[/\\]__mocks__[/\\]/i,
168
+ /[/\\]mocks?[/\\]/i,
166
169
  /[/\\]demo[/\\]/i,
170
+ /[/\\]test[-_][^/\\]+\.[^.]+$/i,
167
171
  ];
168
172
 
169
173
  // Check if a file path looks like a test file
@@ -204,6 +204,16 @@ class DaemonClient {
204
204
  return resp.result;
205
205
  }
206
206
 
207
+ async preWarm() {
208
+ if (this._dead) return;
209
+ try {
210
+ await this.ensureRunning();
211
+ await this.health();
212
+ } catch {
213
+ // Pre-warm failure is non-fatal
214
+ }
215
+ }
216
+
207
217
  async shutdown() {
208
218
  if (!this._proc || this._proc.killed || this._proc.exitCode !== null) return;
209
219
  try {
@@ -0,0 +1,77 @@
1
+ // src/plugin-config.js — Plugin configuration loader
2
+ // Loads config from ~/.openclaw/scanner-config.json, .scannerrc.json, or .clawproofrc.json
3
+
4
+ import { existsSync, readFileSync } from 'fs';
5
+ import { join } from 'path';
6
+ import { homedir } from 'os';
7
+
8
+ const DEFAULT_PLUGIN_CONFIG = {
9
+ version: 1,
10
+ features: {
11
+ scan_on_write: true,
12
+ scan_on_skill_install: true,
13
+ prompt_firewall: true,
14
+ package_hallucination: true,
15
+ config_audit: true,
16
+ },
17
+ severity_threshold: 'warning',
18
+ auto_block: true,
19
+ output_format: 'json',
20
+ daemon: {
21
+ prewarm: true,
22
+ cache_size: 200,
23
+ },
24
+ };
25
+
26
+ export function loadPluginConfig() {
27
+ const paths = [
28
+ join(homedir(), '.openclaw', 'scanner-config.json'),
29
+ join(process.cwd(), '.clawproofrc.json'),
30
+ join(process.cwd(), '.scannerrc.json'),
31
+ ];
32
+
33
+ for (const configPath of paths) {
34
+ if (existsSync(configPath)) {
35
+ try {
36
+ const raw = readFileSync(configPath, 'utf-8');
37
+ const parsed = JSON.parse(raw);
38
+
39
+ if (!parsed || typeof parsed !== 'object') {
40
+ continue;
41
+ }
42
+
43
+ const parsedFeatures = parsed.features && typeof parsed.features === 'object' ? parsed.features : {};
44
+ const parsedDaemon = parsed.daemon && typeof parsed.daemon === 'object' ? parsed.daemon : {};
45
+
46
+ return {
47
+ version: typeof parsed.version === 'number' ? parsed.version : DEFAULT_PLUGIN_CONFIG.version,
48
+ features: {
49
+ scan_on_write: parsedFeatures.scan_on_write ?? DEFAULT_PLUGIN_CONFIG.features.scan_on_write,
50
+ scan_on_skill_install: parsedFeatures.scan_on_skill_install ?? DEFAULT_PLUGIN_CONFIG.features.scan_on_skill_install,
51
+ prompt_firewall: parsedFeatures.prompt_firewall ?? DEFAULT_PLUGIN_CONFIG.features.prompt_firewall,
52
+ package_hallucination: parsedFeatures.package_hallucination ?? DEFAULT_PLUGIN_CONFIG.features.package_hallucination,
53
+ config_audit: parsedFeatures.config_audit ?? DEFAULT_PLUGIN_CONFIG.features.config_audit,
54
+ },
55
+ severity_threshold: ['info', 'warning', 'error'].includes(parsed.severity_threshold)
56
+ ? parsed.severity_threshold : DEFAULT_PLUGIN_CONFIG.severity_threshold,
57
+ auto_block: typeof parsed.auto_block === 'boolean' ? parsed.auto_block : DEFAULT_PLUGIN_CONFIG.auto_block,
58
+ output_format: ['json', 'text'].includes(parsed.output_format)
59
+ ? parsed.output_format : DEFAULT_PLUGIN_CONFIG.output_format,
60
+ daemon: {
61
+ prewarm: parsedDaemon.prewarm ?? DEFAULT_PLUGIN_CONFIG.daemon.prewarm,
62
+ cache_size: typeof parsedDaemon.cache_size === 'number' ? parsedDaemon.cache_size : DEFAULT_PLUGIN_CONFIG.daemon.cache_size,
63
+ },
64
+ _source: configPath,
65
+ };
66
+ } catch {
67
+ // Malformed config, fall through
68
+ }
69
+ }
70
+ }
71
+
72
+ return { ...DEFAULT_PLUGIN_CONFIG, _source: null };
73
+ }
74
+
75
+ export function getDefaultPluginConfig() {
76
+ return { ...DEFAULT_PLUGIN_CONFIG };
77
+ }