@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
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
- };
@@ -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
- };