@guava-parity/guard-scanner 15.0.0 → 16.0.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.
Files changed (61) hide show
  1. package/README.md +208 -42
  2. package/README_ja.md +252 -0
  3. package/SKILL.md +40 -11
  4. package/dist/cli.cjs +5997 -0
  5. package/dist/cli.d.mts +1 -0
  6. package/dist/cli.d.ts +1 -0
  7. package/dist/cli.mjs +6003 -0
  8. package/dist/index.cjs +4825 -0
  9. package/dist/index.d.mts +17 -0
  10. package/dist/index.d.ts +17 -0
  11. package/dist/index.mjs +4798 -0
  12. package/dist/mcp-server.cjs +4756 -0
  13. package/dist/mcp-server.d.mts +1 -0
  14. package/dist/mcp-server.d.ts +1 -0
  15. package/dist/mcp-server.mjs +4767 -0
  16. package/dist/openclaw-plugin.cjs +4863 -0
  17. package/dist/openclaw-plugin.d.mts +11 -0
  18. package/dist/openclaw-plugin.d.ts +11 -0
  19. package/dist/openclaw-plugin.mjs +4847 -34
  20. package/dist/types.cjs +18 -0
  21. package/dist/types.d.mts +215 -0
  22. package/dist/types.d.ts +215 -0
  23. package/dist/types.mjs +1 -0
  24. package/docs/data/benchmark-ledger.json +1428 -0
  25. package/docs/data/corpus-metrics.json +3 -3
  26. package/docs/data/fp-ledger.json +18 -0
  27. package/docs/data/quality-contract.json +36 -0
  28. package/docs/generated/openclaw-upstream-status.json +13 -13
  29. package/docs/openclaw-compatibility-audit.md +3 -2
  30. package/docs/openclaw-continuous-compatibility-plan.md +2 -1
  31. package/docs/spec/capabilities.json +137 -5
  32. package/docs/spec/plugin-trust.json +11 -0
  33. package/hooks/{context.js → context.ts} +1 -0
  34. package/openclaw-plugin.mts +21 -5
  35. package/openclaw.plugin.json +2 -2
  36. package/package.json +58 -20
  37. package/src/asset-auditor.js +0 -508
  38. package/src/ci-reporter.js +0 -135
  39. package/src/cli.js +0 -434
  40. package/src/core/content-loader.js +0 -42
  41. package/src/core/inventory.js +0 -73
  42. package/src/core/report-adapters.js +0 -171
  43. package/src/core/risk-engine.js +0 -93
  44. package/src/core/rule-registry.js +0 -73
  45. package/src/core/semantic-validators.js +0 -85
  46. package/src/finding-schema.js +0 -191
  47. package/src/hooks/context.ts +0 -49
  48. package/src/html-template.js +0 -239
  49. package/src/ioc-db.js +0 -54
  50. package/src/mcp-server.js +0 -653
  51. package/src/openclaw-upstream.js +0 -128
  52. package/src/patterns.js +0 -629
  53. package/src/policy-engine.js +0 -32
  54. package/src/quarantine.js +0 -41
  55. package/src/runtime-guard.js +0 -384
  56. package/src/scanner.js +0 -1042
  57. package/src/skill-crawler.js +0 -254
  58. package/src/threat-model.js +0 -50
  59. package/src/validation-layer.js +0 -39
  60. package/src/vt-client.js +0 -202
  61. package/src/watcher.js +0 -170
@@ -1,239 +0,0 @@
1
- /**
2
- * guard-scanner — HTML Report Template
3
- * Dark Glassmorphism + Conic-gradient Risk Gauges
4
- * Lightweight HTML report template. Pure CSS animations.
5
- */
6
-
7
- 'use strict';
8
-
9
- function generateHTML(version, stats, findings) {
10
- const safetyRate = stats.scanned > 0 ? Math.round(((stats.clean + stats.low) / stats.scanned) * 100) : 100;
11
-
12
- // ── Risk gauge (conic-gradient donut) ──
13
- const riskGauge = (risk) => {
14
- const angle = (risk / 100) * 360;
15
- const color = risk >= 80 ? '#ef4444' : risk >= 30 ? '#f59e0b' : '#22c55e';
16
- return `<div class="risk-gauge" style="--risk-angle:${angle}deg;--risk-color:${color}"><span class="risk-val">${risk}</span></div>`;
17
- };
18
-
19
- // ── Aggregate severity + category counts ──
20
- const sevCounts = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 };
21
- const catCounts = {};
22
- for (const sr of findings) {
23
- for (const f of sr.findings) {
24
- sevCounts[f.severity] = (sevCounts[f.severity] || 0) + 1;
25
- catCounts[f.cat] = (catCounts[f.cat] || 0) + 1;
26
- }
27
- }
28
- const total = Object.values(sevCounts).reduce((a, b) => a + b, 0);
29
-
30
- // ── Severity distribution bars ──
31
- const sevColors = { CRITICAL: '#ef4444', HIGH: '#f97316', MEDIUM: '#eab308', LOW: '#22c55e' };
32
- const sevBars = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'].map(s => {
33
- const c = sevCounts[s], pct = total > 0 ? ((c / total) * 100).toFixed(1) : 0;
34
- return `<div class="bar-row"><span class="bar-label" style="color:${sevColors[s]}">${s}</span><div class="bar-track"><div class="bar-fill" style="width:${pct}%;background:${sevColors[s]}"></div></div><span class="bar-ct">${c}</span></div>`;
35
- }).join('\n');
36
-
37
- // ── Top 8 categories ──
38
- const topCats = Object.entries(catCounts).sort((a, b) => b[1] - a[1]).slice(0, 8);
39
- const catBars = topCats.map(([cat, c]) => {
40
- const pct = total > 0 ? ((c / total) * 100).toFixed(0) : 0;
41
- return `<div class="bar-row"><span class="bar-label cat-lbl">${cat}</span><div class="bar-track"><div class="bar-fill cat-fill" style="width:${pct}%"></div></div><span class="bar-ct">${c}</span></div>`;
42
- }).join('\n');
43
-
44
- // ── Skill cards ──
45
- let cards = '';
46
- for (const sr of findings) {
47
- const vc = sr.verdict === 'MALICIOUS' ? 'mal' : sr.verdict === 'SUSPICIOUS' ? 'sus' : sr.verdict === 'LOW RISK' ? 'low' : 'ok';
48
- const icon = sr.verdict === 'MALICIOUS' ? '💀' : sr.verdict === 'SUSPICIOUS' ? '⚡' : sr.verdict === 'LOW RISK' ? '⚠️' : '✅';
49
- const rows = sr.findings.map(f => {
50
- const sc = f.severity.toLowerCase();
51
- return `<tr class="f-row"><td><span class="pill ${sc}">${f.severity}</span></td><td class="mono dim">${f.cat}</td><td>${f.desc}</td><td class="mono muted">${f.file}${f.line ? ':' + f.line : ''}</td></tr>`;
52
- }).join('\n');
53
-
54
- cards += `
55
- <details class="card card-${vc}" ${(vc === 'mal' || vc === 'sus') ? 'open' : ''}>
56
- <summary class="card-head">
57
- <div class="card-info"><span class="card-icon">${icon}</span><span class="card-name">${sr.skill}</span><span class="v-pill v-${vc}">${sr.verdict}</span></div>
58
- ${riskGauge(sr.risk)}
59
- </summary>
60
- <div class="card-body">
61
- <table class="ftable"><thead><tr><th>Severity</th><th>Category</th><th>Description</th><th>Location</th></tr></thead><tbody>${rows}</tbody></table>
62
- </div>
63
- </details>`;
64
- }
65
-
66
- // ── Full HTML ──
67
- return `<!DOCTYPE html>
68
- <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
69
- <title>guard-scanner v${version} — Security Report</title>
70
- <link rel="preconnect" href="https://fonts.googleapis.com">
71
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
72
- <style>
73
- /* ===== Tokens ===== */
74
- :root{
75
- --bg:#06070d;--sf:rgba(15,18,35,.7);--cd:rgba(20,24,50,.55);--ch:rgba(28,33,65,.65);
76
- --bdr:rgba(100,120,255,.08);--glow:rgba(100,140,255,.15);
77
- --t1:#e8eaf6;--t2:#8b92b3;--t3:#5a6180;
78
- --blue:#6c8cff;--purple:#a78bfa;--cyan:#22d3ee;--green:#22c55e;--yellow:#eab308;--orange:#f97316;--red:#ef4444;
79
- --sans:'Inter',system-ui,-apple-system,sans-serif;--mono:'JetBrains Mono','SF Mono',monospace;
80
- --r:12px;
81
- }
82
- *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
83
- html{scroll-behavior:smooth}
84
- body{font-family:var(--sans);background:var(--bg);color:var(--t1);min-height:100vh;overflow-x:hidden;line-height:1.6}
85
-
86
- /* ===== Ambient BG ===== */
87
- body::before{content:'';position:fixed;inset:0;z-index:-1;
88
- background:radial-gradient(ellipse 80% 60% at 20% 10%,rgba(108,140,255,.08) 0%,transparent 50%),
89
- radial-gradient(ellipse 60% 50% at 80% 90%,rgba(167,139,250,.06) 0%,transparent 50%),
90
- radial-gradient(ellipse 50% 40% at 50% 50%,rgba(34,211,238,.04) 0%,transparent 50%);
91
- animation:pulse 12s ease-in-out infinite alternate}
92
- @keyframes pulse{0%{opacity:.6;transform:scale(1)}100%{opacity:1;transform:scale(1.05)}}
93
-
94
- .wrap{max-width:1200px;margin:0 auto;padding:32px 24px}
95
-
96
- /* ===== Header ===== */
97
- .hdr{text-align:center;margin-bottom:36px;animation:fadeD .6s ease-out}
98
- .hdr h1{font-size:2.2em;font-weight:800;letter-spacing:-.03em;
99
- background:linear-gradient(135deg,var(--blue),var(--purple),var(--cyan));
100
- -webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
101
- .hdr .sub{color:var(--t2);font-size:.95em;margin-top:4px}
102
- .hdr .ts{color:var(--t3);font-family:var(--mono);font-size:.78em;margin-top:2px}
103
-
104
- /* ===== Stat Grid ===== */
105
- .sg{display:grid;grid-template-columns:repeat(auto-fit,minmax(155px,1fr));gap:14px;margin-bottom:28px;animation:fadeU .7s ease-out}
106
- .sc{background:var(--cd);backdrop-filter:blur(20px) saturate(1.5);-webkit-backdrop-filter:blur(20px) saturate(1.5);
107
- border:1px solid var(--bdr);border-radius:var(--r);padding:18px;text-align:center;
108
- transition:all .3s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}
109
- .sc::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;
110
- background:linear-gradient(90deg,transparent,var(--ac,var(--blue)),transparent);opacity:0;transition:opacity .3s}
111
- .sc:hover{transform:translateY(-3px);border-color:var(--glow)}.sc:hover::before{opacity:1}
112
- .sn{font-size:2.2em;font-weight:800;letter-spacing:-.04em;line-height:1.1}
113
- .sl{color:var(--t2);font-size:.78em;font-weight:500;margin-top:3px;text-transform:uppercase;letter-spacing:.06em}
114
- .sc.g{--ac:var(--green)}.sc.g .sn{color:var(--green)}
115
- .sc.l{--ac:#86efac}.sc.l .sn{color:#86efac}
116
- .sc.s{--ac:var(--yellow)}.sc.s .sn{color:var(--yellow)}
117
- .sc.m{--ac:var(--red)}.sc.m .sn{color:var(--red)}
118
- .sc.r{--ac:var(--cyan)}.sc.r .sn{color:var(--cyan)}
119
-
120
- /* ===== Analysis Panels ===== */
121
- .ag{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-bottom:28px;animation:fadeU .8s ease-out}
122
- @media(max-width:768px){.ag{grid-template-columns:1fr}}
123
- .pn{background:var(--cd);backdrop-filter:blur(20px) saturate(1.5);-webkit-backdrop-filter:blur(20px) saturate(1.5);
124
- border:1px solid var(--bdr);border-radius:var(--r);padding:22px}
125
- .pn h2{font-size:.88em;font-weight:600;color:var(--t2);text-transform:uppercase;letter-spacing:.08em;margin-bottom:14px}
126
-
127
- /* Bars */
128
- .bar-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}
129
- .bar-label{font-family:var(--mono);font-size:.72em;font-weight:600;width:68px;text-align:right}
130
- .cat-lbl{font-family:var(--sans);font-weight:500;color:var(--t2);width:110px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
131
- .bar-track{flex:1;height:5px;background:rgba(255,255,255,.04);border-radius:3px;overflow:hidden}
132
- .bar-fill{height:100%;border-radius:3px;transition:width 1.2s cubic-bezier(.4,0,.2,1)}
133
- .cat-fill{background:linear-gradient(90deg,var(--blue),var(--purple))}
134
- .bar-ct{font-family:var(--mono);font-size:.76em;color:var(--t3);width:28px}
135
-
136
- /* ===== Skill Cards ===== */
137
- .sec-title{font-size:1.05em;font-weight:700;margin-bottom:14px}
138
- .card{background:var(--cd);backdrop-filter:blur(16px) saturate(1.4);-webkit-backdrop-filter:blur(16px) saturate(1.4);
139
- border:1px solid var(--bdr);border-radius:var(--r);margin-bottom:10px;overflow:hidden;
140
- transition:all .3s ease;animation:fadeU .5s ease-out both}
141
- .card:hover{border-color:var(--glow)}
142
- .card-mal{border-left:3px solid var(--red)}
143
- .card-sus{border-left:3px solid var(--yellow)}
144
- .card-low{border-left:3px solid #86efac}
145
- .card-ok{border-left:3px solid var(--green)}
146
-
147
- .card-head{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;cursor:pointer;list-style:none}
148
- .card-head::-webkit-details-marker{display:none}
149
- .card-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}
150
- .card-icon{font-size:1.15em}
151
- .card-name{font-weight:600;font-size:.92em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
152
- .v-pill{font-family:var(--mono);font-size:.66em;font-weight:600;padding:2px 7px;border-radius:4px;letter-spacing:.04em}
153
- .v-mal{background:rgba(239,68,68,.15);color:var(--red)}
154
- .v-sus{background:rgba(234,179,8,.15);color:var(--yellow)}
155
- .v-low{background:rgba(134,239,172,.15);color:#86efac}
156
- .v-ok{background:rgba(34,197,94,.15);color:var(--green)}
157
-
158
- /* Risk Gauge */
159
- .risk-gauge{width:44px;height:44px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;position:relative;
160
- background:conic-gradient(var(--risk-color) 0deg,var(--risk-color) var(--risk-angle),rgba(255,255,255,.05) var(--risk-angle),rgba(255,255,255,.05) 360deg)}
161
- .risk-gauge::after{content:'';position:absolute;inset:5px;border-radius:50%;background:var(--bg)}
162
- .risk-val{position:relative;z-index:1;font-family:var(--mono);font-size:.68em;font-weight:700}
163
-
164
- /* Findings Table */
165
- .card-body{padding:0 18px 14px}
166
- .ftable{width:100%;border-collapse:collapse;font-size:.82em}
167
- .ftable th{text-align:left;font-size:.72em;font-weight:600;color:var(--t3);text-transform:uppercase;letter-spacing:.06em;padding:7px 9px;border-bottom:1px solid rgba(255,255,255,.04)}
168
- .ftable td{padding:7px 9px;border-bottom:1px solid rgba(255,255,255,.025)}
169
- .f-row{transition:background .2s}.f-row:hover{background:rgba(255,255,255,.02)}
170
- .pill{font-family:var(--mono);font-size:.7em;font-weight:600;padding:2px 5px;border-radius:3px;letter-spacing:.03em}
171
- .critical{background:rgba(239,68,68,.15);color:var(--red)}
172
- .high{background:rgba(249,115,22,.15);color:var(--orange)}
173
- .medium{background:rgba(234,179,8,.15);color:var(--yellow)}
174
- .low{background:rgba(34,197,94,.15);color:var(--green)}
175
- .mono{font-family:var(--mono);font-size:.9em}
176
- .dim{color:var(--t2)}.muted{color:var(--t3);white-space:nowrap}
177
-
178
- /* Empty */
179
- .empty{text-align:center;padding:48px 20px;background:var(--cd);backdrop-filter:blur(20px);border:1px solid var(--bdr);border-radius:var(--r)}
180
- .empty .ei{font-size:2.8em;margin-bottom:10px}
181
- .empty p{color:var(--green);font-size:1.05em;font-weight:600}
182
- .empty .es{color:var(--t3);font-size:.82em;margin-top:3px}
183
-
184
- /* Footer */
185
- .ft{text-align:center;margin-top:40px;padding-top:20px;border-top:1px solid rgba(255,255,255,.04);color:var(--t3);font-size:.78em}
186
- .ft a{color:var(--blue);text-decoration:none}.ft a:hover{text-decoration:underline}
187
-
188
- /* Animations */
189
- @keyframes fadeD{from{opacity:0;transform:translateY(-18px)}to{opacity:1;transform:translateY(0)}}
190
- @keyframes fadeU{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}
191
-
192
- /* Responsive */
193
- @media(max-width:640px){
194
- .sg{grid-template-columns:repeat(2,1fr)}.sn{font-size:1.7em}.hdr h1{font-size:1.5em}
195
- .ftable{font-size:.74em}
196
- }
197
-
198
- /* Print */
199
- @media print{
200
- body{background:#fff;color:#111}body::before{display:none}
201
- .sc,.pn,.card{background:#f8f8f8;backdrop-filter:none;border-color:#ddd}
202
- .sn{color:#111!important}.card{break-inside:avoid}
203
- }
204
- </style></head><body>
205
- <div class="wrap">
206
- <div class="hdr">
207
- <h1>🛡️ guard-scanner v${version}</h1>
208
- <div class="sub">Security Scan Report</div>
209
- <div class="ts">${new Date().toISOString()}</div>
210
- </div>
211
-
212
- <div class="sg">
213
- <div class="sc"><div class="sn">${stats.scanned}</div><div class="sl">Scanned</div></div>
214
- <div class="sc g"><div class="sn">${stats.clean}</div><div class="sl">Clean</div></div>
215
- <div class="sc l"><div class="sn">${stats.low}</div><div class="sl">Low Risk</div></div>
216
- <div class="sc s"><div class="sn">${stats.suspicious}</div><div class="sl">Suspicious</div></div>
217
- <div class="sc m"><div class="sn">${stats.malicious}</div><div class="sl">Malicious</div></div>
218
- <div class="sc r"><div class="sn">${safetyRate}%</div><div class="sl">Safety Rate</div></div>
219
- </div>
220
-
221
- ${total > 0 ? `<div class="ag">
222
- <div class="pn"><h2>Severity Distribution</h2>${sevBars}</div>
223
- <div class="pn"><h2>Top Categories</h2>${catBars}</div>
224
- </div>` : ''}
225
-
226
- <div>
227
- <div class="sec-title">Skill Analysis</div>
228
- ${cards || `<div class="empty"><div class="ei">✅</div><p>All Clear — No Threats Detected</p><div class="es">${stats.scanned} skill(s) scanned, 0 findings</div></div>`}
229
- </div>
230
-
231
- <div class="ft">
232
- guard-scanner v${version} &mdash; Lightweight runtime footprint (1 dependency: ws). 🛡️<br>
233
- Built by <a href="https://github.com/koatora20">Guava 🍈 &amp; Dee</a>
234
- </div>
235
- </div>
236
- </body></html>`;
237
- }
238
-
239
- module.exports = { generateHTML };
package/src/ioc-db.js DELETED
@@ -1,54 +0,0 @@
1
- /**
2
- * guard-scanner — Indicators of Compromise (IoC) Database
3
- *
4
- * @security-manifest
5
- * env-read: []
6
- * env-write: []
7
- * network: none
8
- * fs-read: []
9
- * fs-write: []
10
- * exec: none
11
- * purpose: IoC data definitions only — no I/O, pure data export
12
- *
13
- * Known malicious IPs, domains, URLs, usernames, filenames, and typosquats.
14
- * Sources: ClawHavoc campaign, Snyk ToxicSkills, Polymarket scams, community reports.
15
- *
16
- * Last updated: 2026-02-12
17
- */
18
-
19
- const KNOWN_MALICIOUS = {
20
- ips: [
21
- '91.92.242.30', // ClawHavoc C2
22
- ],
23
- domains: [
24
- 'webhook.site', // Common exfil endpoint
25
- 'requestbin.com', // Common exfil endpoint
26
- 'hookbin.com', // Common exfil endpoint
27
- 'pipedream.net', // Common exfil endpoint
28
- 'ngrok.io', // Tunnel (context-dependent)
29
- 'download.setup-service.com', // ClawHavoc decoy domain
30
- 'socifiapp.com', // ClawHavoc v2 AMOS C2
31
- ],
32
- urls: [
33
- 'glot.io/snippets/hfd3x9ueu5', // ClawHavoc macOS payload
34
- 'github.com/Ddoy233', // ClawHavoc payload host
35
- ],
36
- usernames: ['zaycv', 'Ddoy233', 'Sakaen736jih'], // Known malicious actors
37
- filenames: ['openclaw-agent.zip', 'openclawcli.zip'],
38
- typosquats: [
39
- // ClawHavoc campaign
40
- 'clawhub', 'clawhub1', 'clawhubb', 'clawhubcli', 'clawwhub', 'cllawhub', 'clawdhub1',
41
- // Polymarket scams
42
- 'polymarket-trader', 'polymarket-pro', 'polytrading',
43
- 'better-polymarket', 'polymarket-all-in-one',
44
- // YouTube scams
45
- 'youtube-summarize', 'youtube-thumbnail-grabber', 'youtube-video-downloader',
46
- // Misc
47
- 'auto-updater-agent', 'yahoo-finance-pro', 'x-trends-tracker',
48
- 'lost-bitcoin-finder', 'solana-wallet-tracker', 'rankaj',
49
- // Snyk ToxicSkills confirmed malicious
50
- 'moltyverse-email', 'buy-anything', 'youtube-data', 'prediction-markets-roarin',
51
- ],
52
- };
53
-
54
- module.exports = { KNOWN_MALICIOUS };