@guava-parity/guard-scanner 13.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 (96) hide show
  1. package/README.md +170 -215
  2. package/README_ja.md +252 -0
  3. package/SECURITY.md +12 -4
  4. package/SKILL.md +148 -57
  5. package/dist/cli.cjs +5997 -0
  6. package/dist/cli.d.mts +1 -0
  7. package/dist/cli.d.ts +1 -0
  8. package/dist/cli.mjs +6003 -0
  9. package/dist/index.cjs +4825 -0
  10. package/dist/index.d.mts +17 -0
  11. package/dist/index.d.ts +17 -0
  12. package/dist/index.mjs +4798 -0
  13. package/dist/mcp-server.cjs +4756 -0
  14. package/dist/mcp-server.d.mts +1 -0
  15. package/dist/mcp-server.d.ts +1 -0
  16. package/dist/mcp-server.mjs +4767 -0
  17. package/dist/openclaw-plugin.cjs +4863 -0
  18. package/dist/openclaw-plugin.d.mts +11 -0
  19. package/dist/openclaw-plugin.d.ts +11 -0
  20. package/dist/openclaw-plugin.mjs +4854 -0
  21. package/dist/types.cjs +18 -0
  22. package/dist/types.d.mts +215 -0
  23. package/dist/types.d.ts +215 -0
  24. package/dist/types.mjs +1 -0
  25. package/docs/EVIDENCE_DRIVEN.md +182 -0
  26. package/docs/banner.png +0 -0
  27. package/docs/data/benchmark-ledger.json +1428 -0
  28. package/docs/data/corpus-metrics.json +11 -0
  29. package/docs/data/fp-ledger.json +18 -0
  30. package/docs/data/latest.json +25837 -2481
  31. package/docs/data/quality-contract.json +36 -0
  32. package/docs/generated/npm-audit-20260312.json +96 -0
  33. package/docs/generated/openclaw-upstream-status.json +25 -0
  34. package/docs/glossary.md +46 -0
  35. package/docs/index.html +1085 -496
  36. package/docs/logo.png +0 -0
  37. package/docs/openclaw-compatibility-audit.md +45 -0
  38. package/docs/openclaw-continuous-compatibility-plan.md +37 -0
  39. package/docs/rules/a2a-contagion.md +68 -0
  40. package/docs/rules/advanced-exfil.md +52 -0
  41. package/docs/rules/agent-protocol.md +108 -0
  42. package/docs/rules/api-abuse.md +68 -0
  43. package/docs/rules/autonomous-risk.md +92 -0
  44. package/docs/rules/config-impact.md +132 -0
  45. package/docs/rules/credential-handling.md +100 -0
  46. package/docs/rules/cve-patterns.md +332 -0
  47. package/docs/rules/data-exposure.md +84 -0
  48. package/docs/rules/exfiltration.md +36 -0
  49. package/docs/rules/financial-access.md +84 -0
  50. package/docs/rules/identity-hijack.md +140 -0
  51. package/docs/rules/inference-manipulation.md +60 -0
  52. package/docs/rules/leaky-skills.md +52 -0
  53. package/docs/rules/malicious-code.md +108 -0
  54. package/docs/rules/mcp-security.md +148 -0
  55. package/docs/rules/memory-poisoning.md +84 -0
  56. package/docs/rules/model-poisoning.md +44 -0
  57. package/docs/rules/obfuscation.md +60 -0
  58. package/docs/rules/persistence.md +108 -0
  59. package/docs/rules/pii-exposure.md +116 -0
  60. package/docs/rules/prompt-injection.md +148 -0
  61. package/docs/rules/prompt-worm.md +44 -0
  62. package/docs/rules/safeguard-bypass.md +44 -0
  63. package/docs/rules/sandbox-escape.md +100 -0
  64. package/docs/rules/secret-detection.md +44 -0
  65. package/docs/rules/supply-chain-v2.md +92 -0
  66. package/docs/rules/suspicious-download.md +60 -0
  67. package/docs/rules/trust-boundary.md +76 -0
  68. package/docs/rules/trust-exploitation.md +92 -0
  69. package/docs/rules/unverifiable-deps.md +84 -0
  70. package/docs/rules/vdb-injection.md +84 -0
  71. package/docs/security-vulnerability-report-20260312.md +53 -0
  72. package/docs/spec/PRD_V2_ARCHITECTURE.md +55 -0
  73. package/docs/spec/capabilities.json +174 -0
  74. package/docs/spec/finding.schema.json +104 -0
  75. package/docs/spec/integration-manifest.md +39 -0
  76. package/docs/spec/plugin-trust.json +11 -0
  77. package/docs/spec/sbom.json +33 -0
  78. package/docs/threat-model.md +65 -0
  79. package/docs/v13-architecture-manifest.md +55 -0
  80. package/hooks/context.ts +306 -0
  81. package/hooks/guard-scanner/plugin.ts +24 -1
  82. package/openclaw-plugin.mts +107 -0
  83. package/openclaw.plugin.json +30 -53
  84. package/package.json +66 -13
  85. package/src/asset-auditor.js +0 -508
  86. package/src/ci-reporter.js +0 -135
  87. package/src/cli.js +0 -294
  88. package/src/html-template.js +0 -239
  89. package/src/ioc-db.js +0 -54
  90. package/src/mcp-server.js +0 -702
  91. package/src/patterns.js +0 -611
  92. package/src/quarantine.js +0 -41
  93. package/src/runtime-guard.js +0 -346
  94. package/src/scanner.js +0 -1157
  95. package/src/vt-client.js +0 -202
  96. package/src/watcher.js +0 -170
package/src/cli.js DELETED
@@ -1,294 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * guard-scanner CLI
4
- *
5
- * @security-manifest
6
- * env-read: []
7
- * env-write: []
8
- * network: none
9
- * fs-read: [scan target directory, plugin files, custom rules files]
10
- * fs-write: [JSON/SARIF/HTML reports to scan directory]
11
- * exec: none
12
- * purpose: CLI entry point for guard-scanner static analysis
13
- *
14
- * Usage: guard-scanner [scan-dir] [options]
15
- *
16
- * Options:
17
- * --verbose, -v Detailed findings
18
- * --json JSON report
19
- * --sarif SARIF report (CI/CD)
20
- * --html HTML report
21
- * --self-exclude Skip scanning self
22
- * --strict Lower thresholds
23
- * --summary-only Summary only
24
- * --check-deps Scan dependencies
25
- * --rules <file> Custom rules JSON
26
- * --plugin <file> Load plugin module
27
- * --fail-on-findings Exit 1 on findings (CI/CD)
28
- * --help, -h Help
29
- */
30
-
31
- const fs = require('fs');
32
- const path = require('path');
33
- const { GuardScanner, VERSION } = require('./scanner.js');
34
- const { AssetAuditor, AUDIT_VERSION } = require('./asset-auditor.js');
35
- const { VTClient } = require('./vt-client.js');
36
- const { GuardWatcher } = require('./watcher.js');
37
- const { CIReporter } = require('./ci-reporter.js');
38
-
39
- const args = process.argv.slice(2);
40
-
41
- if (args.includes('--version') || args.includes('-V')) {
42
- console.log(`guard-scanner v${VERSION} (audit v${AUDIT_VERSION})`);
43
- process.exit(0);
44
- }
45
-
46
- // ── serve subcommand (MCP server) ────────────────────────────
47
- if (args[0] === 'serve') {
48
- const { startServer } = require('./mcp-server.js');
49
- startServer();
50
- // Server runs until stdin closes
51
- }
52
-
53
- // ── watch subcommand ──────────────────────────────────────────
54
- if (args[0] === 'watch') {
55
- const watchDir = args[1] || '.';
56
- const verbose = args.includes('--verbose') || args.includes('-v');
57
- const strict = args.includes('--strict');
58
- const soulLock = args.includes('--soul-lock');
59
-
60
- const watcher = new GuardWatcher({ verbose, strict, soulLock });
61
-
62
- process.on('SIGINT', () => {
63
- const stats = watcher.stop();
64
- console.log(`\n📊 Session: ${stats.scanCount} scans, ${stats.alertCount} alerts`);
65
- process.exit(0);
66
- });
67
-
68
- watcher.watch(watchDir);
69
- // Keep process alive
70
- }
71
-
72
- // ── audit subcommand ──────────────────────────────────────────
73
- if (args[0] === 'audit') {
74
- const subCmd = args[1]; // npm | github | clawhub | all
75
- const target = args[2]; // username or query
76
- const verbose = args.includes('--verbose') || args.includes('-v');
77
- const formatIdx = args.indexOf('--format');
78
- const formatValue = formatIdx >= 0 ? args[formatIdx + 1] : null;
79
- const quiet = args.includes('--quiet') || !!formatValue;
80
-
81
- if (!subCmd || subCmd === '--help' || subCmd === '-h') {
82
- console.log(`
83
- 🛡️ guard-scanner audit v${AUDIT_VERSION} — Asset Audit Platform
84
-
85
- Usage:
86
- guard-scanner audit npm <username> Audit npm packages for leaks
87
- guard-scanner audit github <username> Audit GitHub repos for exposure
88
- guard-scanner audit clawhub <query> Audit ClawHub skills for threats
89
- guard-scanner audit all <username> Run all audits
90
-
91
- Options:
92
- --verbose, -v Detailed alert output
93
- --format json|sarif Print report to stdout (pipeable)
94
- --quiet Suppress text output
95
- --help, -h Show this help
96
-
97
- Examples:
98
- guard-scanner audit npm koatora20 --verbose
99
- guard-scanner audit github koatora20 --format json
100
- guard-scanner audit all koatora20 --verbose
101
- `);
102
- process.exit(0);
103
- }
104
-
105
- if (!target && subCmd !== 'all') {
106
- console.error(`❌ Usage: guard-scanner audit ${subCmd} <username>`);
107
- process.exit(2);
108
- }
109
-
110
- const vtScan = args.includes('--vt-scan');
111
- let vtClient = null;
112
- if (vtScan) {
113
- const vtKey = process.env.VT_API_KEY;
114
- if (!vtKey) { console.error('❌ --vt-scan requires VT_API_KEY environment variable'); process.exit(2); }
115
- vtClient = new VTClient(vtKey, { verbose });
116
- }
117
-
118
- const auditor = new AssetAuditor({ verbose, format: formatValue, quiet, vtClient });
119
-
120
- (async () => {
121
- try {
122
- if (subCmd === 'npm' || subCmd === 'all') {
123
- await auditor.auditNpm(target);
124
- }
125
- if (subCmd === 'github' || subCmd === 'all') {
126
- await auditor.auditGithub(target);
127
- }
128
- if (subCmd === 'clawhub' || subCmd === 'all') {
129
- await auditor.auditClawHub(target);
130
- }
131
-
132
- if (!['npm', 'github', 'clawhub', 'all'].includes(subCmd)) {
133
- console.error(`❌ Unknown audit target: ${subCmd}. Use: npm, github, clawhub, or all`);
134
- process.exit(2);
135
- }
136
-
137
- // Output
138
- if (formatValue === 'json') {
139
- process.stdout.write(JSON.stringify(auditor.toJSON(), null, 2) + '\n');
140
- } else if (formatValue === 'sarif') {
141
- process.stdout.write(JSON.stringify(auditor.toSARIF(), null, 2) + '\n');
142
- } else {
143
- auditor.printSummary();
144
- }
145
-
146
- const verdict = auditor.getVerdict();
147
- process.exit(verdict.exitCode);
148
- } catch (e) {
149
- console.error(`❌ Audit error: ${e.message}`);
150
- process.exit(2);
151
- }
152
- })();
153
- } else {
154
- // ── existing scan logic (backward compatible) ─────────────────
155
-
156
- if (args.includes('--help') || args.includes('-h')) {
157
- console.log(`
158
- 🛡️ guard-scanner v${VERSION} — Agent Skill Security Scanner
159
-
160
- Usage: guard-scanner [scan-dir] [options]
161
- guard-scanner serve Start as MCP server (stdio)
162
- guard-scanner audit <npm|github|clawhub|all> <username> [options]
163
-
164
- Options:
165
- --verbose, -v Detailed findings with categories and samples
166
- --json Write JSON report to file
167
- --sarif Write SARIF report to file (GitHub Code Scanning / CI/CD)
168
- --html Write HTML report (visual dashboard)
169
- --format json|sarif Print JSON or SARIF to stdout (pipeable, v3.2.0)
170
- --quiet Suppress all text output (use with --format for clean pipes)
171
- --self-exclude Skip scanning the guard-scanner skill itself
172
- --strict Lower detection thresholds (more sensitive)
173
- --summary-only Only print the summary table
174
- --check-deps Scan package.json for dependency chain risks
175
- --soul-lock Enable Soul Lock patterns (agent identity protection)
176
- --rules <file> Load custom rules from JSON file
177
- --plugin <file> Load plugin module (JS file exporting { name, patterns })
178
- --fail-on-findings Exit code 1 if any findings (CI/CD)
179
- --help, -h Show this help
180
- --version, -V Show version
181
-
182
- Custom Rules JSON Format:
183
- [
184
- {
185
- "id": "CUSTOM_001",
186
- "pattern": "dangerous_function\\\\(",
187
- "flags": "gi",
188
- "severity": "HIGH",
189
- "cat": "malicious-code",
190
- "desc": "Custom: dangerous function call",
191
- "codeOnly": true
192
- }
193
- ]
194
-
195
- Plugin API:
196
- // my-plugin.js
197
- module.exports = {
198
- name: 'my-plugin',
199
- patterns: [
200
- { id: 'MY_01', cat: 'custom', regex: /pattern/g, severity: 'HIGH', desc: 'Description', all: true }
201
- ]
202
- };
203
-
204
- Examples:
205
- guard-scanner ./skills/ --verbose --self-exclude
206
- guard-scanner ./skills/ --strict --json --sarif --check-deps
207
- guard-scanner ./skills/ --html --verbose --check-deps
208
- guard-scanner ./skills/ --rules my-rules.json --fail-on-findings
209
- guard-scanner ./skills/ --plugin ./my-plugin.js
210
- `);
211
- process.exit(0);
212
- }
213
-
214
- const verbose = args.includes('--verbose') || args.includes('-v');
215
- const jsonOutput = args.includes('--json');
216
- const sarifOutput = args.includes('--sarif');
217
- const htmlOutput = args.includes('--html');
218
- const selfExclude = args.includes('--self-exclude');
219
- const strict = args.includes('--strict');
220
- const summaryOnly = args.includes('--summary-only');
221
- const checkDeps = args.includes('--check-deps');
222
- const soulLock = args.includes('--soul-lock');
223
- const failOnFindings = args.includes('--fail-on-findings');
224
- const quietMode = args.includes('--quiet');
225
-
226
- // --format json|sarif → stdout output (v3.2.0)
227
- const formatIdx = args.indexOf('--format');
228
- const formatValue = formatIdx >= 0 ? args[formatIdx + 1] : null;
229
-
230
- const rulesIdx = args.indexOf('--rules');
231
- const rulesFile = rulesIdx >= 0 ? args[rulesIdx + 1] : null;
232
-
233
- // Collect plugins
234
- const plugins = [];
235
- let idx = 0;
236
- while (idx < args.length) {
237
- if (args[idx] === '--plugin' && args[idx + 1]) {
238
- plugins.push(args[idx + 1]);
239
- idx += 2;
240
- } else {
241
- idx++;
242
- }
243
- }
244
-
245
- const scanDir = args.find(a =>
246
- !a.startsWith('-') &&
247
- a !== rulesFile &&
248
- a !== formatValue &&
249
- !plugins.includes(a)
250
- ) || process.cwd();
251
-
252
- const scanner = new GuardScanner({
253
- verbose, selfExclude, strict, summaryOnly, checkDeps, soulLock, rulesFile, plugins,
254
- quiet: quietMode || !!formatValue,
255
- });
256
-
257
- scanner.scanDirectory(scanDir);
258
-
259
- // Output reports (file-based, backward compatible)
260
- if (jsonOutput) {
261
- const report = scanner.toJSON();
262
- const outPath = path.join(scanDir, 'guard-scanner-report.json');
263
- fs.writeFileSync(outPath, JSON.stringify(report, null, 2));
264
- if (!quietMode && !formatValue) console.log(`\n📄 JSON report: ${outPath}`);
265
- }
266
-
267
- if (sarifOutput) {
268
- const outPath = path.join(scanDir, 'guard-scanner.sarif');
269
- fs.writeFileSync(outPath, JSON.stringify(scanner.toSARIF(scanDir), null, 2));
270
- if (!quietMode && !formatValue) console.log(`\n📄 SARIF report: ${outPath}`);
271
- }
272
-
273
- if (htmlOutput) {
274
- const outPath = path.join(scanDir, 'guard-scanner-report.html');
275
- fs.writeFileSync(outPath, scanner.toHTML());
276
- if (!quietMode && !formatValue) console.log(`\n📄 HTML report: ${outPath}`);
277
- }
278
-
279
- // --format stdout output (v3.2.0)
280
- if (formatValue === 'json') {
281
- process.stdout.write(JSON.stringify(scanner.toJSON(), null, 2) + '\n');
282
- } else if (formatValue === 'sarif') {
283
- process.stdout.write(JSON.stringify(scanner.toSARIF(scanDir), null, 2) + '\n');
284
- } else if (formatValue) {
285
- console.error(`❌ Unknown format: ${formatValue}. Use 'json' or 'sarif'.`);
286
- process.exit(2);
287
- }
288
-
289
- // Exit codes
290
- if (scanner.stats.malicious > 0) process.exit(1);
291
- if (failOnFindings && scanner.findings.length > 0) process.exit(1);
292
- process.exit(0);
293
-
294
- } // end else (scan mode)
@@ -1,239 +0,0 @@
1
- /**
2
- * guard-scanner — HTML Report Template
3
- * Dark Glassmorphism + Conic-gradient Risk Gauges
4
- * Zero dependencies. 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; Zero dependencies. Zero compromises. 🛡️<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 };