@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.
- package/README.md +208 -42
- package/README_ja.md +252 -0
- package/SKILL.md +40 -11
- package/dist/cli.cjs +5997 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.mjs +6003 -0
- package/dist/index.cjs +4825 -0
- package/dist/index.d.mts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.mjs +4798 -0
- package/dist/mcp-server.cjs +4756 -0
- package/dist/mcp-server.d.mts +1 -0
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.mjs +4767 -0
- package/dist/openclaw-plugin.cjs +4863 -0
- package/dist/openclaw-plugin.d.mts +11 -0
- package/dist/openclaw-plugin.d.ts +11 -0
- package/dist/openclaw-plugin.mjs +4847 -34
- package/dist/types.cjs +18 -0
- package/dist/types.d.mts +215 -0
- package/dist/types.d.ts +215 -0
- package/dist/types.mjs +1 -0
- package/docs/data/benchmark-ledger.json +1428 -0
- package/docs/data/corpus-metrics.json +3 -3
- package/docs/data/fp-ledger.json +18 -0
- package/docs/data/quality-contract.json +36 -0
- package/docs/generated/openclaw-upstream-status.json +13 -13
- package/docs/openclaw-compatibility-audit.md +3 -2
- package/docs/openclaw-continuous-compatibility-plan.md +2 -1
- package/docs/spec/capabilities.json +137 -5
- package/docs/spec/plugin-trust.json +11 -0
- package/hooks/{context.js ā context.ts} +1 -0
- package/openclaw-plugin.mts +21 -5
- package/openclaw.plugin.json +2 -2
- package/package.json +58 -20
- package/src/asset-auditor.js +0 -508
- package/src/ci-reporter.js +0 -135
- package/src/cli.js +0 -434
- package/src/core/content-loader.js +0 -42
- package/src/core/inventory.js +0 -73
- package/src/core/report-adapters.js +0 -171
- package/src/core/risk-engine.js +0 -93
- package/src/core/rule-registry.js +0 -73
- package/src/core/semantic-validators.js +0 -85
- package/src/finding-schema.js +0 -191
- package/src/hooks/context.ts +0 -49
- package/src/html-template.js +0 -239
- package/src/ioc-db.js +0 -54
- package/src/mcp-server.js +0 -653
- package/src/openclaw-upstream.js +0 -128
- package/src/patterns.js +0 -629
- package/src/policy-engine.js +0 -32
- package/src/quarantine.js +0 -41
- package/src/runtime-guard.js +0 -384
- package/src/scanner.js +0 -1042
- package/src/skill-crawler.js +0 -254
- package/src/threat-model.js +0 -50
- package/src/validation-layer.js +0 -39
- package/src/vt-client.js +0 -202
- package/src/watcher.js +0 -170
package/src/cli.js
DELETED
|
@@ -1,434 +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
|
-
|
|
154
|
-
// āā crawl subcommand āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
155
|
-
} else if (args[0] === 'crawl') {
|
|
156
|
-
const { SkillCrawler } = require('./skill-crawler.js');
|
|
157
|
-
const subCmd = args[1]; // clawhub | github | url
|
|
158
|
-
const target = args[2]; // query or URL
|
|
159
|
-
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
160
|
-
const formatIdx = args.indexOf('--format');
|
|
161
|
-
const formatValue = formatIdx >= 0 ? args[formatIdx + 1] : null;
|
|
162
|
-
const quiet = !!formatValue;
|
|
163
|
-
const maxIdx = args.indexOf('--max');
|
|
164
|
-
const maxSkills = maxIdx >= 0 ? parseInt(args[maxIdx + 1], 10) : 50;
|
|
165
|
-
|
|
166
|
-
if (!subCmd || subCmd === '--help' || subCmd === '-h') {
|
|
167
|
-
console.log(`
|
|
168
|
-
š”ļø guard-scanner crawl ā Autonomous Skill Scanner
|
|
169
|
-
|
|
170
|
-
Usage:
|
|
171
|
-
guard-scanner crawl clawhub Crawl ClawHub for all SKILL.md
|
|
172
|
-
guard-scanner crawl github <query> Search GitHub for SKILL.md
|
|
173
|
-
guard-scanner crawl url <url> Scan a single SKILL.md URL
|
|
174
|
-
|
|
175
|
-
Options:
|
|
176
|
-
--verbose, -v Show detection details
|
|
177
|
-
--format json Output JSON to stdout
|
|
178
|
-
--max <n> Max skills to scan (default: 50)
|
|
179
|
-
--help, -h Show this help
|
|
180
|
-
|
|
181
|
-
Examples:
|
|
182
|
-
guard-scanner crawl clawhub --verbose
|
|
183
|
-
guard-scanner crawl github "polymarket trader" --format json
|
|
184
|
-
guard-scanner crawl url https://raw.githubusercontent.com/.../SKILL.md
|
|
185
|
-
`);
|
|
186
|
-
process.exit(0);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const crawler = new SkillCrawler({ verbose, quiet, concurrency: 5 });
|
|
190
|
-
|
|
191
|
-
(async () => {
|
|
192
|
-
try {
|
|
193
|
-
if (subCmd === 'clawhub') {
|
|
194
|
-
await crawler.crawlClawHub({ maxSkills });
|
|
195
|
-
} else if (subCmd === 'github') {
|
|
196
|
-
if (!target) { console.error('ā Usage: guard-scanner crawl github <query>'); process.exit(2); }
|
|
197
|
-
await crawler.crawlGitHub(target, { maxResults: maxSkills });
|
|
198
|
-
} else if (subCmd === 'url') {
|
|
199
|
-
if (!target) { console.error('ā Usage: guard-scanner crawl url <url>'); process.exit(2); }
|
|
200
|
-
await crawler.scanUrl(target, target);
|
|
201
|
-
} else {
|
|
202
|
-
console.error(`ā Unknown crawl target: ${subCmd}. Use: clawhub, github, or url`);
|
|
203
|
-
process.exit(2);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (formatValue === 'json') {
|
|
207
|
-
process.stdout.write(JSON.stringify(crawler.toJSON(), null, 2) + '\n');
|
|
208
|
-
} else {
|
|
209
|
-
crawler.printSummary();
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const summary = crawler.getSummary();
|
|
213
|
-
process.exit(summary.unsafe > 0 ? 1 : 0);
|
|
214
|
-
} catch (e) {
|
|
215
|
-
console.error(`ā Crawl error: ${e.message}`);
|
|
216
|
-
process.exit(2);
|
|
217
|
-
}
|
|
218
|
-
})();
|
|
219
|
-
|
|
220
|
-
// āā patrol subcommand āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
221
|
-
} else if (args[0] === 'patrol') {
|
|
222
|
-
const { SkillCrawler } = require('./skill-crawler.js');
|
|
223
|
-
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
224
|
-
const once = args.includes('--once');
|
|
225
|
-
const intervalIdx = args.indexOf('--interval');
|
|
226
|
-
const intervalSecs = intervalIdx >= 0 ? parseInt(args[intervalIdx + 1], 10) : 3600;
|
|
227
|
-
const formatIdx = args.indexOf('--format');
|
|
228
|
-
const formatValue = formatIdx >= 0 ? args[formatIdx + 1] : null;
|
|
229
|
-
|
|
230
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
231
|
-
console.log(`
|
|
232
|
-
š”ļø guard-scanner patrol ā Autonomous Security Patrol
|
|
233
|
-
|
|
234
|
-
Usage:
|
|
235
|
-
guard-scanner patrol --once Run one patrol cycle
|
|
236
|
-
guard-scanner patrol --interval 3600 Patrol every N seconds
|
|
237
|
-
|
|
238
|
-
Options:
|
|
239
|
-
--once Run once and exit
|
|
240
|
-
--interval <secs> Polling interval (default: 3600 = 1 hour)
|
|
241
|
-
--verbose, -v Show detection details
|
|
242
|
-
--format json Output JSON
|
|
243
|
-
--help, -h Show this help
|
|
244
|
-
`);
|
|
245
|
-
process.exit(0);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
async function runPatrol() {
|
|
249
|
-
const timestamp = new Date().toISOString();
|
|
250
|
-
console.log(`\nšØ Patrol cycle starting at ${timestamp}`);
|
|
251
|
-
console.log('ā'.repeat(54));
|
|
252
|
-
|
|
253
|
-
const crawler = new SkillCrawler({ verbose, quiet: !!formatValue, concurrency: 5 });
|
|
254
|
-
await crawler.crawlClawHub({ maxSkills: 50 });
|
|
255
|
-
|
|
256
|
-
if (formatValue === 'json') {
|
|
257
|
-
process.stdout.write(JSON.stringify(crawler.toJSON(), null, 2) + '\n');
|
|
258
|
-
} else {
|
|
259
|
-
crawler.printSummary();
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const summary = crawler.getSummary();
|
|
263
|
-
if (summary.unsafe > 0) {
|
|
264
|
-
console.log(`\nā ļø ${summary.unsafe} unsafe skill(s) detected!`);
|
|
265
|
-
}
|
|
266
|
-
return summary;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
(async () => {
|
|
270
|
-
try {
|
|
271
|
-
if (once) {
|
|
272
|
-
const summary = await runPatrol();
|
|
273
|
-
process.exit(summary.unsafe > 0 ? 1 : 0);
|
|
274
|
-
} else {
|
|
275
|
-
console.log(`š”ļø guard-scanner patrol mode ā interval: ${intervalSecs}s`);
|
|
276
|
-
console.log(` Press Ctrl+C to stop\n`);
|
|
277
|
-
await runPatrol();
|
|
278
|
-
setInterval(async () => {
|
|
279
|
-
await runPatrol();
|
|
280
|
-
}, intervalSecs * 1000);
|
|
281
|
-
}
|
|
282
|
-
} catch (e) {
|
|
283
|
-
console.error(`ā Patrol error: ${e.message}`);
|
|
284
|
-
process.exit(2);
|
|
285
|
-
}
|
|
286
|
-
})();
|
|
287
|
-
|
|
288
|
-
} else {
|
|
289
|
-
// āā existing scan logic (backward compatible) āāāāāāāāāāāāāāāāā
|
|
290
|
-
|
|
291
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
292
|
-
console.log(`
|
|
293
|
-
š”ļø guard-scanner v${VERSION} ā Agent Skill Security Scanner
|
|
294
|
-
|
|
295
|
-
Usage: guard-scanner [scan-dir] [options]
|
|
296
|
-
guard-scanner serve Start as MCP server (stdio)
|
|
297
|
-
guard-scanner audit <npm|github|clawhub|all> <username> [options]
|
|
298
|
-
|
|
299
|
-
Options:
|
|
300
|
-
--verbose, -v Detailed findings with categories and samples
|
|
301
|
-
--json Write JSON report to file
|
|
302
|
-
--sarif Write SARIF report to file (GitHub Code Scanning / CI/CD)
|
|
303
|
-
--html Write HTML report (visual dashboard)
|
|
304
|
-
--format json|sarif Print JSON or SARIF to stdout (pipeable, v3.2.0)
|
|
305
|
-
--quiet Suppress all text output (use with --format for clean pipes)
|
|
306
|
-
--self-exclude Skip scanning the guard-scanner skill itself
|
|
307
|
-
--strict Lower detection thresholds (more sensitive)
|
|
308
|
-
--summary-only Only print the summary table
|
|
309
|
-
--check-deps Scan package.json for dependency chain risks
|
|
310
|
-
--soul-lock Enable Soul Lock patterns (agent identity protection)
|
|
311
|
-
--rules <file> Load custom rules from JSON file
|
|
312
|
-
--plugin <file> Load plugin module (JS file exporting { name, patterns })
|
|
313
|
-
--fail-on-findings Exit code 1 if any findings (CI/CD)
|
|
314
|
-
--help, -h Show this help
|
|
315
|
-
--version, -V Show version
|
|
316
|
-
|
|
317
|
-
Custom Rules JSON Format:
|
|
318
|
-
[
|
|
319
|
-
{
|
|
320
|
-
"id": "CUSTOM_001",
|
|
321
|
-
"pattern": "dangerous_function\\\\(",
|
|
322
|
-
"flags": "gi",
|
|
323
|
-
"severity": "HIGH",
|
|
324
|
-
"cat": "malicious-code",
|
|
325
|
-
"desc": "Custom: dangerous function call",
|
|
326
|
-
"codeOnly": true
|
|
327
|
-
}
|
|
328
|
-
]
|
|
329
|
-
|
|
330
|
-
Plugin API:
|
|
331
|
-
// my-plugin.js
|
|
332
|
-
module.exports = {
|
|
333
|
-
name: 'my-plugin',
|
|
334
|
-
patterns: [
|
|
335
|
-
{ id: 'MY_01', cat: 'custom', regex: /pattern/g, severity: 'HIGH', desc: 'Description', all: true }
|
|
336
|
-
]
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
Examples:
|
|
340
|
-
guard-scanner ./skills/ --verbose --self-exclude
|
|
341
|
-
guard-scanner ./skills/ --strict --json --sarif --check-deps
|
|
342
|
-
guard-scanner ./skills/ --html --verbose --check-deps
|
|
343
|
-
guard-scanner ./skills/ --rules my-rules.json --fail-on-findings
|
|
344
|
-
guard-scanner ./skills/ --plugin ./my-plugin.js
|
|
345
|
-
`);
|
|
346
|
-
process.exit(0);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
350
|
-
const jsonOutput = args.includes('--json');
|
|
351
|
-
const sarifOutput = args.includes('--sarif');
|
|
352
|
-
const htmlOutput = args.includes('--html');
|
|
353
|
-
const selfExclude = args.includes('--self-exclude');
|
|
354
|
-
const strict = args.includes('--strict');
|
|
355
|
-
const summaryOnly = args.includes('--summary-only');
|
|
356
|
-
const checkDeps = args.includes('--check-deps');
|
|
357
|
-
const soulLock = args.includes('--soul-lock');
|
|
358
|
-
const failOnFindings = args.includes('--fail-on-findings');
|
|
359
|
-
const quietMode = args.includes('--quiet');
|
|
360
|
-
|
|
361
|
-
// --format json|sarif ā stdout output (v3.2.0)
|
|
362
|
-
const formatIdx = args.indexOf('--format');
|
|
363
|
-
const formatValue = formatIdx >= 0 ? args[formatIdx + 1] : null;
|
|
364
|
-
|
|
365
|
-
const rulesIdx = args.indexOf('--rules');
|
|
366
|
-
const rulesFile = rulesIdx >= 0 ? args[rulesIdx + 1] : null;
|
|
367
|
-
|
|
368
|
-
// Collect plugins
|
|
369
|
-
const plugins = [];
|
|
370
|
-
let idx = 0;
|
|
371
|
-
while (idx < args.length) {
|
|
372
|
-
if (args[idx] === '--plugin' && args[idx + 1]) {
|
|
373
|
-
plugins.push(args[idx + 1]);
|
|
374
|
-
idx += 2;
|
|
375
|
-
} else {
|
|
376
|
-
idx++;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const scanDir = args.find(a =>
|
|
381
|
-
!a.startsWith('-') &&
|
|
382
|
-
a !== rulesFile &&
|
|
383
|
-
a !== formatValue &&
|
|
384
|
-
!plugins.includes(a)
|
|
385
|
-
) || process.cwd();
|
|
386
|
-
|
|
387
|
-
try {
|
|
388
|
-
const scanner = new GuardScanner({
|
|
389
|
-
verbose, selfExclude, strict, summaryOnly, checkDeps, soulLock, rulesFile, plugins,
|
|
390
|
-
quiet: quietMode || !!formatValue,
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
scanner.scanDirectory(scanDir);
|
|
394
|
-
|
|
395
|
-
// Output reports (file-based, backward compatible)
|
|
396
|
-
if (jsonOutput) {
|
|
397
|
-
const report = scanner.toJSON();
|
|
398
|
-
const outPath = path.join(scanDir, 'guard-scanner-report.json');
|
|
399
|
-
fs.writeFileSync(outPath, JSON.stringify(report, null, 2));
|
|
400
|
-
if (!quietMode && !formatValue) console.log(`\nš JSON report: ${outPath}`);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if (sarifOutput) {
|
|
404
|
-
const outPath = path.join(scanDir, 'guard-scanner.sarif');
|
|
405
|
-
fs.writeFileSync(outPath, JSON.stringify(scanner.toSARIF(scanDir), null, 2));
|
|
406
|
-
if (!quietMode && !formatValue) console.log(`\nš SARIF report: ${outPath}`);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (htmlOutput) {
|
|
410
|
-
const outPath = path.join(scanDir, 'guard-scanner-report.html');
|
|
411
|
-
fs.writeFileSync(outPath, scanner.toHTML());
|
|
412
|
-
if (!quietMode && !formatValue) console.log(`\nš HTML report: ${outPath}`);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// --format stdout output (v3.2.0)
|
|
416
|
-
if (formatValue === 'json') {
|
|
417
|
-
process.stdout.write(JSON.stringify(scanner.toJSON(), null, 2) + '\n');
|
|
418
|
-
} else if (formatValue === 'sarif') {
|
|
419
|
-
process.stdout.write(JSON.stringify(scanner.toSARIF(scanDir), null, 2) + '\n');
|
|
420
|
-
} else if (formatValue) {
|
|
421
|
-
console.error(`ā Unknown format: ${formatValue}. Use 'json' or 'sarif'.`);
|
|
422
|
-
process.exit(2);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Exit codes
|
|
426
|
-
if (scanner.stats.malicious > 0) process.exit(1);
|
|
427
|
-
if (failOnFindings && scanner.findings.length > 0) process.exit(1);
|
|
428
|
-
process.exit(0);
|
|
429
|
-
} catch (error) {
|
|
430
|
-
console.error(`ā ${error.message}`);
|
|
431
|
-
process.exit(2);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
} // end else (scan mode)
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
function loadIgnoreFile(scanDir) {
|
|
7
|
-
const ignoredSkills = new Set();
|
|
8
|
-
const ignoredPatterns = new Set();
|
|
9
|
-
const ignorePaths = [
|
|
10
|
-
path.join(scanDir, '.guard-scanner-ignore'),
|
|
11
|
-
path.join(scanDir, '.guava-guard-ignore'),
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
for (const ignorePath of ignorePaths) {
|
|
15
|
-
if (!fs.existsSync(ignorePath)) continue;
|
|
16
|
-
const lines = fs.readFileSync(ignorePath, 'utf-8').split('\n');
|
|
17
|
-
for (const line of lines) {
|
|
18
|
-
const trimmed = line.trim();
|
|
19
|
-
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
20
|
-
if (trimmed.startsWith('pattern:')) ignoredPatterns.add(trimmed.replace('pattern:', '').trim());
|
|
21
|
-
else ignoredSkills.add(trimmed);
|
|
22
|
-
}
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return { ignoredSkills, ignoredPatterns };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function loadTextFile(filePath, maxLength = 500000) {
|
|
30
|
-
try {
|
|
31
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
32
|
-
if (content.length > maxLength) return null;
|
|
33
|
-
return content;
|
|
34
|
-
} catch {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = {
|
|
40
|
-
loadIgnoreFile,
|
|
41
|
-
loadTextFile,
|
|
42
|
-
};
|
package/src/core/inventory.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
const CODE_EXTENSIONS = new Set(['.js', '.ts', '.mjs', '.cjs', '.py', '.sh', '.bash', '.ps1', '.rb', '.go', '.rs', '.php', '.pl']);
|
|
7
|
-
const DOC_EXTENSIONS = new Set(['.md', '.txt', '.rst', '.adoc']);
|
|
8
|
-
const DATA_EXTENSIONS = new Set(['.json', '.yaml', '.yml', '.toml', '.xml', '.csv']);
|
|
9
|
-
const BINARY_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.ico', '.woff', '.woff2', '.ttf', '.eot', '.wasm', '.wav', '.mp3', '.mp4', '.webm', '.ogg', '.pdf', '.zip', '.tar', '.gz', '.bz2', '.7z', '.exe', '.dll', '.so', '.dylib']);
|
|
10
|
-
const GENERATED_REPORT_FILES = new Set(['guard-scanner-report.json', 'guard-scanner-report.html', 'guard-scanner.sarif']);
|
|
11
|
-
|
|
12
|
-
function classifyFile(ext, relFile) {
|
|
13
|
-
if (CODE_EXTENSIONS.has(ext)) return 'code';
|
|
14
|
-
if (DOC_EXTENSIONS.has(ext)) return 'doc';
|
|
15
|
-
if (DATA_EXTENSIONS.has(ext)) return 'data';
|
|
16
|
-
const base = path.basename(relFile).toLowerCase();
|
|
17
|
-
if (base === 'skill.md' || base === 'readme.md') return 'skill-doc';
|
|
18
|
-
return 'other';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function isSelfNoisePath(skillName, relFile) {
|
|
22
|
-
if (skillName !== 'guard-scanner') return false;
|
|
23
|
-
return /^test\//.test(relFile)
|
|
24
|
-
|| /^dist\/__tests__\//.test(relFile)
|
|
25
|
-
|| /^ts-src\/__tests__\//.test(relFile)
|
|
26
|
-
|| /^docs\//.test(relFile)
|
|
27
|
-
|| relFile === 'ROADMAP-RESEARCH.md'
|
|
28
|
-
|| relFile === 'CHANGELOG.md';
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isSelfThreatCorpus(skillName, relFile) {
|
|
32
|
-
if (skillName !== 'guard-scanner') return false;
|
|
33
|
-
return /(^|\/)(ioc-db|patterns)\.(js|ts)$/.test(relFile);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function getFiles(dir) {
|
|
37
|
-
const results = [];
|
|
38
|
-
try {
|
|
39
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
40
|
-
for (const entry of entries) {
|
|
41
|
-
const fullPath = path.join(dir, entry.name);
|
|
42
|
-
if (entry.isDirectory()) {
|
|
43
|
-
if (entry.name === '.git' || entry.name === 'node_modules') continue;
|
|
44
|
-
results.push(...getFiles(fullPath));
|
|
45
|
-
} else {
|
|
46
|
-
const baseName = entry.name.toLowerCase();
|
|
47
|
-
if (GENERATED_REPORT_FILES.has(baseName)) continue;
|
|
48
|
-
results.push(fullPath);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
} catch { }
|
|
52
|
-
return results;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function listSkills(dir) {
|
|
56
|
-
return fs.readdirSync(dir).filter((file) => {
|
|
57
|
-
const fullPath = path.join(dir, file);
|
|
58
|
-
return fs.statSync(fullPath).isDirectory();
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
module.exports = {
|
|
63
|
-
CODE_EXTENSIONS,
|
|
64
|
-
DOC_EXTENSIONS,
|
|
65
|
-
DATA_EXTENSIONS,
|
|
66
|
-
BINARY_EXTENSIONS,
|
|
67
|
-
GENERATED_REPORT_FILES,
|
|
68
|
-
classifyFile,
|
|
69
|
-
isSelfNoisePath,
|
|
70
|
-
isSelfThreatCorpus,
|
|
71
|
-
getFiles,
|
|
72
|
-
listSkills,
|
|
73
|
-
};
|