skill-security-scanner 0.1.1 → 0.1.3
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 +61 -3
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -0
- package/package.json +10 -7
- package/TODO.md +0 -57
- package/src/index.ts +0 -660
- package/tsconfig.json +0 -25
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
| `scan-all` | Security scan every skill in the project |
|
|
13
13
|
| `lint <skill>` | Validate SKILL.md frontmatter schema |
|
|
14
14
|
| `lint --all` | Validate every skill in the project |
|
|
15
|
+
| `info <skill>` | Pretty-print parsed frontmatter for debugging |
|
|
15
16
|
|
|
16
17
|
---
|
|
17
18
|
|
|
@@ -147,7 +148,7 @@ npx skill-security-scanner lint --all --strict
|
|
|
147
148
|
### Lint Output Example
|
|
148
149
|
|
|
149
150
|
```
|
|
150
|
-
|
|
151
|
+
🔎 Lint — voiceover-bot
|
|
151
152
|
──────────────────────────────────────────────────
|
|
152
153
|
Status: ❌ FAIL
|
|
153
154
|
|
|
@@ -161,6 +162,63 @@ Warnings (1)
|
|
|
161
162
|
|
|
162
163
|
---
|
|
163
164
|
|
|
165
|
+
## `info` — Frontmatter Inspector
|
|
166
|
+
|
|
167
|
+
Pretty-prints everything the scanner sees when it parses a skill's `SKILL.md`. Useful for debugging mismatches between what you wrote and what the tool reads.
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
npx skill-security-scanner info frontend-design
|
|
171
|
+
npx skill-security-scanner info .agent/skills/voiceover-bot
|
|
172
|
+
|
|
173
|
+
# Machine-readable output
|
|
174
|
+
npx skill-security-scanner info frontend-design --json
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### What It Shows
|
|
178
|
+
|
|
179
|
+
| Section | Details |
|
|
180
|
+
|---|---|
|
|
181
|
+
| **📦 Identity** | `name`, `description`, `version`, resolved path |
|
|
182
|
+
| **🔐 Permissions** | Each scope color-coded by risk — 🚨 high, ⚠️ med, ✅ low |
|
|
183
|
+
| **🗂️ Requires** | `bin` (flags dangerous entries), `env` (flags sensitive names), `config` |
|
|
184
|
+
| **⚡ Command Dispatch** | Registered command → handler mappings |
|
|
185
|
+
|
|
186
|
+
### Info Flags
|
|
187
|
+
|
|
188
|
+
| Flag | Description |
|
|
189
|
+
|---|---|
|
|
190
|
+
| `--json` | Output `{ path, frontmatter }` as JSON |
|
|
191
|
+
|
|
192
|
+
### Info Output Example
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
📦 Skill Info — voiceover-bot
|
|
196
|
+
──────────────────────────────────────────────────────
|
|
197
|
+
Name voiceover-bot
|
|
198
|
+
Description Converts text to speech using ElevenLabs
|
|
199
|
+
Version 1.2.0
|
|
200
|
+
Path /project/.agent/skills/voiceover-bot
|
|
201
|
+
|
|
202
|
+
🔐 Permissions
|
|
203
|
+
🚨 shell:execute
|
|
204
|
+
⚠️ network:outbound
|
|
205
|
+
✅ file_system:read
|
|
206
|
+
|
|
207
|
+
🗂️ Requires
|
|
208
|
+
bin:
|
|
209
|
+
⚠ curl (dangerous)
|
|
210
|
+
• ffmpeg
|
|
211
|
+
env:
|
|
212
|
+
🔑 ELEVENLABS_API_KEY (sensitive)
|
|
213
|
+
• VOICE_ID
|
|
214
|
+
|
|
215
|
+
⚡ Command Dispatch
|
|
216
|
+
speak → scripts/speak.sh
|
|
217
|
+
list-voices → scripts/list.py
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
164
222
|
## Install Globally
|
|
165
223
|
|
|
166
224
|
```bash
|
|
@@ -170,6 +228,7 @@ npm install -g skill-security-scanner
|
|
|
170
228
|
skill-security-scanner frontend-design
|
|
171
229
|
skill-security-scanner lint --all
|
|
172
230
|
skill-security-scanner scan-all
|
|
231
|
+
skill-security-scanner info frontend-design
|
|
173
232
|
```
|
|
174
233
|
|
|
175
234
|
## CI / GitHub Actions
|
|
@@ -184,5 +243,4 @@ skill-security-scanner scan-all
|
|
|
184
243
|
|
|
185
244
|
## Upgrade
|
|
186
245
|
|
|
187
|
-
Free tier covers static analysis + linting. For **dynamic sandboxing**, **GitHub org scans**, and **CI dashboards**:
|
|
188
|
-
|
|
246
|
+
Free tier covers static analysis + linting. For **dynamic sandboxing**, **GitHub org scans**, and **CI dashboards**: → skill-security.com
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
CHANGED
|
@@ -472,6 +472,126 @@ program
|
|
|
472
472
|
if (hasFail)
|
|
473
473
|
process.exit(1);
|
|
474
474
|
});
|
|
475
|
+
// ── Command: info ────────────────────────────
|
|
476
|
+
program
|
|
477
|
+
.command('info <skill>')
|
|
478
|
+
.description('Pretty-print parsed frontmatter for a skill (permissions, bins, env vars, command-dispatch)')
|
|
479
|
+
.option('--json', 'Output as JSON')
|
|
480
|
+
.action((skill, opts) => {
|
|
481
|
+
const resolvedDir = resolveSkillDir(skill);
|
|
482
|
+
if (!resolvedDir) {
|
|
483
|
+
console.error(chalk_1.default.red(`❌ Skill not found: "${skill}"`));
|
|
484
|
+
console.error(chalk_1.default.gray(' Try: skill-security-scanner info frontend-design'));
|
|
485
|
+
process.exit(1);
|
|
486
|
+
}
|
|
487
|
+
const skillPath = path_1.default.join(resolvedDir, 'SKILL.md');
|
|
488
|
+
if (!fs_1.default.existsSync(skillPath)) {
|
|
489
|
+
console.error(chalk_1.default.red('❌ No SKILL.md found — is this an OpenClaw skill directory?'));
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
const content = fs_1.default.readFileSync(skillPath, 'utf8');
|
|
493
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/m);
|
|
494
|
+
if (!fmMatch) {
|
|
495
|
+
console.error(chalk_1.default.red('❌ No YAML frontmatter block found in SKILL.md.'));
|
|
496
|
+
console.error(chalk_1.default.gray(' Expected a --- ... --- block at the top of the file.'));
|
|
497
|
+
process.exit(1);
|
|
498
|
+
}
|
|
499
|
+
let fm = {};
|
|
500
|
+
try {
|
|
501
|
+
fm = (js_yaml_1.default.load(fmMatch[1]) ?? {});
|
|
502
|
+
}
|
|
503
|
+
catch (e) {
|
|
504
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
505
|
+
console.error(chalk_1.default.red(`❌ YAML parse error: ${msg}`));
|
|
506
|
+
process.exit(1);
|
|
507
|
+
}
|
|
508
|
+
if (opts.json) {
|
|
509
|
+
console.log(JSON.stringify({ path: resolvedDir, frontmatter: fm }, null, 2));
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
// ── Identity ──
|
|
513
|
+
console.log('');
|
|
514
|
+
console.log(chalk_1.default.bold.white(`📦 Skill Info — ${fm.name ?? path_1.default.basename(resolvedDir)}`));
|
|
515
|
+
console.log(chalk_1.default.gray('─'.repeat(54)));
|
|
516
|
+
console.log(` ${chalk_1.default.bold('Name')} ${fm.name ? chalk_1.default.cyan(fm.name) : chalk_1.default.gray('(not set)')}`);
|
|
517
|
+
console.log(` ${chalk_1.default.bold('Description')} ${fm.description ? chalk_1.default.white(fm.description) : chalk_1.default.gray('(not set)')}`);
|
|
518
|
+
console.log(` ${chalk_1.default.bold('Version')} ${fm.version ? chalk_1.default.cyan(fm.version) : chalk_1.default.gray('(not set)')}`);
|
|
519
|
+
console.log(` ${chalk_1.default.bold('Path')} ${chalk_1.default.gray(resolvedDir)}`);
|
|
520
|
+
console.log('');
|
|
521
|
+
// ── Permissions ──
|
|
522
|
+
const perms = fm.permissions ?? [];
|
|
523
|
+
console.log(chalk_1.default.bold.white('🔐 Permissions'));
|
|
524
|
+
if (!perms.length) {
|
|
525
|
+
console.log(chalk_1.default.gray(' (none declared)'));
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
const PERM_RISK = {
|
|
529
|
+
'shell:execute': 'high',
|
|
530
|
+
'process:spawn': 'high',
|
|
531
|
+
'file_system:*': 'med',
|
|
532
|
+
'file_system:write': 'med',
|
|
533
|
+
'network:outbound': 'med',
|
|
534
|
+
'network:*': 'med',
|
|
535
|
+
'file_system:read': 'low',
|
|
536
|
+
'network:inbound': 'low',
|
|
537
|
+
'memory:read': 'low',
|
|
538
|
+
'memory:write': 'low',
|
|
539
|
+
};
|
|
540
|
+
const RISK_COLOR = { high: chalk_1.default.red, med: chalk_1.default.yellow, low: chalk_1.default.green };
|
|
541
|
+
const RISK_ICON = { high: '🚨', med: '⚠️ ', low: '✅' };
|
|
542
|
+
for (const p of perms) {
|
|
543
|
+
const risk = PERM_RISK[p] ?? 'low';
|
|
544
|
+
const color = RISK_COLOR[risk];
|
|
545
|
+
const icon = RISK_ICON[risk];
|
|
546
|
+
console.log(` ${icon} ${color(p)}`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
console.log('');
|
|
550
|
+
// ── Requires ──
|
|
551
|
+
const bins = fm.requires?.bin ?? [];
|
|
552
|
+
const envs = fm.requires?.env ?? [];
|
|
553
|
+
const configs = fm.requires?.config ?? [];
|
|
554
|
+
console.log(chalk_1.default.bold.white('🗂️ Requires'));
|
|
555
|
+
if (!bins.length && !envs.length && !configs.length) {
|
|
556
|
+
console.log(chalk_1.default.gray(' (none declared)'));
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
if (bins.length) {
|
|
560
|
+
console.log(chalk_1.default.bold(' bin:'));
|
|
561
|
+
for (const b of bins) {
|
|
562
|
+
const isDangerous = DANGEROUS_BINS.includes(b.toLowerCase());
|
|
563
|
+
console.log(` ${isDangerous ? chalk_1.default.red(`⚠ ${b} ${chalk_1.default.italic('(dangerous)')}`) : chalk_1.default.white(`• ${b}`)}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (envs.length) {
|
|
567
|
+
console.log(chalk_1.default.bold(' env:'));
|
|
568
|
+
const SENSITIVE_RE = /key|secret|token|password|credential|api/i;
|
|
569
|
+
for (const e of envs) {
|
|
570
|
+
const isSensitive = SENSITIVE_RE.test(e);
|
|
571
|
+
console.log(` ${isSensitive ? chalk_1.default.yellow(`🔑 ${e} ${chalk_1.default.italic('(sensitive)')}`) : chalk_1.default.white(`• ${e}`)}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (configs.length) {
|
|
575
|
+
console.log(chalk_1.default.bold(' config:'));
|
|
576
|
+
configs.forEach(c => console.log(chalk_1.default.white(` • ${c}`)));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
console.log('');
|
|
580
|
+
// ── Command-dispatch ──
|
|
581
|
+
const dispatch = fm['command-dispatch'] ?? {};
|
|
582
|
+
const dispatchEntries = Object.entries(dispatch);
|
|
583
|
+
console.log(chalk_1.default.bold.white('⚡ Command Dispatch'));
|
|
584
|
+
if (!dispatchEntries.length) {
|
|
585
|
+
console.log(chalk_1.default.gray(' (none declared)'));
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
const maxKey = Math.max(...dispatchEntries.map(([k]) => k.length));
|
|
589
|
+
for (const [cmd, handler] of dispatchEntries) {
|
|
590
|
+
console.log(` ${chalk_1.default.cyan(cmd.padEnd(maxKey))} → ${chalk_1.default.white(handler)}`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
console.log('');
|
|
594
|
+
});
|
|
475
595
|
// ── Command: scan-all ─────────────────────────
|
|
476
596
|
program
|
|
477
597
|
.command('scan-all')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skill-security-scanner",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Static security scanner for OpenClaw skill directories",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Static security scanner and linter for OpenClaw skill directories",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"skill-security-scanner": "dist/index.js"
|
|
8
8
|
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
9
14
|
"scripts": {
|
|
10
15
|
"build": "tsc",
|
|
11
16
|
"dev": "ts-node src/index.ts",
|
|
@@ -18,13 +23,11 @@
|
|
|
18
23
|
"skill",
|
|
19
24
|
"scanner",
|
|
20
25
|
"ai-agent",
|
|
21
|
-
"cli"
|
|
26
|
+
"cli",
|
|
27
|
+
"lint",
|
|
28
|
+
"static-analysis"
|
|
22
29
|
],
|
|
23
30
|
"license": "MIT",
|
|
24
|
-
"repository": {
|
|
25
|
-
"type": "git",
|
|
26
|
-
"url": "https://github.com/yourorg/skill-security-scanner"
|
|
27
|
-
},
|
|
28
31
|
"dependencies": {
|
|
29
32
|
"chalk": "^5.3.0",
|
|
30
33
|
"commander": "^12.1.0",
|
package/TODO.md
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# skill-security-scanner — CLI Roadmap
|
|
2
|
-
|
|
3
|
-
> Free tier features only. SaaS (dynamic analysis, GitHub Actions, CI dashboards) tracked separately.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 🗂️ Skill Metadata & Quality
|
|
8
|
-
|
|
9
|
-
- [x] **`lint <skill>`** — Validate SKILL.md frontmatter against OpenClaw schema (required fields: `name`, `description`, `version`). Exit 1 on missing fields.
|
|
10
|
-
- [ ] **`info <skill>`** — Pretty-print parsed frontmatter (permissions, bins, env vars, command-dispatch). Useful for debugging what the scanner actually sees.
|
|
11
|
-
- [ ] **`diff <skill-a> <skill-b>`** — Side-by-side comparison of permissions, requires.bin, and risk scores between two skills or two versions.
|
|
12
|
-
- [ ] **`stats`** — Project-wide health snapshot: most common permissions, most used bins, avg finding count per skill, riskiest skill.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 🔍 Deeper Static Analysis
|
|
17
|
-
|
|
18
|
-
- [ ] **Entropy-based secret detection** — Flag high-entropy strings that look like secrets even without a known prefix (e.g. base64 blobs, random hex). Supplement existing regex checks.
|
|
19
|
-
- [ ] **License check** — Scan `package.json` / `requirements.txt` for non-commercial or copyleft licenses (GPL, AGPL). Flag as MED risk.
|
|
20
|
-
- [ ] **Dead code detection** — Find files in the skill dir never referenced by any other file. Flags bloat and unnecessary attack surface.
|
|
21
|
-
- [ ] **Import graph** — List all external packages a skill actually imports and flag ones not declared in frontmatter `requires`.
|
|
22
|
-
- [ ] **`--depth <n>`** flag — Control how many levels deep the code scanner recurses (default: 3).
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 🛠️ Developer UX
|
|
27
|
-
|
|
28
|
-
- [ ] **`fix --dry-run`** — Suggest auto-fixable issues with diffs shown. E.g. overly broad `file_system:*` → suggest `file_system:read`.
|
|
29
|
-
- [ ] **`watch <skill>`** — Re-scan on file save. Live feedback while authoring a skill.
|
|
30
|
-
- [ ] **`init`** — Scaffold a compliant `SKILL.md` with correct frontmatter structure and placeholder values.
|
|
31
|
-
- [ ] **`--ignore <rule-id>`** — Suppress specific finding by ID inline (e.g. `# sss-ignore: exec-pattern` in source).
|
|
32
|
-
- [ ] **`.sss.yaml` config file** — Project-level config: ignored rules, custom dangerous bins list, custom secret regex patterns, default `--fail-on` level.
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## 📊 Reporting & Output
|
|
37
|
-
|
|
38
|
-
- [ ] **`--output <file>`** — Write full report to disk. Support `.json`, `.md`, and `.sarif` extensions (auto-detected from extension).
|
|
39
|
-
- [ ] **Markdown report** — `scan-all --output report.md` generates a formatted table suitable for pasting into PR descriptions.
|
|
40
|
-
- [ ] **SARIF output** — `--format sarif` for GitHub Code Scanning and VS Code Problems panel integration.
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Priority Order
|
|
45
|
-
|
|
46
|
-
| Priority | Feature | Effort |
|
|
47
|
-
|---|---|---|
|
|
48
|
-
| 🔴 1 | `lint` command | Low |
|
|
49
|
-
| 🔴 2 | `--ignore` + `.sss.yaml` config | Medium |
|
|
50
|
-
| 🟡 3 | Entropy-based secret detection | Medium |
|
|
51
|
-
| 🟡 4 | `--output report.md` | Low |
|
|
52
|
-
| 🟢 5 | `diff` command | Medium |
|
|
53
|
-
| 🟢 6 | `stats` command | Low |
|
|
54
|
-
| 🟢 7 | `watch` mode | Medium |
|
|
55
|
-
| ⚪ 8 | Import graph | High |
|
|
56
|
-
| ⚪ 9 | Dead code detection | High |
|
|
57
|
-
| ⚪ 10 | SARIF output | Medium |
|
package/src/index.ts
DELETED
|
@@ -1,660 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import ora from 'ora';
|
|
5
|
-
import yaml from 'js-yaml';
|
|
6
|
-
import fs from 'fs';
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import { execSync } from 'child_process';
|
|
9
|
-
|
|
10
|
-
// ──────────────────────────────────────────────
|
|
11
|
-
// Types
|
|
12
|
-
// ──────────────────────────────────────────────
|
|
13
|
-
type RiskLevel = 'low' | 'med' | 'high';
|
|
14
|
-
|
|
15
|
-
interface Finding {
|
|
16
|
-
level: RiskLevel;
|
|
17
|
-
message: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface ScanReport {
|
|
21
|
-
skill: string;
|
|
22
|
-
score: RiskLevel;
|
|
23
|
-
findings: Finding[];
|
|
24
|
-
badge: string;
|
|
25
|
-
upgrade: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface Frontmatter {
|
|
29
|
-
name?: string;
|
|
30
|
-
description?: string;
|
|
31
|
-
version?: string;
|
|
32
|
-
permissions?: string[];
|
|
33
|
-
requires?: {
|
|
34
|
-
env?: string[];
|
|
35
|
-
config?: string[];
|
|
36
|
-
bin?: string[];
|
|
37
|
-
};
|
|
38
|
-
'command-dispatch'?: Record<string, string>;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
type LintSeverity = 'error' | 'warn';
|
|
42
|
-
|
|
43
|
-
interface LintIssue {
|
|
44
|
-
severity: LintSeverity;
|
|
45
|
-
field: string;
|
|
46
|
-
message: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
interface LintResult {
|
|
50
|
-
skill: string;
|
|
51
|
-
passed: boolean;
|
|
52
|
-
issues: LintIssue[];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ──────────────────────────────────────────────
|
|
56
|
-
// Risk Checks
|
|
57
|
-
// ──────────────────────────────────────────────
|
|
58
|
-
|
|
59
|
-
const DANGEROUS_BINS = ['curl', 'wget', 'nc', 'ncat', 'ssh', 'scp', 'rsync', 'python', 'python3', 'bash', 'sh'];
|
|
60
|
-
const SECRET_PATTERNS = [
|
|
61
|
-
/api[_-]?key\s*[:=]\s*["']?[A-Za-z0-9_\-]{16,}/i,
|
|
62
|
-
/secret\s*[:=]\s*["']?[A-Za-z0-9_\-]{16,}/i,
|
|
63
|
-
/token\s*[:=]\s*["']?[A-Za-z0-9_\-]{20,}/i,
|
|
64
|
-
/password\s*[:=]\s*["']?[A-Za-z0-9@#$!%^&*_\-]{8,}/i,
|
|
65
|
-
/sk-[A-Za-z0-9]{32,}/, // OpenAI key
|
|
66
|
-
/ghp_[A-Za-z0-9]{36}/, // GitHub PAT
|
|
67
|
-
/AKIA[0-9A-Z]{16}/, // AWS Access Key
|
|
68
|
-
];
|
|
69
|
-
const EXEC_PATTERNS = /\b(eval|exec|execSync|spawn|spawnSync|child_process)\s*\(/g;
|
|
70
|
-
const NETWORK_PATTERNS = /\b(fetch|axios|http\.get|http\.request|https\.get|https\.request|got\(|request\()\s*\(/g;
|
|
71
|
-
|
|
72
|
-
function checkPermissions(fm: Frontmatter): Finding[] {
|
|
73
|
-
const findings: Finding[] = [];
|
|
74
|
-
const perms = fm.permissions ?? [];
|
|
75
|
-
|
|
76
|
-
if (perms.some(p => p.includes('file_system:*') || p === 'file_system:write')) {
|
|
77
|
-
findings.push({ level: 'med', message: 'Broad file_system write permission — verify scope is intentional' });
|
|
78
|
-
}
|
|
79
|
-
if (perms.some(p => p.includes('network:*') || p === 'network:outbound')) {
|
|
80
|
-
findings.push({ level: 'med', message: 'Outbound network permission detected — review data exfiltration risk' });
|
|
81
|
-
}
|
|
82
|
-
if (perms.some(p => p.includes('shell:execute') || p.includes('process:spawn'))) {
|
|
83
|
-
findings.push({ level: 'high', message: 'Shell/process execution permission — high privilege escalation risk' });
|
|
84
|
-
}
|
|
85
|
-
return findings;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function checkDangerousBins(fm: Frontmatter): Finding[] {
|
|
89
|
-
const bins = fm.requires?.bin ?? [];
|
|
90
|
-
return bins
|
|
91
|
-
.filter(b => DANGEROUS_BINS.includes(b.toLowerCase()))
|
|
92
|
-
.map(b => ({ level: 'high' as RiskLevel, message: `Requires dangerous binary: ${b} (network/exfil risk)` }));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function checkExposedEnv(fm: Frontmatter): Finding[] {
|
|
96
|
-
const envVars = fm.requires?.env ?? [];
|
|
97
|
-
const sensitivePatterns = /key|secret|token|password|credential|api/i;
|
|
98
|
-
return envVars
|
|
99
|
-
.filter(e => sensitivePatterns.test(e))
|
|
100
|
-
.map(e => ({ level: 'med' as RiskLevel, message: `Sensitive env var required: ${e}` }));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function collectSourceFiles(dir: string): string[] {
|
|
104
|
-
const exts = ['.ts', '.js', '.py', '.sh', '.bash'];
|
|
105
|
-
const results: string[] = [];
|
|
106
|
-
|
|
107
|
-
function walk(current: string) {
|
|
108
|
-
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
109
|
-
const full = path.join(current, entry.name);
|
|
110
|
-
if (entry.isDirectory() && !['node_modules', '.git', 'dist'].includes(entry.name)) {
|
|
111
|
-
walk(full);
|
|
112
|
-
} else if (entry.isFile() && exts.includes(path.extname(entry.name))) {
|
|
113
|
-
results.push(full);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
walk(dir);
|
|
119
|
-
return results;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function checkCodeRisks(dir: string): Finding[] {
|
|
123
|
-
const findings: Finding[] = [];
|
|
124
|
-
const files = collectSourceFiles(dir);
|
|
125
|
-
|
|
126
|
-
for (const file of files) {
|
|
127
|
-
const content = fs.readFileSync(file, 'utf8');
|
|
128
|
-
const rel = path.relative(dir, file);
|
|
129
|
-
|
|
130
|
-
if (EXEC_PATTERNS.test(content)) {
|
|
131
|
-
findings.push({ level: 'high', message: `eval/exec found in ${rel} — remote code execution risk` });
|
|
132
|
-
}
|
|
133
|
-
if (NETWORK_PATTERNS.test(content)) {
|
|
134
|
-
findings.push({ level: 'med', message: `Network call found in ${rel} — verify destination is safe` });
|
|
135
|
-
}
|
|
136
|
-
for (const pattern of SECRET_PATTERNS) {
|
|
137
|
-
if (pattern.test(content)) {
|
|
138
|
-
findings.push({ level: 'high', message: `Possible hardcoded secret in ${rel}` });
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Reset stateful regexes
|
|
144
|
-
EXEC_PATTERNS.lastIndex = 0;
|
|
145
|
-
NETWORK_PATTERNS.lastIndex = 0;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return findings;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function checkDependencyVulns(dir: string, skipAudit = false): Finding[] {
|
|
152
|
-
const findings: Finding[] = [];
|
|
153
|
-
const pkgPath = path.join(dir, 'package.json');
|
|
154
|
-
if (!fs.existsSync(pkgPath) || skipAudit) return findings;
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
const auditOutput = execSync('npm audit --json', { cwd: dir, timeout: 5_000, stdio: ['pipe', 'pipe', 'pipe'] }).toString();
|
|
158
|
-
const audit = JSON.parse(auditOutput);
|
|
159
|
-
const vulnCount: number = audit?.metadata?.vulnerabilities?.total ?? 0;
|
|
160
|
-
const high: number = (audit?.metadata?.vulnerabilities?.high ?? 0) + (audit?.metadata?.vulnerabilities?.critical ?? 0);
|
|
161
|
-
|
|
162
|
-
if (high > 0) findings.push({ level: 'high', message: `${high} high/critical npm vulnerabilities found — run npm audit fix` });
|
|
163
|
-
else if (vulnCount > 0) findings.push({ level: 'med', message: `${vulnCount} moderate npm vulnerabilities found` });
|
|
164
|
-
} catch {
|
|
165
|
-
// npm not available, no lock file, or timed out — skip silently
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return findings;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// ──────────────────────────────────────────────
|
|
172
|
-
// Score Calculation
|
|
173
|
-
// ──────────────────────────────────────────────
|
|
174
|
-
|
|
175
|
-
function calculateScore(findings: Finding[]): RiskLevel {
|
|
176
|
-
if (findings.some(f => f.level === 'high')) return 'high';
|
|
177
|
-
if (findings.some(f => f.level === 'med')) return 'med';
|
|
178
|
-
return 'low';
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
// ──────────────────────────────────────────────
|
|
183
|
-
// Path Resolution
|
|
184
|
-
// ──────────────────────────────────────────────
|
|
185
|
-
|
|
186
|
-
/** Walk up from `startDir` looking for a directory named `folderName`. */
|
|
187
|
-
function findAncestorDir(startDir: string, folderName: string): string | null {
|
|
188
|
-
let current = startDir;
|
|
189
|
-
for (let i = 0; i < 8; i++) {
|
|
190
|
-
const candidate = path.join(current, folderName);
|
|
191
|
-
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) return candidate;
|
|
192
|
-
const parent = path.dirname(current);
|
|
193
|
-
if (parent === current) break;
|
|
194
|
-
current = parent;
|
|
195
|
-
}
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Resolve the skill directory from user input.
|
|
201
|
-
* Priority:
|
|
202
|
-
* 1. Exact path (absolute or relative) → use as-is
|
|
203
|
-
* 2. Walk up looking for `.agent/skills/<input>` or `agent/skills/<input>`
|
|
204
|
-
* 3. Walk up looking for any `skills/<input>` folder
|
|
205
|
-
*/
|
|
206
|
-
function resolveSkillDir(input: string): string | null {
|
|
207
|
-
const exact = path.resolve(input);
|
|
208
|
-
if (fs.existsSync(exact)) return exact;
|
|
209
|
-
|
|
210
|
-
const name = path.basename(input); // support "frontend-design" or "skills/frontend-design"
|
|
211
|
-
const agentFolders = ['.agent', 'agent', '_agent', '_agents'];
|
|
212
|
-
|
|
213
|
-
for (const agentFolder of agentFolders) {
|
|
214
|
-
const agentDir = findAncestorDir(process.cwd(), agentFolder);
|
|
215
|
-
if (!agentDir) continue;
|
|
216
|
-
|
|
217
|
-
const candidate = path.join(agentDir, 'skills', name);
|
|
218
|
-
if (fs.existsSync(candidate)) return candidate;
|
|
219
|
-
|
|
220
|
-
// also try the input as a sub-path inside agentDir
|
|
221
|
-
const subCandidate = path.join(agentDir, input);
|
|
222
|
-
if (fs.existsSync(subCandidate)) return subCandidate;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// ──────────────────────────────────────────────
|
|
229
|
-
// Shared scan helper
|
|
230
|
-
// ──────────────────────────────────────────────
|
|
231
|
-
|
|
232
|
-
interface SkillScanResult {
|
|
233
|
-
name: string;
|
|
234
|
-
dir: string;
|
|
235
|
-
score: RiskLevel;
|
|
236
|
-
findings: Finding[];
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function scanSkill(skillDir: string, skipAudit = false): SkillScanResult {
|
|
240
|
-
const skillPath = path.join(skillDir, 'SKILL.md');
|
|
241
|
-
const content = fs.existsSync(skillPath) ? fs.readFileSync(skillPath, 'utf8') : '';
|
|
242
|
-
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/m);
|
|
243
|
-
|
|
244
|
-
let frontmatter: Frontmatter = {};
|
|
245
|
-
try {
|
|
246
|
-
frontmatter = (yaml.load(fmMatch?.[1] ?? '') ?? {}) as Frontmatter;
|
|
247
|
-
} catch {
|
|
248
|
-
// malformed YAML — treat as unparseable, flag it
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const findings: Finding[] = [
|
|
252
|
-
...checkPermissions(frontmatter),
|
|
253
|
-
...checkDangerousBins(frontmatter),
|
|
254
|
-
...checkExposedEnv(frontmatter),
|
|
255
|
-
...checkCodeRisks(skillDir),
|
|
256
|
-
...checkDependencyVulns(skillDir, skipAudit),
|
|
257
|
-
];
|
|
258
|
-
|
|
259
|
-
if (!fmMatch) {
|
|
260
|
-
findings.push({ level: 'low', message: 'No YAML frontmatter found in SKILL.md' });
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return {
|
|
264
|
-
name: frontmatter.name ?? path.basename(skillDir),
|
|
265
|
-
dir: skillDir,
|
|
266
|
-
score: calculateScore(findings),
|
|
267
|
-
findings,
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
/** Find all immediate subdirectories of `skillsDir` that contain a SKILL.md */
|
|
273
|
-
function discoverSkillDirs(skillsDir: string): string[] {
|
|
274
|
-
if (!fs.existsSync(skillsDir)) return [];
|
|
275
|
-
return fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
276
|
-
.filter(e => e.isDirectory())
|
|
277
|
-
.map(e => path.join(skillsDir, e.name))
|
|
278
|
-
.filter(d => fs.existsSync(path.join(d, 'SKILL.md')));
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// ──────────────────────────────────────────────
|
|
282
|
-
// Lint
|
|
283
|
-
// ──────────────────────────────────────────────
|
|
284
|
-
|
|
285
|
-
const SEMVER_RE = /^\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$/;
|
|
286
|
-
const PERMISSION_SCOPES = [
|
|
287
|
-
'file_system:read', 'file_system:write', 'file_system:*',
|
|
288
|
-
'network:inbound', 'network:outbound', 'network:*',
|
|
289
|
-
'shell:execute', 'process:spawn', 'memory:read', 'memory:write',
|
|
290
|
-
];
|
|
291
|
-
|
|
292
|
-
function lintSkill(skillDir: string): LintResult {
|
|
293
|
-
const skillPath = path.join(skillDir, 'SKILL.md');
|
|
294
|
-
const issues: LintIssue[] = [];
|
|
295
|
-
const skillName = path.basename(skillDir);
|
|
296
|
-
|
|
297
|
-
// ── File existence ──
|
|
298
|
-
if (!fs.existsSync(skillPath)) {
|
|
299
|
-
return { skill: skillName, passed: false, issues: [{ severity: 'error', field: 'SKILL.md', message: 'File not found' }] };
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const content = fs.readFileSync(skillPath, 'utf8');
|
|
303
|
-
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/m);
|
|
304
|
-
|
|
305
|
-
if (!fmMatch) {
|
|
306
|
-
return { skill: skillName, passed: false, issues: [{ severity: 'error', field: 'frontmatter', message: 'No YAML frontmatter block found (expected --- ... ---)' }] };
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
let fm: Frontmatter = {};
|
|
310
|
-
try {
|
|
311
|
-
fm = (yaml.load(fmMatch[1]) ?? {}) as Frontmatter;
|
|
312
|
-
} catch (e: unknown) {
|
|
313
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
314
|
-
return { skill: skillName, passed: false, issues: [{ severity: 'error', field: 'frontmatter', message: `YAML parse error: ${msg}` }] };
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// ── Required fields ──
|
|
318
|
-
if (!fm.name) {
|
|
319
|
-
issues.push({ severity: 'error', field: 'name', message: 'Missing required field' });
|
|
320
|
-
} else if (typeof fm.name !== 'string') {
|
|
321
|
-
issues.push({ severity: 'error', field: 'name', message: 'Must be a string' });
|
|
322
|
-
} else if (fm.name.trim().length < 3) {
|
|
323
|
-
issues.push({ severity: 'error', field: 'name', message: 'Too short (min 3 chars)' });
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (!fm.description) {
|
|
327
|
-
issues.push({ severity: 'error', field: 'description', message: 'Missing required field' });
|
|
328
|
-
} else if (typeof fm.description !== 'string') {
|
|
329
|
-
issues.push({ severity: 'error', field: 'description', message: 'Must be a string' });
|
|
330
|
-
} else if (fm.description.trim().length < 10) {
|
|
331
|
-
issues.push({ severity: 'warn', field: 'description', message: 'Very short description (min 10 chars recommended)' });
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (!fm.version) {
|
|
335
|
-
issues.push({ severity: 'error', field: 'version', message: 'Missing required field' });
|
|
336
|
-
} else if (!SEMVER_RE.test(String(fm.version))) {
|
|
337
|
-
issues.push({ severity: 'error', field: 'version', message: `"${fm.version}" is not valid semver (expected x.y.z)` });
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// ── Optional field validation ──
|
|
341
|
-
if (fm.permissions !== undefined) {
|
|
342
|
-
if (!Array.isArray(fm.permissions)) {
|
|
343
|
-
issues.push({ severity: 'error', field: 'permissions', message: 'Must be an array of strings' });
|
|
344
|
-
} else {
|
|
345
|
-
fm.permissions.forEach((p, i) => {
|
|
346
|
-
if (typeof p !== 'string') {
|
|
347
|
-
issues.push({ severity: 'error', field: `permissions[${i}]`, message: 'Each permission must be a string' });
|
|
348
|
-
} else if (!PERMISSION_SCOPES.includes(p)) {
|
|
349
|
-
issues.push({ severity: 'warn', field: `permissions[${i}]`, message: `Unknown permission scope "${p}" — check OpenClaw docs` });
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (fm.requires !== undefined) {
|
|
356
|
-
if (typeof fm.requires !== 'object' || Array.isArray(fm.requires)) {
|
|
357
|
-
issues.push({ severity: 'error', field: 'requires', message: 'Must be an object with optional env/config/bin arrays' });
|
|
358
|
-
} else {
|
|
359
|
-
for (const key of ['env', 'config', 'bin'] as const) {
|
|
360
|
-
const val = fm.requires[key];
|
|
361
|
-
if (val !== undefined && !Array.isArray(val)) {
|
|
362
|
-
issues.push({ severity: 'error', field: `requires.${key}`, message: 'Must be an array of strings' });
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// ── Description quality hints ──
|
|
369
|
-
const body = content.slice(content.indexOf('---', 3) + 3).trim();
|
|
370
|
-
if (body.length < 50) {
|
|
371
|
-
issues.push({ severity: 'warn', field: 'SKILL.md body', message: 'Skill body is very short — consider adding usage examples or detailed instructions' });
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const errors = issues.filter(i => i.severity === 'error');
|
|
375
|
-
return { skill: fm.name ?? skillName, passed: errors.length === 0, issues };
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// ──────────────────────────────────────────────
|
|
379
|
-
// CLI
|
|
380
|
-
// ──────────────────────────────────────────────
|
|
381
|
-
|
|
382
|
-
const program = new Command();
|
|
383
|
-
|
|
384
|
-
program
|
|
385
|
-
.name('skill-security-scanner')
|
|
386
|
-
.description('Static security scanner for OpenClaw skill directories')
|
|
387
|
-
.version('0.1.0');
|
|
388
|
-
|
|
389
|
-
// ── Command: scan (single skill) ──────────────
|
|
390
|
-
|
|
391
|
-
program
|
|
392
|
-
.command('scan [dir]', { isDefault: true })
|
|
393
|
-
.description('Scan a single skill directory (default command)')
|
|
394
|
-
.option('--json', 'Output results as JSON (for CI pipelines)')
|
|
395
|
-
.option('--badge', 'Print Markdown badge to stdout')
|
|
396
|
-
.action(async (dir: string | undefined, opts: { json?: boolean; badge?: boolean }) => {
|
|
397
|
-
if (!dir) {
|
|
398
|
-
console.error(chalk.red('❌ Missing skill name or path.'));
|
|
399
|
-
console.error(chalk.gray(' Usage: skill-security-scanner <name>'));
|
|
400
|
-
console.error(chalk.gray(' Example: skill-security-scanner frontend-design'));
|
|
401
|
-
console.error(chalk.gray(' Scan all: skill-security-scanner scan-all'));
|
|
402
|
-
process.exit(1);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const resolvedDir = resolveSkillDir(dir);
|
|
406
|
-
|
|
407
|
-
if (!resolvedDir) {
|
|
408
|
-
console.error(chalk.red(`❌ Skill not found: "${dir}"`));
|
|
409
|
-
console.error(chalk.gray(' Try: skill-security-scanner frontend-design'));
|
|
410
|
-
console.error(chalk.gray(' Or: skill-security-scanner .agent/skills/frontend-design'));
|
|
411
|
-
process.exit(1);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
if (!fs.existsSync(path.join(resolvedDir, 'SKILL.md'))) {
|
|
415
|
-
console.error(chalk.red('❌ No SKILL.md found — is this an OpenClaw skill directory?'));
|
|
416
|
-
process.exit(1);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const spinner = ora('Scanning skill...').start();
|
|
420
|
-
const result = scanSkill(resolvedDir);
|
|
421
|
-
spinner.stop();
|
|
422
|
-
|
|
423
|
-
const BADGE_COLOR_MAP: Record<RiskLevel, string> = { low: 'brightgreen', med: 'yellow', high: 'red' };
|
|
424
|
-
const badgeLabel = result.score.toUpperCase();
|
|
425
|
-
const badge = ``;
|
|
426
|
-
const upgrade = 'Full dynamic analysis, GitHub Action & CI dashboards → skill-security.com (7-day free trial)';
|
|
427
|
-
|
|
428
|
-
const report: ScanReport = {
|
|
429
|
-
skill: result.name, score: result.score, findings: result.findings, badge,
|
|
430
|
-
upgrade: ''
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
if (opts.json) { console.log(JSON.stringify(report, null, 2)); return; }
|
|
434
|
-
|
|
435
|
-
const scoreColor = { low: chalk.green, med: chalk.yellow, high: chalk.red }[result.score];
|
|
436
|
-
const icon = { low: '✅', med: '⚠️', high: '🚨' }[result.score];
|
|
437
|
-
const printGroup = (level: RiskLevel, label: string, color: { bold: (s: string) => string } & ((s: string) => string)) => {
|
|
438
|
-
const group = result.findings.filter(f => f.level === level);
|
|
439
|
-
if (!group.length) return;
|
|
440
|
-
console.log(color.bold(`${label} (${group.length})`));
|
|
441
|
-
group.forEach(f => console.log(color(` • ${f.message}`)));
|
|
442
|
-
console.log('');
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
console.log('');
|
|
446
|
-
console.log(chalk.bold.white(`🔍 Skill Security Scanner — ${result.name}`));
|
|
447
|
-
console.log(chalk.gray('─'.repeat(50)));
|
|
448
|
-
console.log(`Risk Score: ${scoreColor(`${icon} ${result.score.toUpperCase()}`)}`);
|
|
449
|
-
console.log('');
|
|
450
|
-
|
|
451
|
-
if (!result.findings.length) {
|
|
452
|
-
console.log(chalk.green('✅ No issues found. Skill looks clean.'));
|
|
453
|
-
} else {
|
|
454
|
-
printGroup('high', '🚨 HIGH RISK', chalk.red);
|
|
455
|
-
printGroup('med', '⚠️ MEDIUM RISK', chalk.yellow);
|
|
456
|
-
printGroup('low', '💡 LOW RISK', chalk.gray);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
if (opts.badge) { console.log(chalk.gray('Badge:\n')); console.log(badge); console.log(''); }
|
|
460
|
-
console.log(chalk.blueBright(`💡 ${upgrade}`));
|
|
461
|
-
console.log('');
|
|
462
|
-
if (result.score === 'high') process.exit(1);
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
// ── Command: lint ────────────────────────────
|
|
466
|
-
|
|
467
|
-
program
|
|
468
|
-
.command('lint [dir]')
|
|
469
|
-
.description('Validate SKILL.md frontmatter against the OpenClaw schema')
|
|
470
|
-
.option('--json', 'Output results as JSON')
|
|
471
|
-
.option('--strict', 'Treat warnings as errors (exit 1)')
|
|
472
|
-
.option('--all', 'Lint every skill in the project')
|
|
473
|
-
.action((dir: string | undefined, opts: { json?: boolean; strict?: boolean; all?: boolean }) => {
|
|
474
|
-
// ── lint --all shortcut ──
|
|
475
|
-
if (opts.all || !dir) {
|
|
476
|
-
const agentFolders = ['.agent', 'agent', '_agent', '_agents'];
|
|
477
|
-
let skillsDir: string | null = null;
|
|
478
|
-
for (const folder of agentFolders) {
|
|
479
|
-
const agentDir = findAncestorDir(process.cwd(), folder);
|
|
480
|
-
if (agentDir) {
|
|
481
|
-
const c = path.join(agentDir, 'skills');
|
|
482
|
-
if (fs.existsSync(c)) { skillsDir = c; break; }
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
if (!skillsDir) {
|
|
486
|
-
console.error(chalk.red('❌ No .agent/skills directory found.'));
|
|
487
|
-
process.exit(1);
|
|
488
|
-
}
|
|
489
|
-
const dirs = discoverSkillDirs(skillsDir);
|
|
490
|
-
const results = dirs.map(d => lintSkill(d));
|
|
491
|
-
|
|
492
|
-
if (opts.json) { console.log(JSON.stringify(results, null, 2)); }
|
|
493
|
-
else {
|
|
494
|
-
const passed = results.filter(r => r.passed && !(opts.strict && r.issues.some(i => i.severity === 'warn')));
|
|
495
|
-
const failed = results.filter(r => !r.passed || (opts.strict && r.issues.some(i => i.severity === 'warn')));
|
|
496
|
-
console.log('');
|
|
497
|
-
console.log(chalk.bold.white(`🔎 Lint — ${results.length} skills`));
|
|
498
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
499
|
-
for (const r of [...failed, ...passed]) {
|
|
500
|
-
const ok = !failed.includes(r);
|
|
501
|
-
console.log(ok ? chalk.green(` ✅ ${r.skill}`) : chalk.red(` ❌ ${r.skill}`));
|
|
502
|
-
r.issues.forEach(issue => {
|
|
503
|
-
const c = issue.severity === 'error' ? chalk.red : chalk.yellow;
|
|
504
|
-
console.log(c(` [${issue.severity.toUpperCase()}] ${issue.field}: ${issue.message}`));
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
508
|
-
console.log(` ${chalk.green(`✅ ${passed.length} passed`)} ${chalk.red(`❌ ${failed.length} failed`)}`);
|
|
509
|
-
console.log('');
|
|
510
|
-
}
|
|
511
|
-
const anyFailed = results.some(r => !r.passed || (opts.strict && r.issues.some(i => i.severity === 'warn')));
|
|
512
|
-
if (anyFailed) process.exit(1);
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
// ── single skill ──
|
|
517
|
-
const resolvedDir = resolveSkillDir(dir);
|
|
518
|
-
if (!resolvedDir) {
|
|
519
|
-
console.error(chalk.red(`❌ Skill not found: "${dir}"`));
|
|
520
|
-
process.exit(1);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
const result = lintSkill(resolvedDir);
|
|
524
|
-
const hasFail = !result.passed || (opts.strict && result.issues.some(i => i.severity === 'warn'));
|
|
525
|
-
|
|
526
|
-
if (opts.json) { console.log(JSON.stringify(result, null, 2)); }
|
|
527
|
-
else {
|
|
528
|
-
const errors = result.issues.filter(i => i.severity === 'error');
|
|
529
|
-
const warns = result.issues.filter(i => i.severity === 'warn');
|
|
530
|
-
const statusIcon = hasFail ? '❌' : '✅';
|
|
531
|
-
const statusText = hasFail ? chalk.red('FAIL') : chalk.green('PASS');
|
|
532
|
-
|
|
533
|
-
console.log('');
|
|
534
|
-
console.log(chalk.bold.white(`🔎 Lint — ${result.skill}`));
|
|
535
|
-
console.log(chalk.gray('─'.repeat(50)));
|
|
536
|
-
console.log(`Status: ${statusIcon} ${statusText}`);
|
|
537
|
-
console.log('');
|
|
538
|
-
|
|
539
|
-
if (!result.issues.length) {
|
|
540
|
-
console.log(chalk.green('✅ All fields valid. Skill looks well-formed.'));
|
|
541
|
-
} else {
|
|
542
|
-
if (errors.length) {
|
|
543
|
-
console.log(chalk.red.bold(`Errors (${errors.length})`));
|
|
544
|
-
errors.forEach(i => console.log(chalk.red(` • [${i.field}] ${i.message}`)));
|
|
545
|
-
console.log('');
|
|
546
|
-
}
|
|
547
|
-
if (warns.length) {
|
|
548
|
-
console.log(chalk.yellow.bold(`Warnings (${warns.length})`));
|
|
549
|
-
warns.forEach(i => console.log(chalk.yellow(` • [${i.field}] ${i.message}`)));
|
|
550
|
-
console.log('');
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (hasFail) process.exit(1);
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
// ── Command: scan-all ─────────────────────────
|
|
559
|
-
|
|
560
|
-
program
|
|
561
|
-
.command('scan-all')
|
|
562
|
-
.description('Scan every skill in the project and show a summary table')
|
|
563
|
-
.option('--json', 'Output results as JSON array')
|
|
564
|
-
.option('--fail-on <level>', 'Exit code 1 if any skill reaches this level (low|med|high)', 'high')
|
|
565
|
-
.option('--skip-audit', 'Skip npm audit (much faster for large projects)')
|
|
566
|
-
.action(async (opts: { json?: boolean; failOn: RiskLevel; skipAudit?: boolean }) => {
|
|
567
|
-
const agentFolders = ['.agent', 'agent', '_agent', '_agents'];
|
|
568
|
-
let skillsDir: string | null = null;
|
|
569
|
-
|
|
570
|
-
for (const folder of agentFolders) {
|
|
571
|
-
const agentDir = findAncestorDir(process.cwd(), folder);
|
|
572
|
-
if (agentDir) {
|
|
573
|
-
const candidate = path.join(agentDir, 'skills');
|
|
574
|
-
if (fs.existsSync(candidate)) { skillsDir = candidate; break; }
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
if (!skillsDir) {
|
|
579
|
-
console.error(chalk.red('❌ No .agent/skills directory found from the current location.'));
|
|
580
|
-
process.exit(1);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
const skillDirs = discoverSkillDirs(skillsDir);
|
|
584
|
-
|
|
585
|
-
if (!skillDirs.length) {
|
|
586
|
-
console.error(chalk.yellow('⚠️ No skill directories with SKILL.md found.'));
|
|
587
|
-
process.exit(0);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// Auto-skip audit for large projects unless explicitly requested
|
|
591
|
-
const skipAudit = opts.skipAudit ?? skillDirs.length > 5;
|
|
592
|
-
const spinner = ora(`Scanning ${skillDirs.length} skills${skipAudit ? ' (audit skipped)' : ''}...`).start();
|
|
593
|
-
const results: SkillScanResult[] = skillDirs.map(d => scanSkill(d, skipAudit));
|
|
594
|
-
spinner.stop();
|
|
595
|
-
|
|
596
|
-
if (opts.json) {
|
|
597
|
-
console.log(JSON.stringify(results.map(r => ({
|
|
598
|
-
skill: r.name, score: r.score, findingCount: r.findings.length, findings: r.findings,
|
|
599
|
-
})), null, 2));
|
|
600
|
-
const hasFailure = results.some(r => r.score === opts.failOn || (opts.failOn === 'low' && true) || (opts.failOn === 'med' && r.score !== 'low'));
|
|
601
|
-
if (hasFailure) process.exit(1);
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// ── Summary table ──
|
|
606
|
-
const counts = { high: 0, med: 0, low: 0 };
|
|
607
|
-
results.forEach(r => counts[r.score]++);
|
|
608
|
-
|
|
609
|
-
const SCORE_COLOR = { low: chalk.green, med: chalk.yellow, high: chalk.red };
|
|
610
|
-
const SCORE_ICON = { low: '✅', med: '⚠️ ', high: '🚨' };
|
|
611
|
-
const FIND_COLOR = { low: chalk.gray, med: chalk.yellow, high: chalk.red };
|
|
612
|
-
|
|
613
|
-
// Sort: high first, then med, then low
|
|
614
|
-
const sorted = [...results].sort((a, b) => {
|
|
615
|
-
const order: Record<RiskLevel, number> = { high: 0, med: 1, low: 2 };
|
|
616
|
-
return order[a.score] - order[b.score];
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
console.log('');
|
|
620
|
-
console.log(chalk.bold.white(`🔍 Skill Security Scanner — Project Scan (${results.length} skills)`));
|
|
621
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
622
|
-
|
|
623
|
-
for (const r of sorted) {
|
|
624
|
-
const color = SCORE_COLOR[r.score];
|
|
625
|
-
const icon = SCORE_ICON[r.score];
|
|
626
|
-
|
|
627
|
-
if (!r.findings.length) {
|
|
628
|
-
console.log(` ${icon} ${color(r.score.toUpperCase().padEnd(5))} ${r.name}`);
|
|
629
|
-
} else {
|
|
630
|
-
// Header row for this skill
|
|
631
|
-
console.log(` ${icon} ${color(r.score.toUpperCase().padEnd(5))} ${chalk.bold(r.name)}`);
|
|
632
|
-
|
|
633
|
-
// Print findings grouped by severity
|
|
634
|
-
for (const level of ['high', 'med', 'low'] as RiskLevel[]) {
|
|
635
|
-
const group = r.findings.filter(f => f.level === level);
|
|
636
|
-
group.forEach(f => {
|
|
637
|
-
console.log(` ${FIND_COLOR[level](`→ [${f.level.toUpperCase()}] ${f.message}`)}`);
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
console.log('');
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
645
|
-
console.log(
|
|
646
|
-
` ${chalk.red(`🚨 ${counts.high} HIGH`)} ` +
|
|
647
|
-
`${chalk.yellow(`⚠️ ${counts.med} MED`)} ` +
|
|
648
|
-
`${chalk.green(`✅ ${counts.low} LOW`)}`
|
|
649
|
-
);
|
|
650
|
-
console.log('');
|
|
651
|
-
// console.log(chalk.blueBright('Full dynamic analysis, GitHub Action & CI dashboards → skill-security.com (7-day free trial)'));
|
|
652
|
-
console.log('');
|
|
653
|
-
|
|
654
|
-
const LEVEL_ORDER: Record<RiskLevel, number> = { low: 0, med: 1, high: 2 };
|
|
655
|
-
const shouldFail = results.some(r => LEVEL_ORDER[r.score] >= LEVEL_ORDER[opts.failOn]);
|
|
656
|
-
if (shouldFail) process.exit(1);
|
|
657
|
-
});
|
|
658
|
-
|
|
659
|
-
program.parse();
|
|
660
|
-
|
package/tsconfig.json
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"lib": [
|
|
6
|
-
"ES2022"
|
|
7
|
-
],
|
|
8
|
-
"outDir": "dist",
|
|
9
|
-
"rootDir": "src",
|
|
10
|
-
"strict": true,
|
|
11
|
-
"esModuleInterop": true,
|
|
12
|
-
"skipLibCheck": true,
|
|
13
|
-
"declaration": true,
|
|
14
|
-
"declarationMap": true,
|
|
15
|
-
"sourceMap": true,
|
|
16
|
-
"resolveJsonModule": true
|
|
17
|
-
},
|
|
18
|
-
"include": [
|
|
19
|
-
"src/**/*"
|
|
20
|
-
],
|
|
21
|
-
"exclude": [
|
|
22
|
-
"node_modules",
|
|
23
|
-
"dist"
|
|
24
|
-
]
|
|
25
|
-
}
|