agent-skills-cli 1.0.8 → 1.1.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 +44 -19
- package/dist/adapters/adapter.d.ts +51 -0
- package/dist/adapters/adapter.d.ts.map +1 -0
- package/dist/adapters/adapter.js +51 -0
- package/dist/adapters/adapter.js.map +1 -0
- package/dist/adapters/claude.d.ts +16 -0
- package/dist/adapters/claude.d.ts.map +1 -0
- package/dist/adapters/claude.js +30 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/copilot.d.ts +16 -0
- package/dist/adapters/copilot.d.ts.map +1 -0
- package/dist/adapters/copilot.js +32 -0
- package/dist/adapters/copilot.js.map +1 -0
- package/dist/adapters/cursor.d.ts +17 -0
- package/dist/adapters/cursor.d.ts.map +1 -0
- package/dist/adapters/cursor.js +34 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +10 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/universal.d.ts +12 -0
- package/dist/adapters/universal.d.ts.map +1 -0
- package/dist/adapters/universal.js +16 -0
- package/dist/adapters/universal.js.map +1 -0
- package/dist/cli/agents.d.ts +21 -0
- package/dist/cli/agents.d.ts.map +1 -0
- package/dist/cli/agents.js +317 -0
- package/dist/cli/agents.js.map +1 -0
- package/dist/cli/commands/audit.d.ts +11 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/audit.js +168 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/commands/blueprint.d.ts +11 -0
- package/dist/cli/commands/blueprint.d.ts.map +1 -0
- package/dist/cli/commands/blueprint.js +210 -0
- package/dist/cli/commands/blueprint.js.map +1 -0
- package/dist/cli/commands/bootstrap.d.ts +11 -0
- package/dist/cli/commands/bootstrap.d.ts.map +1 -0
- package/dist/cli/commands/bootstrap.js +267 -0
- package/dist/cli/commands/bootstrap.js.map +1 -0
- package/dist/cli/commands/capture.d.ts +11 -0
- package/dist/cli/commands/capture.d.ts.map +1 -0
- package/dist/cli/commands/capture.js +109 -0
- package/dist/cli/commands/capture.js.map +1 -0
- package/dist/cli/commands/ci.d.ts +11 -0
- package/dist/cli/commands/ci.d.ts.map +1 -0
- package/dist/cli/commands/ci.js +144 -0
- package/dist/cli/commands/ci.js.map +1 -0
- package/dist/cli/commands/collab.d.ts +11 -0
- package/dist/cli/commands/collab.d.ts.map +1 -0
- package/dist/cli/commands/collab.js +196 -0
- package/dist/cli/commands/collab.js.map +1 -0
- package/dist/cli/commands/convert.d.ts +11 -0
- package/dist/cli/commands/convert.d.ts.map +1 -0
- package/dist/cli/commands/convert.js +170 -0
- package/dist/cli/commands/convert.js.map +1 -0
- package/dist/cli/commands/craft.d.ts +18 -0
- package/dist/cli/commands/craft.d.ts.map +1 -0
- package/dist/cli/commands/craft.js +205 -0
- package/dist/cli/commands/craft.js.map +1 -0
- package/dist/cli/commands/export.d.ts +9 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +111 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/forge.d.ts +11 -0
- package/dist/cli/commands/forge.d.ts.map +1 -0
- package/dist/cli/commands/forge.js +152 -0
- package/dist/cli/commands/forge.js.map +1 -0
- package/dist/cli/commands/grid.d.ts +11 -0
- package/dist/cli/commands/grid.d.ts.map +1 -0
- package/dist/cli/commands/grid.js +217 -0
- package/dist/cli/commands/grid.js.map +1 -0
- package/dist/cli/commands/insight.d.ts +7 -0
- package/dist/cli/commands/insight.d.ts.map +1 -0
- package/dist/cli/commands/insight.js +71 -0
- package/dist/cli/commands/insight.js.map +1 -0
- package/dist/cli/commands/install.d.ts +6 -0
- package/dist/cli/commands/install.d.ts.map +1 -0
- package/dist/cli/commands/install.js +359 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/interactive.d.ts +7 -0
- package/dist/cli/commands/interactive.d.ts.map +1 -0
- package/dist/cli/commands/interactive.js +535 -0
- package/dist/cli/commands/interactive.js.map +1 -0
- package/dist/cli/commands/list.d.ts +6 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +77 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/lockspec.d.ts +11 -0
- package/dist/cli/commands/lockspec.d.ts.map +1 -0
- package/dist/cli/commands/lockspec.js +179 -0
- package/dist/cli/commands/lockspec.js.map +1 -0
- package/dist/cli/commands/marketplace.d.ts +7 -0
- package/dist/cli/commands/marketplace.d.ts.map +1 -0
- package/dist/cli/commands/marketplace.js +417 -0
- package/dist/cli/commands/marketplace.js.map +1 -0
- package/dist/cli/commands/method.d.ts +7 -0
- package/dist/cli/commands/method.d.ts.map +1 -0
- package/dist/cli/commands/method.js +140 -0
- package/dist/cli/commands/method.js.map +1 -0
- package/dist/cli/commands/mine.d.ts +11 -0
- package/dist/cli/commands/mine.d.ts.map +1 -0
- package/dist/cli/commands/mine.js +254 -0
- package/dist/cli/commands/mine.js.map +1 -0
- package/dist/cli/commands/recall.d.ts +11 -0
- package/dist/cli/commands/recall.d.ts.map +1 -0
- package/dist/cli/commands/recall.js +201 -0
- package/dist/cli/commands/recall.js.map +1 -0
- package/dist/cli/commands/rule.d.ts +11 -0
- package/dist/cli/commands/rule.d.ts.map +1 -0
- package/dist/cli/commands/rule.js +230 -0
- package/dist/cli/commands/rule.js.map +1 -0
- package/dist/cli/commands/score.d.ts +10 -0
- package/dist/cli/commands/score.d.ts.map +1 -0
- package/dist/cli/commands/score.js +91 -0
- package/dist/cli/commands/score.js.map +1 -0
- package/dist/cli/commands/search.d.ts +6 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +173 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/show.d.ts +6 -0
- package/dist/cli/commands/show.d.ts.map +1 -0
- package/dist/cli/commands/show.js +158 -0
- package/dist/cli/commands/show.js.map +1 -0
- package/dist/cli/commands/submit-repo.d.ts +11 -0
- package/dist/cli/commands/submit-repo.d.ts.map +1 -0
- package/dist/cli/commands/submit-repo.js +133 -0
- package/dist/cli/commands/submit-repo.js.map +1 -0
- package/dist/cli/commands/submit.d.ts +15 -0
- package/dist/cli/commands/submit.d.ts.map +1 -0
- package/dist/cli/commands/submit.js +161 -0
- package/dist/cli/commands/submit.js.map +1 -0
- package/dist/cli/commands/suggest.d.ts +11 -0
- package/dist/cli/commands/suggest.d.ts.map +1 -0
- package/dist/cli/commands/suggest.js +164 -0
- package/dist/cli/commands/suggest.js.map +1 -0
- package/dist/cli/commands/track.d.ts +11 -0
- package/dist/cli/commands/track.d.ts.map +1 -0
- package/dist/cli/commands/track.js +199 -0
- package/dist/cli/commands/track.js.map +1 -0
- package/dist/cli/commands/trigger.d.ts +11 -0
- package/dist/cli/commands/trigger.d.ts.map +1 -0
- package/dist/cli/commands/trigger.js +157 -0
- package/dist/cli/commands/trigger.js.map +1 -0
- package/dist/cli/commands/utils-commands.d.ts +9 -0
- package/dist/cli/commands/utils-commands.d.ts.map +1 -0
- package/dist/cli/commands/utils-commands.js +389 -0
- package/dist/cli/commands/utils-commands.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +6 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +40 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +88 -2743
- package/dist/cli/index.js.map +1 -1
- package/dist/core/audit.d.ts +24 -0
- package/dist/core/audit.d.ts.map +1 -0
- package/dist/core/audit.js +195 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +8 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/quality.d.ts +48 -0
- package/dist/core/quality.d.ts.map +1 -0
- package/dist/core/quality.js +241 -0
- package/dist/core/quality.js.map +1 -0
- package/dist/core/scanner-rules.d.ts +58 -0
- package/dist/core/scanner-rules.d.ts.map +1 -0
- package/dist/core/scanner-rules.js +335 -0
- package/dist/core/scanner-rules.js.map +1 -0
- package/dist/core/suggest.d.ts +51 -0
- package/dist/core/suggest.d.ts.map +1 -0
- package/dist/core/suggest.js +241 -0
- package/dist/core/suggest.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality.d.ts","sourceRoot":"","sources":["../../src/core/quality.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,MAAM,WAAW,YAAY;IACzB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IACxB,SAAS,EAAE,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,CAAC;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAaD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA4C5E;AA0MD;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,MAAM,CAKxE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAIvE"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Quality Scoring Module
|
|
3
|
+
* 4-dimension quality assessment for skills
|
|
4
|
+
*
|
|
5
|
+
* Dimensions:
|
|
6
|
+
* Structure (30%) — frontmatter, required fields, directory layout
|
|
7
|
+
* Clarity (30%) — description quality, headings, examples, "when to use"
|
|
8
|
+
* Specificity (30%) — actionable steps, code examples, numbered instructions
|
|
9
|
+
* Advanced (10%) — scripts, references, assets, anti-patterns, changelog
|
|
10
|
+
*/
|
|
11
|
+
import { readFile, stat } from 'fs/promises';
|
|
12
|
+
import { existsSync } from 'fs';
|
|
13
|
+
import { join, resolve } from 'path';
|
|
14
|
+
// ── Weights ──────────────────────────────────────────────────────────────
|
|
15
|
+
const WEIGHTS = {
|
|
16
|
+
structure: 0.30,
|
|
17
|
+
clarity: 0.30,
|
|
18
|
+
specificity: 0.30,
|
|
19
|
+
advanced: 0.10,
|
|
20
|
+
};
|
|
21
|
+
// ── Main Entry ───────────────────────────────────────────────────────────
|
|
22
|
+
/**
|
|
23
|
+
* Assess the quality of a skill at the given path.
|
|
24
|
+
* @param skillPath — path to skill directory or SKILL.md file
|
|
25
|
+
*/
|
|
26
|
+
export async function assessQuality(skillPath) {
|
|
27
|
+
const resolved = resolve(skillPath);
|
|
28
|
+
const pathStat = await stat(resolved).catch(() => null);
|
|
29
|
+
let dir;
|
|
30
|
+
let skillMdPath;
|
|
31
|
+
if (pathStat?.isDirectory()) {
|
|
32
|
+
dir = resolved;
|
|
33
|
+
skillMdPath = join(resolved, 'SKILL.md');
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
dir = resolve(resolved, '..');
|
|
37
|
+
skillMdPath = resolved;
|
|
38
|
+
}
|
|
39
|
+
let content = '';
|
|
40
|
+
if (existsSync(skillMdPath)) {
|
|
41
|
+
content = await readFile(skillMdPath, 'utf-8');
|
|
42
|
+
}
|
|
43
|
+
const details = [];
|
|
44
|
+
// Run all dimension checks
|
|
45
|
+
const structureScore = scoreStructure(content, dir, details);
|
|
46
|
+
const clarityScore = scoreClarity(content, details);
|
|
47
|
+
const specificityScore = scoreSpecificity(content, details);
|
|
48
|
+
const advancedScore = await scoreAdvanced(content, dir, details);
|
|
49
|
+
const overall = Math.round(structureScore * WEIGHTS.structure +
|
|
50
|
+
clarityScore * WEIGHTS.clarity +
|
|
51
|
+
specificityScore * WEIGHTS.specificity +
|
|
52
|
+
advancedScore * WEIGHTS.advanced);
|
|
53
|
+
return {
|
|
54
|
+
overall,
|
|
55
|
+
structure: structureScore,
|
|
56
|
+
clarity: clarityScore,
|
|
57
|
+
specificity: specificityScore,
|
|
58
|
+
advanced: advancedScore,
|
|
59
|
+
details,
|
|
60
|
+
grade: toGrade(overall),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// ── Dimension 1: Structure (30%) ─────────────────────────────────────────
|
|
64
|
+
function scoreStructure(content, dir, details) {
|
|
65
|
+
let score = 0;
|
|
66
|
+
const max = 100;
|
|
67
|
+
// Check 1: SKILL.md exists (25 pts)
|
|
68
|
+
const hasMd = content.length > 0;
|
|
69
|
+
addDetail(details, 'structure', 'SKILL.md file exists', hasMd, 25, 25, hasMd ? undefined : 'Create a SKILL.md file in your skill directory');
|
|
70
|
+
if (hasMd)
|
|
71
|
+
score += 25;
|
|
72
|
+
// Check 2: YAML frontmatter present (25 pts)
|
|
73
|
+
const hasFrontmatter = /^---\n[\s\S]*?\n---/m.test(content);
|
|
74
|
+
addDetail(details, 'structure', 'YAML frontmatter present', hasFrontmatter, hasFrontmatter ? 25 : 0, 25, hasFrontmatter ? undefined : 'Add YAML frontmatter with --- delimiters at the top');
|
|
75
|
+
if (hasFrontmatter)
|
|
76
|
+
score += 25;
|
|
77
|
+
// Check 3: Name field in frontmatter (20 pts)
|
|
78
|
+
const hasName = /^name:\s*.+/m.test(content);
|
|
79
|
+
addDetail(details, 'structure', 'name field in frontmatter', hasName, hasName ? 20 : 0, 20, hasName ? undefined : 'Add a "name:" field to your frontmatter');
|
|
80
|
+
if (hasName)
|
|
81
|
+
score += 20;
|
|
82
|
+
// Check 4: Description field in frontmatter (20 pts)
|
|
83
|
+
const hasDesc = /^description:\s*.+/m.test(content);
|
|
84
|
+
addDetail(details, 'structure', 'description field in frontmatter', hasDesc, hasDesc ? 20 : 0, 20, hasDesc ? undefined : 'Add a "description:" field to your frontmatter');
|
|
85
|
+
if (hasDesc)
|
|
86
|
+
score += 20;
|
|
87
|
+
// Check 5: Proper directory structure (10 pts)
|
|
88
|
+
const hasProperDir = existsSync(join(dir, 'SKILL.md'));
|
|
89
|
+
addDetail(details, 'structure', 'Proper directory structure (skillname/SKILL.md)', hasProperDir, hasProperDir ? 10 : 0, 10, hasProperDir ? undefined : 'Place SKILL.md inside a named directory');
|
|
90
|
+
if (hasProperDir)
|
|
91
|
+
score += 10;
|
|
92
|
+
return Math.min(score, max);
|
|
93
|
+
}
|
|
94
|
+
// ── Dimension 2: Clarity (30%) ───────────────────────────────────────────
|
|
95
|
+
function scoreClarity(content, details) {
|
|
96
|
+
let score = 0;
|
|
97
|
+
const max = 100;
|
|
98
|
+
// Check 1: Description is meaningful (>50 chars) (20 pts)
|
|
99
|
+
const descMatch = content.match(/^description:\s*(.+)/m);
|
|
100
|
+
const descLen = descMatch?.[1]?.trim().length ?? 0;
|
|
101
|
+
const goodDesc = descLen >= 50;
|
|
102
|
+
addDetail(details, 'clarity', 'Description is meaningful (≥50 chars)', goodDesc, goodDesc ? 20 : 0, 20, goodDesc ? undefined : `Description is ${descLen} chars — aim for 50+ to explain when/why to use this skill`);
|
|
103
|
+
if (goodDesc)
|
|
104
|
+
score += 20;
|
|
105
|
+
// Check 2: Has section headings (## or ###) (20 pts)
|
|
106
|
+
const headings = content.match(/^#{2,3}\s+.+/gm) || [];
|
|
107
|
+
const hasHeadings = headings.length >= 2;
|
|
108
|
+
addDetail(details, 'clarity', 'Has section headings (≥2)', hasHeadings, hasHeadings ? 20 : 0, 20, hasHeadings ? undefined : 'Add ## headings to organize your skill into logical sections');
|
|
109
|
+
if (hasHeadings)
|
|
110
|
+
score += 20;
|
|
111
|
+
// Check 3: "When to use" or "Usage" section (15 pts)
|
|
112
|
+
const hasUsage = /when\s+to\s+use|usage|use\s+cases?/i.test(content);
|
|
113
|
+
addDetail(details, 'clarity', '"When to use" or "Usage" section', hasUsage, hasUsage ? 15 : 0, 15, hasUsage ? undefined : 'Add a "When to Use" section so agents know when to activate this skill');
|
|
114
|
+
if (hasUsage)
|
|
115
|
+
score += 15;
|
|
116
|
+
// Check 4: Has examples section (15 pts)
|
|
117
|
+
const hasExamples = /example|demo|sample/i.test(content);
|
|
118
|
+
addDetail(details, 'clarity', 'Has examples or demos', hasExamples, hasExamples ? 15 : 0, 15, hasExamples ? undefined : 'Include examples to show expected behavior');
|
|
119
|
+
if (hasExamples)
|
|
120
|
+
score += 15;
|
|
121
|
+
// Check 5: Body is substantial (>100 lines → 15 pts, >50 → 10, >20 → 5)
|
|
122
|
+
const lines = content.split('\n').length;
|
|
123
|
+
const bodyPts = lines > 100 ? 15 : lines > 50 ? 10 : lines > 20 ? 5 : 0;
|
|
124
|
+
addDetail(details, 'clarity', `Substantial body content (${lines} lines)`, bodyPts > 0, bodyPts, 15, bodyPts > 0 ? undefined : 'Add more content — skills under 20 lines are rarely comprehensive');
|
|
125
|
+
score += bodyPts;
|
|
126
|
+
// Check 6: No excessive blank lines (15 pts)
|
|
127
|
+
const blankRatio = (content.match(/\n\n\n+/g) || []).length;
|
|
128
|
+
const cleanFormat = blankRatio <= 3;
|
|
129
|
+
addDetail(details, 'clarity', 'Clean formatting (no excessive blank lines)', cleanFormat, cleanFormat ? 15 : 0, 15, cleanFormat ? undefined : 'Remove excessive blank lines for better readability');
|
|
130
|
+
if (cleanFormat)
|
|
131
|
+
score += 15;
|
|
132
|
+
return Math.min(score, max);
|
|
133
|
+
}
|
|
134
|
+
// ── Dimension 3: Specificity (30%) ───────────────────────────────────────
|
|
135
|
+
function scoreSpecificity(content, details) {
|
|
136
|
+
let score = 0;
|
|
137
|
+
const max = 100;
|
|
138
|
+
// Check 1: Has code blocks (25 pts)
|
|
139
|
+
const codeBlocks = content.match(/```[\s\S]*?```/g) || [];
|
|
140
|
+
const hasCode = codeBlocks.length >= 1;
|
|
141
|
+
addDetail(details, 'specificity', 'Contains code blocks', hasCode, hasCode ? 25 : 0, 25, hasCode ? undefined : 'Add code blocks with concrete examples agents can follow');
|
|
142
|
+
if (hasCode)
|
|
143
|
+
score += 25;
|
|
144
|
+
// Check 2: Has numbered/ordered steps (20 pts)
|
|
145
|
+
const hasSteps = /^\d+\.\s+/m.test(content) || /^-\s+\*\*Step/m.test(content);
|
|
146
|
+
addDetail(details, 'specificity', 'Has numbered/ordered steps', hasSteps, hasSteps ? 20 : 0, 20, hasSteps ? undefined : 'Use numbered steps (1. 2. 3.) for step-by-step instructions');
|
|
147
|
+
if (hasSteps)
|
|
148
|
+
score += 20;
|
|
149
|
+
// Check 3: References specific tools or commands (15 pts)
|
|
150
|
+
const hasToolRefs = /`[a-z]+\s+[a-z]+`|npm\s+|npx\s+|git\s+|docker\s+|curl\s+/i.test(content);
|
|
151
|
+
addDetail(details, 'specificity', 'References specific tools or commands', hasToolRefs, hasToolRefs ? 15 : 0, 15, hasToolRefs ? undefined : 'Reference specific CLI commands or tools the agent should use');
|
|
152
|
+
if (hasToolRefs)
|
|
153
|
+
score += 15;
|
|
154
|
+
// Check 4: File path references (15 pts)
|
|
155
|
+
const hasFilePaths = /`[^\s`]*\/[^\s`]*`|`\.[a-z]+`/i.test(content);
|
|
156
|
+
addDetail(details, 'specificity', 'References file paths or extensions', hasFilePaths, hasFilePaths ? 15 : 0, 15, hasFilePaths ? undefined : 'Include file paths or extensions the skill targets');
|
|
157
|
+
if (hasFilePaths)
|
|
158
|
+
score += 15;
|
|
159
|
+
// Check 5: Conditional logic / decision trees (10 pts)
|
|
160
|
+
const hasConditional = /\bif\b.*\bthen\b|when\s+.*\bdo\b|in\s+case/i.test(content)
|
|
161
|
+
|| /\bIF\b|\bWHEN\b|\bUNLESS\b/m.test(content);
|
|
162
|
+
addDetail(details, 'specificity', 'Has conditional logic or decision rules', hasConditional, hasConditional ? 10 : 0, 10, hasConditional ? undefined : 'Add conditional rules (IF...THEN) for nuanced agent behavior');
|
|
163
|
+
if (hasConditional)
|
|
164
|
+
score += 10;
|
|
165
|
+
// Check 6: Constraints / "Do NOT" rules (15 pts)
|
|
166
|
+
const hasConstraints = /\bdo\s+not\b|\bdon't\b|\bnever\b|\bavoid\b|\bmust\s+not\b/i.test(content);
|
|
167
|
+
addDetail(details, 'specificity', 'Has constraints or "Do NOT" rules', hasConstraints, hasConstraints ? 15 : 0, 15, hasConstraints ? undefined : 'Add "Do NOT" or "Avoid" constraints to prevent common mistakes');
|
|
168
|
+
if (hasConstraints)
|
|
169
|
+
score += 15;
|
|
170
|
+
return Math.min(score, max);
|
|
171
|
+
}
|
|
172
|
+
// ── Dimension 4: Advanced (10%) ──────────────────────────────────────────
|
|
173
|
+
async function scoreAdvanced(content, dir, details) {
|
|
174
|
+
let score = 0;
|
|
175
|
+
const max = 100;
|
|
176
|
+
// Check 1: Has scripts/ directory (25 pts)
|
|
177
|
+
const hasScripts = existsSync(join(dir, 'scripts'));
|
|
178
|
+
addDetail(details, 'advanced', 'Has scripts/ directory', hasScripts, hasScripts ? 25 : 0, 25, hasScripts ? undefined : 'Add a scripts/ directory for automation hooks');
|
|
179
|
+
if (hasScripts)
|
|
180
|
+
score += 25;
|
|
181
|
+
// Check 2: Has references/ or resources/ directory (20 pts)
|
|
182
|
+
const hasRefs = existsSync(join(dir, 'references')) || existsSync(join(dir, 'resources'));
|
|
183
|
+
addDetail(details, 'advanced', 'Has references/ or resources/ directory', hasRefs, hasRefs ? 20 : 0, 20, hasRefs ? undefined : 'Add a references/ directory for supporting documentation');
|
|
184
|
+
if (hasRefs)
|
|
185
|
+
score += 20;
|
|
186
|
+
// Check 3: Has anti-patterns section (20 pts)
|
|
187
|
+
const hasAntiPatterns = /anti.?pattern|common\s+mistake|pitfall|gotcha/i.test(content);
|
|
188
|
+
addDetail(details, 'advanced', 'Has anti-patterns or pitfalls section', hasAntiPatterns, hasAntiPatterns ? 20 : 0, 20, hasAntiPatterns ? undefined : 'Document anti-patterns or common mistakes to help agents avoid errors');
|
|
189
|
+
if (hasAntiPatterns)
|
|
190
|
+
score += 20;
|
|
191
|
+
// Check 4: Has changelog or version history (15 pts)
|
|
192
|
+
const hasChangelog = /changelog|version\s+history|what'?s\s+new/i.test(content)
|
|
193
|
+
|| existsSync(join(dir, 'CHANGELOG.md'));
|
|
194
|
+
addDetail(details, 'advanced', 'Has changelog or version history', hasChangelog, hasChangelog ? 15 : 0, 15, hasChangelog ? undefined : 'Add a version history to track skill evolution');
|
|
195
|
+
if (hasChangelog)
|
|
196
|
+
score += 15;
|
|
197
|
+
// Check 5: Has tests or testing section (20 pts)
|
|
198
|
+
const hasTests = existsSync(join(dir, 'tests')) || existsSync(join(dir, '__tests__'))
|
|
199
|
+
|| /testing|test\s+cases?|validation/i.test(content);
|
|
200
|
+
addDetail(details, 'advanced', 'Has tests or testing instructions', hasTests, hasTests ? 20 : 0, 20, hasTests ? undefined : 'Add testing instructions or a tests/ directory');
|
|
201
|
+
if (hasTests)
|
|
202
|
+
score += 20;
|
|
203
|
+
return Math.min(score, max);
|
|
204
|
+
}
|
|
205
|
+
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
206
|
+
function addDetail(details, dimension, check, passed, points, maxPoints, tip) {
|
|
207
|
+
details.push({ dimension, check, passed, points, maxPoints, tip });
|
|
208
|
+
}
|
|
209
|
+
function toGrade(score) {
|
|
210
|
+
if (score >= 90)
|
|
211
|
+
return 'A+';
|
|
212
|
+
if (score >= 80)
|
|
213
|
+
return 'A';
|
|
214
|
+
if (score >= 70)
|
|
215
|
+
return 'B';
|
|
216
|
+
if (score >= 60)
|
|
217
|
+
return 'C';
|
|
218
|
+
if (score >= 50)
|
|
219
|
+
return 'D';
|
|
220
|
+
return 'F';
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Format a quality score as a colored bar string (for terminal output).
|
|
224
|
+
*/
|
|
225
|
+
export function formatScoreBar(score, width = 20) {
|
|
226
|
+
const filled = Math.round((score / 100) * width);
|
|
227
|
+
const empty = width - filled;
|
|
228
|
+
const bar = '█'.repeat(filled) + '░'.repeat(empty);
|
|
229
|
+
return `${bar} ${score}/100`;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Get color name for a score (for chalk usage).
|
|
233
|
+
*/
|
|
234
|
+
export function getScoreColor(score) {
|
|
235
|
+
if (score >= 70)
|
|
236
|
+
return 'green';
|
|
237
|
+
if (score >= 50)
|
|
238
|
+
return 'yellow';
|
|
239
|
+
return 'red';
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=quality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality.js","sourceRoot":"","sources":["../../src/core/quality.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scanner Rules - Security vulnerability detection rules
|
|
3
|
+
* Used by the audit command to scan skills for potential threats
|
|
4
|
+
*/
|
|
5
|
+
export type Severity = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
6
|
+
export interface ScannerRule {
|
|
7
|
+
id: string;
|
|
8
|
+
category: string;
|
|
9
|
+
severity: Severity;
|
|
10
|
+
title: string;
|
|
11
|
+
description: string;
|
|
12
|
+
pattern: RegExp;
|
|
13
|
+
falsePositiveCheck?: (line: string, context: string[]) => boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface ScanFinding {
|
|
16
|
+
ruleId: string;
|
|
17
|
+
category: string;
|
|
18
|
+
severity: Severity;
|
|
19
|
+
title: string;
|
|
20
|
+
description: string;
|
|
21
|
+
file: string;
|
|
22
|
+
line: number;
|
|
23
|
+
lineContent: string;
|
|
24
|
+
column?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ScanResult {
|
|
27
|
+
findings: ScanFinding[];
|
|
28
|
+
filesScanned: number;
|
|
29
|
+
summary: {
|
|
30
|
+
critical: number;
|
|
31
|
+
high: number;
|
|
32
|
+
medium: number;
|
|
33
|
+
low: number;
|
|
34
|
+
info: number;
|
|
35
|
+
total: number;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* All scanner rules organized by category
|
|
40
|
+
*/
|
|
41
|
+
export declare const SCANNER_RULES: ScannerRule[];
|
|
42
|
+
/**
|
|
43
|
+
* Get rules by category
|
|
44
|
+
*/
|
|
45
|
+
export declare function getRulesByCategory(category: string): ScannerRule[];
|
|
46
|
+
/**
|
|
47
|
+
* Get rule by ID
|
|
48
|
+
*/
|
|
49
|
+
export declare function getRuleById(id: string): ScannerRule | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Get all categories
|
|
52
|
+
*/
|
|
53
|
+
export declare function getCategories(): string[];
|
|
54
|
+
/**
|
|
55
|
+
* Create empty scan result
|
|
56
|
+
*/
|
|
57
|
+
export declare function createEmptyScanResult(): ScanResult;
|
|
58
|
+
//# sourceMappingURL=scanner-rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner-rules.d.ts","sourceRoot":"","sources":["../../src/core/scanner-rules.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAEvE,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;CACrE;AAED,MAAM,WAAW,WAAW;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACvB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE;QACL,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC;CACL;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,EAwStC,CAAC;AAEF;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CAElE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAE/D;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,EAAE,CAExC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,UAAU,CAalD"}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scanner Rules - Security vulnerability detection rules
|
|
3
|
+
* Used by the audit command to scan skills for potential threats
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* All scanner rules organized by category
|
|
7
|
+
*/
|
|
8
|
+
export const SCANNER_RULES = [
|
|
9
|
+
// ═══════════════════════════════════════════
|
|
10
|
+
// PROMPT INJECTION (PI001-PI010)
|
|
11
|
+
// ═══════════════════════════════════════════
|
|
12
|
+
{
|
|
13
|
+
id: 'PI001',
|
|
14
|
+
category: 'prompt-injection',
|
|
15
|
+
severity: 'critical',
|
|
16
|
+
title: 'Direct prompt override attempt',
|
|
17
|
+
description: 'Attempts to override or ignore previous instructions',
|
|
18
|
+
pattern: /ignore\s+(all\s+)?previous\s+instructions/i,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'PI002',
|
|
22
|
+
category: 'prompt-injection',
|
|
23
|
+
severity: 'critical',
|
|
24
|
+
title: 'System prompt override',
|
|
25
|
+
description: 'Attempts to set a new system prompt or role',
|
|
26
|
+
pattern: /you\s+are\s+now\s+(a|an|the)\s+/i,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'PI003',
|
|
30
|
+
category: 'prompt-injection',
|
|
31
|
+
severity: 'high',
|
|
32
|
+
title: 'Role manipulation',
|
|
33
|
+
description: 'Attempts to change the AI agent\'s role or behavior',
|
|
34
|
+
pattern: /(act|behave|pretend|roleplay)\s+(as|like)\s+(a|an|the)\s+/i,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'PI004',
|
|
38
|
+
category: 'prompt-injection',
|
|
39
|
+
severity: 'high',
|
|
40
|
+
title: 'Instruction bypass',
|
|
41
|
+
description: 'Attempts to bypass safety restrictions',
|
|
42
|
+
pattern: /(bypass|circumvent|override|disable)\s+(safety|restrictions?|filters?|guardrails?)/i,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'PI005',
|
|
46
|
+
category: 'prompt-injection',
|
|
47
|
+
severity: 'medium',
|
|
48
|
+
title: 'Jailbreak attempt',
|
|
49
|
+
description: 'Known jailbreak patterns detected',
|
|
50
|
+
pattern: /(DAN|do\s+anything\s+now|STAN|jailbreak)/i,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'PI006',
|
|
54
|
+
category: 'prompt-injection',
|
|
55
|
+
severity: 'medium',
|
|
56
|
+
title: 'Prompt leak request',
|
|
57
|
+
description: 'Attempts to extract system prompts or instructions',
|
|
58
|
+
pattern: /(reveal|show|print|output|display)\s+(your|the|system)\s+(prompt|instructions|rules)/i,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'PI007',
|
|
62
|
+
category: 'prompt-injection',
|
|
63
|
+
severity: 'high',
|
|
64
|
+
title: 'Encoding-based injection',
|
|
65
|
+
description: 'Using base64 or hex encoding to hide instructions',
|
|
66
|
+
pattern: /(atob|btoa|Buffer\.from)\s*\(\s*['"`]/,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'PI008',
|
|
70
|
+
category: 'prompt-injection',
|
|
71
|
+
severity: 'medium',
|
|
72
|
+
title: 'Delimiter manipulation',
|
|
73
|
+
description: 'Using special delimiters to break prompt boundaries',
|
|
74
|
+
pattern: /(```system|<\|im_start\|>|<\|endoftext\|>|<\/system>)/i,
|
|
75
|
+
},
|
|
76
|
+
// ═══════════════════════════════════════════
|
|
77
|
+
// COMMAND INJECTION (CI001-CI008)
|
|
78
|
+
// ═══════════════════════════════════════════
|
|
79
|
+
{
|
|
80
|
+
id: 'CI001',
|
|
81
|
+
category: 'command-injection',
|
|
82
|
+
severity: 'critical',
|
|
83
|
+
title: 'Destructive command',
|
|
84
|
+
description: 'Potentially destructive shell command',
|
|
85
|
+
pattern: /\b(rm\s+-rf\s+[\/~]|rmdir\s+\/s|del\s+\/f\s+\/s|format\s+[a-z]:)/i,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'CI002',
|
|
89
|
+
category: 'command-injection',
|
|
90
|
+
severity: 'critical',
|
|
91
|
+
title: 'Remote code execution',
|
|
92
|
+
description: 'Piping remote content to shell execution',
|
|
93
|
+
pattern: /\b(curl|wget)\s+.*\|\s*(bash|sh|zsh|python|node|eval)/i,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'CI003',
|
|
97
|
+
category: 'command-injection',
|
|
98
|
+
severity: 'high',
|
|
99
|
+
title: 'Shell eval usage',
|
|
100
|
+
description: 'Dynamic code evaluation which could execute arbitrary code',
|
|
101
|
+
pattern: /\b(eval|exec|execSync|spawnSync|child_process)\s*\(/,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 'CI004',
|
|
105
|
+
category: 'command-injection',
|
|
106
|
+
severity: 'high',
|
|
107
|
+
title: 'Dangerous chmod',
|
|
108
|
+
description: 'Setting overly permissive file permissions',
|
|
109
|
+
pattern: /chmod\s+(777|666|a\+[rwx])/,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'CI005',
|
|
113
|
+
category: 'command-injection',
|
|
114
|
+
severity: 'medium',
|
|
115
|
+
title: 'Sudo usage',
|
|
116
|
+
description: 'Privilege escalation via sudo',
|
|
117
|
+
pattern: /\bsudo\s+/,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'CI006',
|
|
121
|
+
category: 'command-injection',
|
|
122
|
+
severity: 'medium',
|
|
123
|
+
title: 'Network download',
|
|
124
|
+
description: 'Downloading content from external sources',
|
|
125
|
+
pattern: /\b(curl|wget|fetch)\s+https?:\/\//i,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: 'CI007',
|
|
129
|
+
category: 'command-injection',
|
|
130
|
+
severity: 'high',
|
|
131
|
+
title: 'Shell script generation',
|
|
132
|
+
description: 'Dynamically creating and executing shell scripts',
|
|
133
|
+
pattern: />\s*\/tmp\/.*\.sh|echo\s+.*>\s+.*\.sh\s*&&\s*(bash|sh|chmod)/,
|
|
134
|
+
},
|
|
135
|
+
// ═══════════════════════════════════════════
|
|
136
|
+
// DATA EXFILTRATION (DE001-DE006)
|
|
137
|
+
// ═══════════════════════════════════════════
|
|
138
|
+
{
|
|
139
|
+
id: 'DE001',
|
|
140
|
+
category: 'data-exfiltration',
|
|
141
|
+
severity: 'critical',
|
|
142
|
+
title: 'Data exfiltration via webhook',
|
|
143
|
+
description: 'Sending data to external webhook or API endpoint',
|
|
144
|
+
pattern: /(fetch|axios|got|request)\s*\(\s*['"`](https?:\/\/[^'"]*?(webhook|exfil|ngrok|burp|requestbin))/i,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'DE002',
|
|
148
|
+
category: 'data-exfiltration',
|
|
149
|
+
severity: 'high',
|
|
150
|
+
title: 'DNS exfiltration',
|
|
151
|
+
description: 'Using DNS queries to exfiltrate data',
|
|
152
|
+
pattern: /dns\.(resolve|lookup)\s*\(.*\$\{/,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'DE003',
|
|
156
|
+
category: 'data-exfiltration',
|
|
157
|
+
severity: 'high',
|
|
158
|
+
title: 'Environment variable access',
|
|
159
|
+
description: 'Reading sensitive environment variables',
|
|
160
|
+
pattern: /process\.env\[(.*?(KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL))/i,
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: 'DE004',
|
|
164
|
+
category: 'data-exfiltration',
|
|
165
|
+
severity: 'medium',
|
|
166
|
+
title: 'File read of sensitive paths',
|
|
167
|
+
description: 'Reading from sensitive system paths',
|
|
168
|
+
pattern: /readFile(Sync)?\s*\(\s*['"`](\/etc\/(passwd|shadow|ssh)|~\/\.ssh|~\/\.aws)/,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'DE005',
|
|
172
|
+
category: 'data-exfiltration',
|
|
173
|
+
severity: 'high',
|
|
174
|
+
title: 'Base64 encoding of data',
|
|
175
|
+
description: 'Encoding data to base64 before transmission',
|
|
176
|
+
pattern: /Buffer\.from\(.*\)\.toString\(['"`]base64['"`]\)/,
|
|
177
|
+
},
|
|
178
|
+
// ═══════════════════════════════════════════
|
|
179
|
+
// TOOL ABUSE (TA001-TA008)
|
|
180
|
+
// ═══════════════════════════════════════════
|
|
181
|
+
{
|
|
182
|
+
id: 'TA001',
|
|
183
|
+
category: 'tool-abuse',
|
|
184
|
+
severity: 'high',
|
|
185
|
+
title: 'Filesystem traversal',
|
|
186
|
+
description: 'Accessing files outside project directory',
|
|
187
|
+
pattern: /\.\.\/(\.\.\/){2,}|path\.(join|resolve)\s*\(.*\.\.\//,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: 'TA002',
|
|
191
|
+
category: 'tool-abuse',
|
|
192
|
+
severity: 'medium',
|
|
193
|
+
title: 'Unrestricted file write',
|
|
194
|
+
description: 'Writing to system or home directories',
|
|
195
|
+
pattern: /writeFile(Sync)?\s*\(\s*['"`](\/usr|\/etc|\/var|\/home|\/root)/,
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'TA003',
|
|
199
|
+
category: 'tool-abuse',
|
|
200
|
+
severity: 'medium',
|
|
201
|
+
title: 'Process spawning',
|
|
202
|
+
description: 'Spawning new processes from skill code',
|
|
203
|
+
pattern: /\b(spawn|fork|exec)\s*\(\s*['"`]/,
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: 'TA004',
|
|
207
|
+
category: 'tool-abuse',
|
|
208
|
+
severity: 'low',
|
|
209
|
+
title: 'Global npm install',
|
|
210
|
+
description: 'Installing packages globally',
|
|
211
|
+
pattern: /npm\s+install\s+(-g|--global)/,
|
|
212
|
+
},
|
|
213
|
+
// ═══════════════════════════════════════════
|
|
214
|
+
// HARDCODED SECRETS (HS001-HS008)
|
|
215
|
+
// ═══════════════════════════════════════════
|
|
216
|
+
{
|
|
217
|
+
id: 'HS001',
|
|
218
|
+
category: 'hardcoded-secrets',
|
|
219
|
+
severity: 'critical',
|
|
220
|
+
title: 'AWS access key',
|
|
221
|
+
description: 'Hardcoded AWS access key ID',
|
|
222
|
+
pattern: /AKIA[0-9A-Z]{16}/,
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: 'HS002',
|
|
226
|
+
category: 'hardcoded-secrets',
|
|
227
|
+
severity: 'critical',
|
|
228
|
+
title: 'API key pattern',
|
|
229
|
+
description: 'Possible hardcoded API key',
|
|
230
|
+
pattern: /(sk-[a-zA-Z0-9]{20,}|sk-proj-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|gho_[a-zA-Z0-9]{36})/,
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: 'HS003',
|
|
234
|
+
category: 'hardcoded-secrets',
|
|
235
|
+
severity: 'high',
|
|
236
|
+
title: 'Generic secret assignment',
|
|
237
|
+
description: 'Variable named secret/password/token with hardcoded value',
|
|
238
|
+
pattern: /(password|secret|token|api_key|apikey|api[-_]?secret)\s*[:=]\s*['"`][A-Za-z0-9+/=]{8,}/i,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: 'HS004',
|
|
242
|
+
category: 'hardcoded-secrets',
|
|
243
|
+
severity: 'high',
|
|
244
|
+
title: 'Private key block',
|
|
245
|
+
description: 'Embedded private key',
|
|
246
|
+
pattern: /-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/,
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
id: 'HS005',
|
|
250
|
+
category: 'hardcoded-secrets',
|
|
251
|
+
severity: 'high',
|
|
252
|
+
title: 'JWT token',
|
|
253
|
+
description: 'Hardcoded JWT token',
|
|
254
|
+
pattern: /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'HS006',
|
|
258
|
+
category: 'hardcoded-secrets',
|
|
259
|
+
severity: 'medium',
|
|
260
|
+
title: 'Database connection string',
|
|
261
|
+
description: 'Hardcoded database URL with credentials',
|
|
262
|
+
pattern: /(postgres|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/,
|
|
263
|
+
},
|
|
264
|
+
// ═══════════════════════════════════════════
|
|
265
|
+
// UNICODE STEGANOGRAPHY (UC001-UC006)
|
|
266
|
+
// ═══════════════════════════════════════════
|
|
267
|
+
{
|
|
268
|
+
id: 'UC001',
|
|
269
|
+
category: 'unicode-steganography',
|
|
270
|
+
severity: 'high',
|
|
271
|
+
title: 'Zero-width characters',
|
|
272
|
+
description: 'Hidden zero-width characters that may contain embedded instructions',
|
|
273
|
+
pattern: /[\u200B\u200C\u200D\uFEFF\u200E\u200F]/,
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
id: 'UC002',
|
|
277
|
+
category: 'unicode-steganography',
|
|
278
|
+
severity: 'medium',
|
|
279
|
+
title: 'Right-to-left override',
|
|
280
|
+
description: 'Unicode RTL override that can disguise code direction',
|
|
281
|
+
pattern: /[\u202A\u202B\u202C\u202D\u202E\u2066\u2067\u2068\u2069]/,
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
id: 'UC003',
|
|
285
|
+
category: 'unicode-steganography',
|
|
286
|
+
severity: 'medium',
|
|
287
|
+
title: 'Homoglyph characters',
|
|
288
|
+
description: 'Characters that look like ASCII but are different Unicode codepoints',
|
|
289
|
+
pattern: /[\u0410\u0412\u0415\u041A\u041C\u041D\u041E\u0420\u0421\u0422\u0425]/,
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
id: 'UC004',
|
|
293
|
+
category: 'unicode-steganography',
|
|
294
|
+
severity: 'low',
|
|
295
|
+
title: 'Invisible separator characters',
|
|
296
|
+
description: 'Unicode separator characters that may hide content',
|
|
297
|
+
pattern: /[\u2028\u2029\u00A0\u2000-\u200A]/,
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
/**
|
|
301
|
+
* Get rules by category
|
|
302
|
+
*/
|
|
303
|
+
export function getRulesByCategory(category) {
|
|
304
|
+
return SCANNER_RULES.filter(r => r.category === category);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Get rule by ID
|
|
308
|
+
*/
|
|
309
|
+
export function getRuleById(id) {
|
|
310
|
+
return SCANNER_RULES.find(r => r.id === id);
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Get all categories
|
|
314
|
+
*/
|
|
315
|
+
export function getCategories() {
|
|
316
|
+
return [...new Set(SCANNER_RULES.map(r => r.category))];
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Create empty scan result
|
|
320
|
+
*/
|
|
321
|
+
export function createEmptyScanResult() {
|
|
322
|
+
return {
|
|
323
|
+
findings: [],
|
|
324
|
+
filesScanned: 0,
|
|
325
|
+
summary: {
|
|
326
|
+
critical: 0,
|
|
327
|
+
high: 0,
|
|
328
|
+
medium: 0,
|
|
329
|
+
low: 0,
|
|
330
|
+
info: 0,
|
|
331
|
+
total: 0,
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=scanner-rules.js.map
|