hackmyagent-core 0.3.9 → 0.4.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.
package/README.md CHANGED
@@ -7,13 +7,24 @@
7
7
 
8
8
  **Website:** [hackmyagent.com](https://hackmyagent.com) — Scan external infrastructure for exposed MCP endpoints, configs, and credentials
9
9
 
10
+ ## What's New — v0.4.0
11
+
12
+ **First scanner for [CVE-2026-25253](https://hackmyagent.com/blog/cve-2026-25253-detection)** (CVSS 8.8) — the OpenClaw WebSocket hijacking RCE.
13
+
14
+ - **CVE-001:** Detect vulnerable OpenClaw versions (before v2026.1.29)
15
+ - **CVE-002:** Missing `controlUi.allowedOrigins` (patch alone isn't enough)
16
+ - **SUPPLY-005–008:** ClawHavoc campaign IOCs (C2 IPs, malware filenames, ClickFix patterns)
17
+ - **GATEWAY-007–008, CONFIG-007–009:** Config hardening (open DM wildcards, disabled sandbox, weak tokens)
18
+
19
+ 11 new checks. 145+ total.
20
+
10
21
  ## Disclaimer
11
22
 
12
23
  HackMyAgent performs passive reconnaissance only (port checks and HTTP requests) — it does not exploit vulnerabilities. However, please only scan systems you own or have permission to test. The authors assume no liability for misuse of this tool.
13
24
 
14
25
  ```bash
15
26
  npx hackmyagent check @publisher/skill # verify a skill before installing
16
- npx hackmyagent secure # harden your agent setup (100 checks)
27
+ npx hackmyagent secure # harden your agent setup (145+ checks)
17
28
  npx hackmyagent secure --fix # auto-fix security issues
18
29
  npx hackmyagent scan example.com # scan for exposed infrastructure
19
30
  npx hackmyagent attack --local # red team with 55 attack payloads
@@ -29,10 +40,10 @@ npx hackmyagent secure --benchmark oasb-1 # run OASB-1 security benchmark
29
40
 
30
41
  ## Why HackMyAgent?
31
42
 
32
- AI agents are powerful but introduce new attack surfaces. Skills can be malicious. Configs can leak secrets. MCP servers can be exposed. HackMyAgent helps you:
43
+ CVE-2026-25253 turned every OpenClaw installation into a remote code execution target. 341 malicious skills were distributed through ClawHub. AI agent security is no longer theoretical — HackMyAgent helps you:
33
44
 
34
45
  - **Check** skills before installing (publisher verification, permission analysis)
35
- - **Secure** your agent setup (100-point CIS security scan, auto-remediation)
46
+ - **Secure** your agent setup (145+ security checks with auto-remediation)
36
47
  - **Scan** external infrastructure (exposed MCP endpoints, leaked configs)
37
48
 
38
49
  ## Installation
@@ -52,7 +63,7 @@ npm install --save-dev hackmyagent
52
63
 
53
64
  ### `hackmyagent secure`
54
65
 
55
- Scan and harden your local agent setup with 100 security checks across 24 categories.
66
+ Scan and harden your local agent setup with 145+ security checks across 31 categories.
56
67
 
57
68
  ```bash
58
69
  # Basic scan
@@ -92,7 +103,26 @@ hackmyagent secure --verbose
92
103
  | AUDIT | 4 | Audit trails |
93
104
  | SANDBOX | 4 | Process isolation |
94
105
  | TOOL | 4 | Tool permission boundaries |
95
- | And 13 more... | 42 | Auth, deps, env, git, io, log, perm, proc, rate, sec, api, vscode, cursor |
106
+ | AUTH | 4 | Authentication checks |
107
+ | DEPS | 4 | Dependency security |
108
+ | ENV | 4 | Environment variable safety |
109
+ | GIT | 4 | Git security (.gitignore, secrets in history) |
110
+ | IO | 4 | Input/output validation |
111
+ | LOG | 4 | Logging and monitoring |
112
+ | PERM | 4 | File permissions |
113
+ | PROC | 4 | Process isolation |
114
+ | RATE | 4 | Rate limiting |
115
+ | SEC | 4 | General security headers |
116
+ | API | 4 | API security |
117
+ | VSCODE | 4 | VS Code configuration |
118
+ | CURSOR | 4 | Cursor IDE configuration |
119
+ | CVE | 2 | CVE-2026-25253 detection |
120
+ | GATEWAY | 8 | Gateway misconfigurations |
121
+ | CONFIG | 9 | Insecure settings |
122
+ | SUPPLY | 8 | Supply chain attacks |
123
+ | SKILL | 12 | Malicious skill detection |
124
+ | HEARTBEAT | 6 | Heartbeat/cron abuse |
125
+ | WINDSURF | 3 | Windsurf IDE configuration |
96
126
 
97
127
  **Exit Codes:**
98
128
  - `0` - No critical/high issues
@@ -299,7 +329,7 @@ hackmyagent secure -b oasb-1 --fail-below 70
299
329
 
300
330
  ### `hackmyagent secure-openclaw`
301
331
 
302
- Scan OpenClaw/Moltbot installations with 34 specialized security checks and auto-remediation.
332
+ Scan OpenClaw/Moltbot installations with 45 specialized security checks and auto-remediation.
303
333
 
304
334
  ```bash
305
335
  hackmyagent secure-openclaw # scan default location
@@ -310,8 +340,11 @@ hackmyagent secure-openclaw --json # JSON output for CI/CD
310
340
  ```
311
341
 
312
342
  **Detects:**
343
+ - CVE-2026-25253 vulnerable versions (before v2026.1.29)
344
+ - Missing `controlUi.allowedOrigins` (patch alone isn't enough)
345
+ - ClawHavoc C2 IP addresses and malware filenames
346
+ - ClickFix social engineering patterns
313
347
  - Unsigned/malicious skills (ClawHavoc campaign patterns)
314
- - ClickFix social engineering attacks
315
348
  - Reverse shell backdoors
316
349
  - Credential exfiltration (wallets, SSH keys, API keys)
317
350
  - Heartbeat/cron abuse
@@ -331,9 +364,10 @@ hackmyagent secure-openclaw --json # JSON output for CI/CD
331
364
  |----------|--------|-------------|
332
365
  | SKILL | 12 | Malicious skill detection |
333
366
  | HEARTBEAT | 6 | Heartbeat/cron abuse |
334
- | GATEWAY | 6 | Gateway misconfigurations (4 auto-fixable) |
335
- | CONFIG | 6 | Insecure settings |
336
- | SUPPLY | 4 | Supply chain attacks |
367
+ | GATEWAY | 8 | Gateway misconfigurations (4 auto-fixable) |
368
+ | CONFIG | 9 | Insecure settings |
369
+ | SUPPLY | 8 | Supply chain attacks |
370
+ | CVE | 2 | CVE-2026-25253 detection |
337
371
 
338
372
  See [SECURITY_CHECKS.md](docs/SECURITY_CHECKS.md#openclaw-security-checks) for full documentation.
339
373
 
@@ -428,7 +462,7 @@ hackmyagent secure --json | jq '.findings[] | select(.severity == "critical")'
428
462
 
429
463
  ## Security Check Reference
430
464
 
431
- For the complete list of 100 security checks with descriptions and remediation guidance, see [SECURITY_CHECKS.md](docs/SECURITY_CHECKS.md).
465
+ For the complete list of 145+ security checks with descriptions and remediation guidance, see [SECURITY_CHECKS.md](docs/SECURITY_CHECKS.md).
432
466
 
433
467
  ## Auto-Fix Capabilities
434
468
 
@@ -135,5 +135,9 @@ export declare class HardeningScanner {
135
135
  * OpenClaw supply chain security checks (SUPPLY-001 to SUPPLY-004)
136
136
  */
137
137
  private checkOpenclawSupplyChain;
138
+ /**
139
+ * OpenClaw CVE-specific checks (CVE-001, CVE-002)
140
+ */
141
+ private checkOpenclawCVE;
138
142
  }
139
143
  //# sourceMappingURL=scanner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/hardening/scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAA0C,MAAM,kBAAkB,CAAC;AAwD3F,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAgHD,qBAAa,gBAAgB;IAE3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAelC;IAEF;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAMvB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;YA0NvC,cAAc;IAsE5B;;OAEG;YACW,iBAAiB;IA+F/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;YAeV,uBAAuB;YAmGvB,aAAa;YAgDb,cAAc;YA+Fd,oBAAoB;YAwDpB,gBAAgB;YA0IhB,oBAAoB;YAgFpB,gBAAgB;YA2IhB,mBAAmB;YA4EnB,iBAAiB;YAyCjB,iBAAiB;YA+DjB,wBAAwB;YA0FxB,wBAAwB;YAmExB,wBAAwB;YAqHxB,oBAAoB;YA+GpB,uBAAuB;YA8HvB,iBAAiB;YA8GjB,oBAAoB;YAuGpB,mBAAmB;YAiGnB,gBAAgB;YAmIhB,oBAAoB;YAoIpB,gBAAgB;YAyHhB,qBAAqB;YA+GrB,eAAe;IAiI7B;;OAEG;YACW,mBAAmB;IA8GjC;;OAEG;YACW,oBAAoB;IAiKlC;;OAEG;YACW,iBAAiB;IA4I/B;;OAEG;YACW,oBAAoB;IAwIlC;;OAEG;YACW,eAAe;IAqJ7B;;OAEG;YACW,eAAe;IAuI7B;;OAEG;YACW,eAAe;IAyG7B;;OAEG;YACW,mBAAmB;IAmHjC,OAAO,CAAC,cAAc;IAsBtB;;OAEG;YACW,YAAY;IAkD1B;;OAEG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6DhD;;;OAGG;YACW,cAAc;IAgD5B;;OAEG;YACW,mBAAmB;IAoUjC;;;OAGG;YACW,kBAAkB;IAgDhC;;OAEG;YACW,sBAAsB;IA2LpC;;OAEG;YACW,sBAAsB;IA+BpC;;OAEG;YACW,oBAAoB;IA0RlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4B3B;;OAEG;YACW,iBAAiB;IA8D/B;;OAEG;YACW,mBAAmB;IAmRjC;;OAEG;YACW,wBAAwB;CAsJvC"}
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/hardening/scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAA0C,MAAM,kBAAkB,CAAC;AAyD3F,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AA8HD,qBAAa,gBAAgB;IAE3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAelC;IAEF;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAMvB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;YA8NvC,cAAc;IAsE5B;;OAEG;YACW,iBAAiB;IA+F/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;YAeV,uBAAuB;YAmGvB,aAAa;YAgDb,cAAc;YA+Fd,oBAAoB;YAwDpB,gBAAgB;YA0IhB,oBAAoB;YAgFpB,gBAAgB;YA2IhB,mBAAmB;YA4EnB,iBAAiB;YAyCjB,iBAAiB;YA+DjB,wBAAwB;YA0FxB,wBAAwB;YAmExB,wBAAwB;YAqHxB,oBAAoB;YA+GpB,uBAAuB;YA8HvB,iBAAiB;YA8GjB,oBAAoB;YAuGpB,mBAAmB;YAiGnB,gBAAgB;YAmIhB,oBAAoB;YAoIpB,gBAAgB;YAyHhB,qBAAqB;YA+GrB,eAAe;IAiI7B;;OAEG;YACW,mBAAmB;IA8GjC;;OAEG;YACW,oBAAoB;IAiKlC;;OAEG;YACW,iBAAiB;IA4I/B;;OAEG;YACW,oBAAoB;IAwIlC;;OAEG;YACW,eAAe;IAqJ7B;;OAEG;YACW,eAAe;IAuI7B;;OAEG;YACW,eAAe;IAyG7B;;OAEG;YACW,mBAAmB;IAmHjC,OAAO,CAAC,cAAc;IAsBtB;;OAEG;YACW,YAAY;IAkD1B;;OAEG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6DhD;;;OAGG;YACW,cAAc;IAgD5B;;OAEG;YACW,mBAAmB;IAoUjC;;;OAGG;YACW,kBAAkB;IAgDhC;;OAEG;YACW,sBAAsB;IA2LpC;;OAEG;YACW,sBAAsB;IA+BpC;;OAEG;YACW,oBAAoB;IAqVlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4B3B;;OAEG;YACW,iBAAiB;IA8D/B;;OAEG;YACW,mBAAmB;IA6VjC;;OAEG;YACW,wBAAwB;IA4OtC;;OAEG;YACW,gBAAgB;CAmG/B"}
@@ -71,6 +71,7 @@ const CHECK_PROJECT_TYPES = {
71
71
  'GATEWAY-': ['openclaw'], // Gateway configuration security
72
72
  'CONFIG-': ['openclaw', 'mcp'], // Configuration file security
73
73
  'SUPPLY-': ['openclaw', 'mcp'], // Supply chain security
74
+ 'CVE-': ['openclaw'], // CVE-specific detection
74
75
  'API-': ['api'], // API security headers
75
76
  'RATE-': ['webapp', 'api'], // Rate limiting
76
77
  'PROC-': ['webapp', 'api'], // Process security (headers, etc.)
@@ -165,6 +166,19 @@ const HEARTBEAT_DANGEROUS_CAPS = [
165
166
  'filesystem:/',
166
167
  'network:*',
167
168
  ];
169
+ // ClawHavoc campaign IOCs (Koi Security research, Jan 2026)
170
+ const CLAWHAVOC_C2_IPS = ['91.92.242.30'];
171
+ const CLAWHAVOC_MALICIOUS_FILES = [
172
+ 'openclaw-agent.exe', 'openclaw-agent.zip', 'openclawcli.zip',
173
+ 'agent-setup.exe', 'openclaw-installer.dmg',
174
+ ];
175
+ const CLAWHAVOC_CLICKFIX_PATTERNS = [
176
+ /download.*paste.*terminal/i,
177
+ /copy.*(?:command|script).*terminal/i,
178
+ /right[- ]click.*open/i,
179
+ /run.*\.exe/i,
180
+ ];
181
+ const CLAWHAVOC_ARCHIVE_PASSWORD = /password\s*[:=]\s*["']?(openclaw|claw|agent|setup)["']?/i;
168
182
  const PROMPT_INJECTION_PATTERNS = [
169
183
  /ignore\s+(all\s+)?(previous|prior|above)/gi,
170
184
  /disregard\s+(all\s+)?(previous|prior)/gi,
@@ -320,6 +334,9 @@ class HardeningScanner {
320
334
  // OpenClaw supply chain checks
321
335
  const supplyFindings = await this.checkOpenclawSupplyChain(targetDir, shouldFix);
322
336
  findings.push(...supplyFindings);
337
+ // OpenClaw CVE-specific checks
338
+ const cveFindings = await this.checkOpenclawCVE(targetDir, shouldFix);
339
+ findings.push(...cveFindings);
323
340
  // Filter findings to only show real, actionable issues:
324
341
  // 1. Only failed checks (passed: false)
325
342
  // 2. Only checks with a file path (concrete findings, not generic advice)
@@ -4495,6 +4512,58 @@ dist/
4495
4512
  fix: 'Manually disable privileged mode and remove sensitive host mounts - requires careful review',
4496
4513
  });
4497
4514
  }
4515
+ // GATEWAY-007: Open DM Policy with Wildcard
4516
+ const channels = config.channels;
4517
+ const dm = config.dm;
4518
+ let hasOpenDmWildcard = false;
4519
+ if (channels) {
4520
+ for (const [, channelConfig] of Object.entries(channels)) {
4521
+ if (channelConfig.dmPolicy === 'open' &&
4522
+ Array.isArray(channelConfig.allowFrom) &&
4523
+ channelConfig.allowFrom.includes('*')) {
4524
+ hasOpenDmWildcard = true;
4525
+ break;
4526
+ }
4527
+ }
4528
+ }
4529
+ if (!hasOpenDmWildcard && dm?.policy === 'open') {
4530
+ const allowList = dm.allowFrom;
4531
+ if (Array.isArray(allowList) && allowList.includes('*')) {
4532
+ hasOpenDmWildcard = true;
4533
+ }
4534
+ }
4535
+ if (hasOpenDmWildcard) {
4536
+ findings.push({
4537
+ checkId: 'GATEWAY-007',
4538
+ name: 'Open DM Policy with Wildcard',
4539
+ description: 'Direct message policy allows messages from any source',
4540
+ category: 'gateway',
4541
+ severity: 'critical',
4542
+ passed: false,
4543
+ message: 'DM policy is open with wildcard allowFrom - anyone can message the agent',
4544
+ file: relativePath,
4545
+ fixable: false,
4546
+ fix: 'Replace wildcard "*" in allowFrom with specific allowed sender IDs or domains',
4547
+ });
4548
+ }
4549
+ // GATEWAY-008: Tailscale Funnel Exposure
4550
+ const tailscale = gateway?.tailscale;
4551
+ const tailscaleRoot = config.tailscale;
4552
+ const funnelEnabled = tailscale?.funnel === true || tailscaleRoot?.funnel === true;
4553
+ if (funnelEnabled) {
4554
+ findings.push({
4555
+ checkId: 'GATEWAY-008',
4556
+ name: 'Tailscale Funnel Exposure',
4557
+ description: 'Tailscale Funnel is enabled, exposing the agent to the public internet',
4558
+ category: 'gateway',
4559
+ severity: 'high',
4560
+ passed: false,
4561
+ message: 'Tailscale Funnel enabled - agent is publicly accessible from the internet',
4562
+ file: relativePath,
4563
+ fixable: false,
4564
+ fix: 'Disable Tailscale Funnel unless public access is intentional. Use Tailscale ACLs to restrict access.',
4565
+ });
4566
+ }
4498
4567
  // Write modified config back to file if any fixes were applied
4499
4568
  if (configModified) {
4500
4569
  try {
@@ -4873,6 +4942,71 @@ dist/
4873
4942
  fix: 'Disable autoFollow or review moltbook security settings',
4874
4943
  });
4875
4944
  }
4945
+ // CONFIG-007: Unrestricted Elevated Execution
4946
+ const tools = config.tools;
4947
+ const elevated = tools?.elevated;
4948
+ const exec = config.exec;
4949
+ const execApprovals = exec?.approvals;
4950
+ const hasUnrestrictedExec = elevated?.defaultLevel === 'full' ||
4951
+ execApprovals?.set === 'off';
4952
+ if (hasUnrestrictedExec) {
4953
+ findings.push({
4954
+ checkId: 'CONFIG-007',
4955
+ name: 'Unrestricted Elevated Execution',
4956
+ description: 'Elevated execution is set to full access without restrictions or approvals are bypassed',
4957
+ category: 'config',
4958
+ severity: 'critical',
4959
+ passed: false,
4960
+ message: elevated?.defaultLevel === 'full'
4961
+ ? 'tools.elevated.defaultLevel is "full" - all tools run with maximum privileges'
4962
+ : 'exec.approvals.set is "off" - execution approval is bypassed',
4963
+ file: relativePath,
4964
+ fixable: false,
4965
+ fix: 'Set tools.elevated.defaultLevel to "restricted" and enable exec.approvals',
4966
+ });
4967
+ }
4968
+ // CONFIG-008: Sandbox Disabled
4969
+ const sandbox = config.sandbox;
4970
+ const toolExec = tools?.exec;
4971
+ const sandboxDisabled = sandbox?.enabled === false ||
4972
+ toolExec?.sandbox === false;
4973
+ if (sandboxDisabled) {
4974
+ findings.push({
4975
+ checkId: 'CONFIG-008',
4976
+ name: 'Sandbox Disabled',
4977
+ description: 'Sandbox execution environment is explicitly disabled in config',
4978
+ category: 'config',
4979
+ severity: 'high',
4980
+ passed: false,
4981
+ message: sandbox?.enabled === false
4982
+ ? 'sandbox.enabled is false - code runs without isolation'
4983
+ : 'tools.exec.sandbox is false - tool execution is not sandboxed',
4984
+ file: relativePath,
4985
+ fixable: false,
4986
+ fix: 'Enable sandbox: set sandbox.enabled to true or tools.exec.sandbox to true',
4987
+ });
4988
+ }
4989
+ // CONFIG-009: Weak Gateway Token
4990
+ const gatewayConfig = config.gateway;
4991
+ const gatewayAuth = gatewayConfig?.auth;
4992
+ const tokenValue = gatewayAuth?.token || config.token;
4993
+ if (typeof tokenValue === 'string' &&
4994
+ tokenValue.length > 0 &&
4995
+ tokenValue.length < 24 &&
4996
+ !tokenValue.startsWith('${')) {
4997
+ findings.push({
4998
+ checkId: 'CONFIG-009',
4999
+ name: 'Weak Gateway Token',
5000
+ description: 'Gateway authentication token is too short (< 24 characters)',
5001
+ category: 'config',
5002
+ severity: 'high',
5003
+ passed: false,
5004
+ message: `Token is only ${tokenValue.length} characters - minimum 24 recommended`,
5005
+ file: relativePath,
5006
+ fixable: false,
5007
+ fix: 'Generate a stronger token: openssl rand -base64 32',
5008
+ });
5009
+ }
4876
5010
  }
4877
5011
  return findings;
4878
5012
  }
@@ -4891,6 +5025,16 @@ dist/
4891
5025
  'phantom-wallet',
4892
5026
  'youtube-downloader',
4893
5027
  'clawhub',
5028
+ 'clawhub1',
5029
+ 'clawhubb',
5030
+ 'cllawhub',
5031
+ 'clawhub-official',
5032
+ 'openclaw-official',
5033
+ 'openclaw1',
5034
+ 'opennclaw',
5035
+ 'insiderwallet',
5036
+ 'wallet-finder',
5037
+ 'crypto-insider',
4894
5038
  ];
4895
5039
  for (const skillFile of skillFiles) {
4896
5040
  const relativePath = path.relative(targetDir, skillFile);
@@ -5008,6 +5152,170 @@ dist/
5008
5152
  fixable: false,
5009
5153
  fix: 'Add installed_hash: with SHA-256 hash of the original skill content',
5010
5154
  });
5155
+ // SUPPLY-005: ClawHavoc C2 IP
5156
+ for (const ip of CLAWHAVOC_C2_IPS) {
5157
+ if (content.includes(ip)) {
5158
+ findings.push({
5159
+ checkId: 'SUPPLY-005',
5160
+ name: 'ClawHavoc C2 IP Detected',
5161
+ description: 'Skill contains known ClawHavoc command-and-control IP address',
5162
+ category: 'supply',
5163
+ severity: 'critical',
5164
+ passed: false,
5165
+ message: `Known C2 IP address found: ${ip}`,
5166
+ file: relativePath,
5167
+ fixable: false,
5168
+ fix: 'Remove this skill immediately - contains known malware C2 infrastructure',
5169
+ });
5170
+ break;
5171
+ }
5172
+ }
5173
+ // SUPPLY-006: Malware Filenames
5174
+ for (const filename of CLAWHAVOC_MALICIOUS_FILES) {
5175
+ if (content.toLowerCase().includes(filename.toLowerCase())) {
5176
+ findings.push({
5177
+ checkId: 'SUPPLY-006',
5178
+ name: 'ClawHavoc Malware Filename',
5179
+ description: 'Skill references known ClawHavoc malware payload filename',
5180
+ category: 'supply',
5181
+ severity: 'critical',
5182
+ passed: false,
5183
+ message: `Known malware filename referenced: "${filename}"`,
5184
+ file: relativePath,
5185
+ fixable: false,
5186
+ fix: 'Remove this skill immediately - references known malware payload',
5187
+ });
5188
+ break;
5189
+ }
5190
+ }
5191
+ // SUPPLY-007: ClickFix Pattern
5192
+ for (const pattern of CLAWHAVOC_CLICKFIX_PATTERNS) {
5193
+ const match = content.match(pattern);
5194
+ if (match) {
5195
+ findings.push({
5196
+ checkId: 'SUPPLY-007',
5197
+ name: 'ClawHavoc ClickFix Pattern',
5198
+ description: 'Skill contains social engineering instructions to execute malware',
5199
+ category: 'supply',
5200
+ severity: 'high',
5201
+ passed: false,
5202
+ message: `ClickFix social engineering pattern detected: "${match[0]}"`,
5203
+ file: relativePath,
5204
+ fixable: false,
5205
+ fix: 'Review and remove suspicious download/execute instructions',
5206
+ });
5207
+ break;
5208
+ }
5209
+ }
5210
+ // SUPPLY-008: Suspicious Archive Password
5211
+ const archiveMatch = content.match(CLAWHAVOC_ARCHIVE_PASSWORD);
5212
+ if (archiveMatch) {
5213
+ findings.push({
5214
+ checkId: 'SUPPLY-008',
5215
+ name: 'Suspicious Archive Password',
5216
+ description: 'Skill contains password-protected archive reference typical of malware distribution',
5217
+ category: 'supply',
5218
+ severity: 'high',
5219
+ passed: false,
5220
+ message: `Suspicious archive password pattern: "${archiveMatch[0]}"`,
5221
+ file: relativePath,
5222
+ fixable: false,
5223
+ fix: 'Investigate password-protected archive reference - common malware distribution technique',
5224
+ });
5225
+ }
5226
+ }
5227
+ return findings;
5228
+ }
5229
+ /**
5230
+ * OpenClaw CVE-specific checks (CVE-001, CVE-002)
5231
+ */
5232
+ async checkOpenclawCVE(targetDir, _autoFix) {
5233
+ const findings = [];
5234
+ // CVE-001: Vulnerable OpenClaw Version
5235
+ const pkgJsonPath = path.join(targetDir, 'package.json');
5236
+ try {
5237
+ const pkgContent = await fs.readFile(pkgJsonPath, 'utf-8');
5238
+ const pkg = JSON.parse(pkgContent);
5239
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
5240
+ const openclawVersion = deps?.openclaw || deps?.['@openclaw/core'];
5241
+ if (openclawVersion) {
5242
+ // Extract numeric version (strip ^ ~ >= etc.)
5243
+ const versionMatch = openclawVersion.match(/(\d{4})\.(\d{1,2})\.(\d{1,2})/);
5244
+ if (versionMatch) {
5245
+ const year = parseInt(versionMatch[1], 10);
5246
+ const month = parseInt(versionMatch[2], 10);
5247
+ const day = parseInt(versionMatch[3], 10);
5248
+ // Patch release: v2026.1.29
5249
+ const isVulnerable = year < 2026 ||
5250
+ (year === 2026 && month < 1) ||
5251
+ (year === 2026 && month === 1 && day < 29);
5252
+ findings.push({
5253
+ checkId: 'CVE-001',
5254
+ name: 'CVE-2026-25253: WebSocket Hijacking RCE',
5255
+ description: 'OpenClaw version vulnerable to CVE-2026-25253 (CVSS 8.8) - WebSocket hijacking enables 1-click RCE',
5256
+ category: 'cve',
5257
+ severity: 'critical',
5258
+ passed: !isVulnerable,
5259
+ message: isVulnerable
5260
+ ? `OpenClaw ${openclawVersion} is vulnerable to CVE-2026-25253 - upgrade to v2026.1.29+`
5261
+ : `OpenClaw ${openclawVersion} includes CVE-2026-25253 fix`,
5262
+ file: 'package.json',
5263
+ fixable: false,
5264
+ fix: 'Upgrade openclaw to v2026.1.29 or later: npm install openclaw@latest',
5265
+ });
5266
+ }
5267
+ }
5268
+ }
5269
+ catch {
5270
+ // No package.json or parse error - skip CVE-001
5271
+ }
5272
+ // CVE-002: Missing Control UI Origin Restrictions
5273
+ const configFiles = await this.findGatewayConfigFiles(targetDir);
5274
+ for (const configFile of configFiles) {
5275
+ const relativePath = path.relative(targetDir, configFile);
5276
+ try {
5277
+ const stats = await fs.stat(configFile);
5278
+ if (stats.size > MAX_FILE_SIZE)
5279
+ continue;
5280
+ const content = await fs.readFile(configFile, 'utf-8');
5281
+ const config = JSON.parse(content);
5282
+ const gateway = config.gateway;
5283
+ const controlUi = gateway?.controlUi;
5284
+ const hasAllowedOrigins = controlUi?.allowedOrigins && Array.isArray(controlUi.allowedOrigins) && controlUi.allowedOrigins.length > 0;
5285
+ // Only flag if auth is configured (no auth = lower risk)
5286
+ const hasAuth = gateway?.auth || config.auth || config.token || gateway?.token;
5287
+ if (hasAuth && !hasAllowedOrigins) {
5288
+ findings.push({
5289
+ checkId: 'CVE-002',
5290
+ name: 'CVE-2026-25253: Missing Control UI Origin Restrictions',
5291
+ description: 'Auth is configured but controlUi.allowedOrigins is missing - vulnerable to cross-origin WebSocket hijacking',
5292
+ category: 'cve',
5293
+ severity: 'critical',
5294
+ passed: false,
5295
+ message: 'Auth configured without controlUi.allowedOrigins - cross-origin requests can steal auth tokens',
5296
+ file: relativePath,
5297
+ fixable: false,
5298
+ fix: 'Add gateway.controlUi.allowedOrigins with your allowed origins (e.g., ["http://localhost:3000"])',
5299
+ });
5300
+ }
5301
+ else if (hasAuth && hasAllowedOrigins) {
5302
+ findings.push({
5303
+ checkId: 'CVE-002',
5304
+ name: 'CVE-2026-25253: Control UI Origin Restrictions',
5305
+ description: 'Control UI origin restrictions are configured',
5306
+ category: 'cve',
5307
+ severity: 'critical',
5308
+ passed: true,
5309
+ message: 'controlUi.allowedOrigins is configured',
5310
+ file: relativePath,
5311
+ fixable: false,
5312
+ fix: 'No action needed',
5313
+ });
5314
+ }
5315
+ }
5316
+ catch {
5317
+ continue;
5318
+ }
5011
5319
  }
5012
5320
  return findings;
5013
5321
  }