@codexstar/bug-hunter 3.0.0 → 3.0.6
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/CHANGELOG.md +149 -83
- package/README.md +150 -15
- package/SKILL.md +94 -27
- package/agents/openai.yaml +4 -0
- package/bin/bug-hunter +9 -3
- package/docs/images/2026-03-12-fix-plan-rollout.png +0 -0
- package/docs/images/2026-03-12-hero-bug-hunter-overview.png +0 -0
- package/docs/images/2026-03-12-machine-readable-artifacts.png +0 -0
- package/docs/images/2026-03-12-pr-review-flow.png +0 -0
- package/docs/images/2026-03-12-security-pack.png +0 -0
- package/docs/images/adversarial-debate.png +0 -0
- package/docs/images/doc-verify-fix-plan.png +0 -0
- package/docs/images/hero.png +0 -0
- package/docs/images/pipeline-overview.png +0 -0
- package/docs/images/security-finding-card.png +0 -0
- package/docs/plans/2026-03-11-structured-output-migration-plan.md +288 -0
- package/docs/plans/2026-03-12-audit-bug-fixes-surgical-plan.md +193 -0
- package/docs/plans/2026-03-12-enterprise-security-pack-e2e-plan.md +59 -0
- package/docs/plans/2026-03-12-local-security-skills-integration-plan.md +39 -0
- package/docs/plans/2026-03-12-pr-review-strategic-fix-flow.md +78 -0
- package/evals/evals.json +366 -102
- package/modes/extended.md +2 -2
- package/modes/fix-loop.md +30 -30
- package/modes/fix-pipeline.md +32 -6
- package/modes/large-codebase.md +14 -15
- package/modes/local-sequential.md +44 -20
- package/modes/loop.md +56 -56
- package/modes/parallel.md +3 -3
- package/modes/scaled.md +2 -2
- package/modes/single-file.md +3 -3
- package/modes/small.md +11 -11
- package/package.json +11 -1
- package/prompts/fixer.md +37 -23
- package/prompts/hunter.md +39 -20
- package/prompts/referee.md +34 -20
- package/prompts/skeptic.md +25 -22
- package/schemas/coverage.schema.json +67 -0
- package/schemas/examples/findings.invalid.json +13 -0
- package/schemas/examples/findings.valid.json +17 -0
- package/schemas/findings.schema.json +76 -0
- package/schemas/fix-plan.schema.json +94 -0
- package/schemas/fix-report.schema.json +105 -0
- package/schemas/fix-strategy.schema.json +99 -0
- package/schemas/recon.schema.json +31 -0
- package/schemas/referee.schema.json +46 -0
- package/schemas/shared.schema.json +51 -0
- package/schemas/skeptic.schema.json +21 -0
- package/scripts/bug-hunter-state.cjs +35 -12
- package/scripts/code-index.cjs +11 -4
- package/scripts/fix-lock.cjs +95 -25
- package/scripts/payload-guard.cjs +24 -10
- package/scripts/pr-scope.cjs +181 -0
- package/scripts/prepublish-guard.cjs +82 -0
- package/scripts/render-report.cjs +346 -0
- package/scripts/run-bug-hunter.cjs +669 -33
- package/scripts/schema-runtime.cjs +273 -0
- package/scripts/schema-validate.cjs +40 -0
- package/scripts/tests/bug-hunter-state.test.cjs +68 -3
- package/scripts/tests/code-index.test.cjs +15 -0
- package/scripts/tests/fix-lock.test.cjs +60 -2
- package/scripts/tests/fixtures/flaky-worker.cjs +6 -1
- package/scripts/tests/fixtures/low-confidence-worker.cjs +8 -2
- package/scripts/tests/fixtures/success-worker.cjs +6 -1
- package/scripts/tests/payload-guard.test.cjs +154 -2
- package/scripts/tests/pr-scope.test.cjs +212 -0
- package/scripts/tests/render-report.test.cjs +180 -0
- package/scripts/tests/run-bug-hunter.test.cjs +686 -2
- package/scripts/tests/security-skills-integration.test.cjs +29 -0
- package/scripts/tests/skills-packaging.test.cjs +30 -0
- package/scripts/tests/worktree-harvest.test.cjs +67 -1
- package/scripts/worktree-harvest.cjs +62 -9
- package/skills/README.md +19 -0
- package/skills/commit-security-scan/SKILL.md +63 -0
- package/skills/security-review/SKILL.md +57 -0
- package/skills/threat-model-generation/SKILL.md +47 -0
- package/skills/vulnerability-validation/SKILL.md +59 -0
- package/templates/subagent-wrapper.md +12 -3
- package/modes/_dispatch.md +0 -121
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const assert = require('node:assert/strict');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
|
|
5
|
+
const { resolveSkillScript } = require('./test-utils.cjs');
|
|
6
|
+
|
|
7
|
+
test('main SKILL routes into bundled local security skills', () => {
|
|
8
|
+
const skillDoc = fs.readFileSync(resolveSkillScript('..', 'SKILL.md'), 'utf8');
|
|
9
|
+
|
|
10
|
+
assert.match(skillDoc, /skills\/commit-security-scan\/SKILL\.md/);
|
|
11
|
+
assert.match(skillDoc, /skills\/security-review\/SKILL\.md/);
|
|
12
|
+
assert.match(skillDoc, /skills\/threat-model-generation\/SKILL\.md/);
|
|
13
|
+
assert.match(skillDoc, /skills\/vulnerability-validation\/SKILL\.md/);
|
|
14
|
+
|
|
15
|
+
assert.match(skillDoc, /--pr-security/);
|
|
16
|
+
assert.match(skillDoc, /--security-review/);
|
|
17
|
+
assert.match(skillDoc, /--validate-security/);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('README documents the integrated enterprise security pack flows', () => {
|
|
21
|
+
const readme = fs.readFileSync(resolveSkillScript('..', 'README.md'), 'utf8');
|
|
22
|
+
|
|
23
|
+
assert.match(readme, /PR-focused security review routes into `commit-security-scan`/);
|
|
24
|
+
assert.match(readme, /`--threat-model` routes into `threat-model-generation`/);
|
|
25
|
+
assert.match(readme, /enterprise\/full security review routes into `security-review`/);
|
|
26
|
+
assert.match(readme, /`--pr-security`/);
|
|
27
|
+
assert.match(readme, /`--security-review`/);
|
|
28
|
+
assert.match(readme, /`--validate-security`/);
|
|
29
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const assert = require('node:assert/strict');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const test = require('node:test');
|
|
5
|
+
|
|
6
|
+
const { resolveSkillScript } = require('./test-utils.cjs');
|
|
7
|
+
|
|
8
|
+
test('package.json ships the bundled local security skills', () => {
|
|
9
|
+
const packageJson = require(resolveSkillScript('..', 'package.json'));
|
|
10
|
+
assert.equal(Array.isArray(packageJson.files), true);
|
|
11
|
+
assert.equal(packageJson.files.includes('skills/'), true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('bundled local security skills exist with SKILL.md entrypoints', () => {
|
|
15
|
+
const skillNames = [
|
|
16
|
+
'commit-security-scan',
|
|
17
|
+
'security-review',
|
|
18
|
+
'threat-model-generation',
|
|
19
|
+
'vulnerability-validation'
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
for (const skillName of skillNames) {
|
|
23
|
+
const skillPath = resolveSkillScript('..', 'skills', skillName, 'SKILL.md');
|
|
24
|
+
assert.equal(fs.existsSync(skillPath), true, `${skillName} should exist`);
|
|
25
|
+
const contents = fs.readFileSync(skillPath, 'utf8');
|
|
26
|
+
assert.match(contents, /^---/);
|
|
27
|
+
assert.match(contents, /name:/);
|
|
28
|
+
assert.match(contents, /description:/);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
@@ -20,7 +20,7 @@ function makeGitFixture() {
|
|
|
20
20
|
// Bare origin
|
|
21
21
|
const origin = path.join(sandbox, 'origin.git');
|
|
22
22
|
fs.mkdirSync(origin, { recursive: true });
|
|
23
|
-
execFileSync('git', ['init', '--bare'], { cwd: origin, stdio: 'ignore' });
|
|
23
|
+
execFileSync('git', ['init', '--bare', '-b', 'main'], { cwd: origin, stdio: 'ignore' });
|
|
24
24
|
|
|
25
25
|
// Working clone
|
|
26
26
|
const repo = path.join(sandbox, 'repo');
|
|
@@ -106,6 +106,17 @@ test('prepare cleans up stale worktree', () => {
|
|
|
106
106
|
assert.equal(result.ok, true);
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
+
test('prepare refuses to delete an unrelated pre-existing directory', () => {
|
|
110
|
+
const { repo, fixBranch } = makeGitFixture();
|
|
111
|
+
const wtDir = path.join(repo, '.bug-hunter', 'worktrees', 'notes');
|
|
112
|
+
fs.mkdirSync(wtDir, { recursive: true });
|
|
113
|
+
fs.writeFileSync(path.join(wtDir, 'keep.txt'), 'keep\n');
|
|
114
|
+
|
|
115
|
+
const result = runRaw('node', [SCRIPT, 'prepare', fixBranch, wtDir], { cwd: repo });
|
|
116
|
+
assert.notEqual(result.status, 0);
|
|
117
|
+
assert.equal(fs.existsSync(path.join(wtDir, 'keep.txt')), true);
|
|
118
|
+
});
|
|
119
|
+
|
|
109
120
|
test('harvest finds new commits', () => {
|
|
110
121
|
const { repo, fixBranch } = makeGitFixture();
|
|
111
122
|
const wtDir = path.join(repo, '.bug-hunter', 'worktrees', 'batch-1');
|
|
@@ -180,6 +191,45 @@ test('cleanup handles missing worktree gracefully', () => {
|
|
|
180
191
|
assert.equal(result.reason, 'not-found');
|
|
181
192
|
});
|
|
182
193
|
|
|
194
|
+
test('cleanup does not report success for unmanaged directories', () => {
|
|
195
|
+
const { repo } = makeGitFixture();
|
|
196
|
+
const wtDir = path.join(repo, '.bug-hunter', 'worktrees', 'notes');
|
|
197
|
+
fs.mkdirSync(wtDir, { recursive: true });
|
|
198
|
+
fs.writeFileSync(path.join(wtDir, 'keep.txt'), 'keep\n');
|
|
199
|
+
|
|
200
|
+
const result = runJson('node', [SCRIPT, 'cleanup', wtDir], { cwd: repo });
|
|
201
|
+
assert.equal(result.ok, true);
|
|
202
|
+
assert.equal(result.removed, false);
|
|
203
|
+
assert.equal(fs.existsSync(path.join(wtDir, 'keep.txt')), true);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('cleanup preserves worktree contents when harvest fails', () => {
|
|
207
|
+
const { repo, fixBranch } = makeGitFixture();
|
|
208
|
+
const wtDir = path.join(repo, '.bug-hunter', 'worktrees', 'batch-1');
|
|
209
|
+
|
|
210
|
+
runJson('node', [SCRIPT, 'prepare', fixBranch, wtDir], { cwd: repo });
|
|
211
|
+
fs.rmSync(path.join(wtDir, '.worktree-manifest.json'));
|
|
212
|
+
|
|
213
|
+
const result = runJson('node', [SCRIPT, 'cleanup', wtDir], { cwd: repo });
|
|
214
|
+
assert.equal(result.ok, true);
|
|
215
|
+
assert.equal(result.removed, false);
|
|
216
|
+
assert.equal(fs.existsSync(wtDir), true);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test('cleanup returns stash metadata when defensive harvest stashes uncommitted work', () => {
|
|
220
|
+
const { repo, fixBranch } = makeGitFixture();
|
|
221
|
+
const wtDir = path.join(repo, '.bug-hunter', 'worktrees', 'batch-1');
|
|
222
|
+
|
|
223
|
+
runJson('node', [SCRIPT, 'prepare', fixBranch, wtDir], { cwd: repo });
|
|
224
|
+
fs.writeFileSync(path.join(wtDir, 'dirty.txt'), 'uncommitted\n');
|
|
225
|
+
|
|
226
|
+
const result = runJson('node', [SCRIPT, 'cleanup', wtDir], { cwd: repo });
|
|
227
|
+
assert.equal(result.ok, true);
|
|
228
|
+
assert.equal(result.removed, true);
|
|
229
|
+
assert.equal(typeof result.stashRef, 'string');
|
|
230
|
+
assert.equal(result.stashRef.length > 0, true);
|
|
231
|
+
});
|
|
232
|
+
|
|
183
233
|
test('cleanup-all removes multiple worktrees', () => {
|
|
184
234
|
const { repo, fixBranch } = makeGitFixture();
|
|
185
235
|
const parentDir = path.join(repo, '.bug-hunter', 'worktrees');
|
|
@@ -202,6 +252,21 @@ test('cleanup-all removes multiple worktrees', () => {
|
|
|
202
252
|
assert.ok(result.cleaned >= 1);
|
|
203
253
|
});
|
|
204
254
|
|
|
255
|
+
test('cleanup-all preserves unrelated directories under the parent', () => {
|
|
256
|
+
const { repo, fixBranch } = makeGitFixture();
|
|
257
|
+
const parentDir = path.join(repo, '.bug-hunter', 'worktrees');
|
|
258
|
+
const wt1 = path.join(parentDir, 'batch-1');
|
|
259
|
+
const unrelated = path.join(parentDir, 'notes');
|
|
260
|
+
|
|
261
|
+
runJson('node', [SCRIPT, 'prepare', fixBranch, wt1], { cwd: repo });
|
|
262
|
+
fs.mkdirSync(unrelated, { recursive: true });
|
|
263
|
+
fs.writeFileSync(path.join(unrelated, 'readme.txt'), 'keep me\n', 'utf8');
|
|
264
|
+
|
|
265
|
+
runJson('node', [SCRIPT, 'cleanup-all', parentDir], { cwd: repo });
|
|
266
|
+
assert.equal(fs.existsSync(unrelated), true);
|
|
267
|
+
assert.equal(fs.existsSync(path.join(unrelated, 'readme.txt')), true);
|
|
268
|
+
});
|
|
269
|
+
|
|
205
270
|
test('checkout-fix returns main tree to fix branch', () => {
|
|
206
271
|
const { repo, fixBranch } = makeGitFixture();
|
|
207
272
|
const wtDir = path.join(repo, '.bug-hunter', 'worktrees', 'batch-1');
|
|
@@ -290,6 +355,7 @@ test('status reports worktree health', () => {
|
|
|
290
355
|
assert.equal(s2.isStale, false);
|
|
291
356
|
assert.equal(s2.commitCount, 0);
|
|
292
357
|
assert.equal(s2.harvested, false);
|
|
358
|
+
assert.equal(s2.hasUncommitted, false);
|
|
293
359
|
|
|
294
360
|
// Clean up
|
|
295
361
|
runJson('node', [SCRIPT, 'harvest', wtDir], { cwd: repo });
|
|
@@ -82,6 +82,21 @@ function writeJsonFile(filePath, data) {
|
|
|
82
82
|
fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
function isManagedWorktreeDir(worktreeDir) {
|
|
86
|
+
const absDir = path.resolve(worktreeDir);
|
|
87
|
+
if (readJsonFile(manifestPath(absDir))) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
const listed = gitSafe(['worktree', 'list', '--porcelain']);
|
|
91
|
+
if (!listed.ok || !listed.output) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return listed.output
|
|
95
|
+
.split('\n')
|
|
96
|
+
.filter((line) => line.startsWith('worktree '))
|
|
97
|
+
.some((line) => path.resolve(line.slice('worktree '.length).trim()) === absDir);
|
|
98
|
+
}
|
|
99
|
+
|
|
85
100
|
// ---------------------------------------------------------------------------
|
|
86
101
|
// prepare — create worktree on the fix branch
|
|
87
102
|
// ---------------------------------------------------------------------------
|
|
@@ -96,8 +111,13 @@ function prepare(fixBranch, worktreeDir) {
|
|
|
96
111
|
process.exit(1);
|
|
97
112
|
}
|
|
98
113
|
|
|
99
|
-
// 2. If worktreeDir already exists, clean up stale worktree
|
|
114
|
+
// 2. If worktreeDir already exists, clean up stale managed worktree only
|
|
100
115
|
if (fs.existsSync(absDir)) {
|
|
116
|
+
const managed = isManagedWorktreeDir(absDir);
|
|
117
|
+
if (!managed) {
|
|
118
|
+
out({ ok: false, error: 'path-not-managed-worktree', detail: `${absDir} already exists and is not a managed worktree` });
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
101
121
|
gitSafe(['worktree', 'remove', absDir, '--force']);
|
|
102
122
|
if (fs.existsSync(absDir)) {
|
|
103
123
|
fs.rmSync(absDir, { recursive: true, force: true });
|
|
@@ -319,9 +339,21 @@ function cleanup(worktreeDir) {
|
|
|
319
339
|
return;
|
|
320
340
|
}
|
|
321
341
|
|
|
342
|
+
const managed = isManagedWorktreeDir(absDir);
|
|
343
|
+
if (!managed) {
|
|
344
|
+
out({ ok: true, removed: false, reason: 'not-managed-worktree' });
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let defensiveHarvest = readJsonFile(harvestPath(absDir));
|
|
322
349
|
// If harvest hasn't run yet, run it defensively
|
|
323
|
-
if (!
|
|
324
|
-
try {
|
|
350
|
+
if (!defensiveHarvest) {
|
|
351
|
+
try {
|
|
352
|
+
defensiveHarvest = harvestCore(absDir);
|
|
353
|
+
} catch (_) {
|
|
354
|
+
out({ ok: true, removed: false, reason: 'harvest-failed' });
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
325
357
|
}
|
|
326
358
|
|
|
327
359
|
const manifest = readJsonFile(manifestPath(absDir));
|
|
@@ -333,11 +365,14 @@ function cleanup(worktreeDir) {
|
|
|
333
365
|
}
|
|
334
366
|
|
|
335
367
|
gitSafe(['worktree', 'prune']);
|
|
368
|
+
const removed = !fs.existsSync(absDir);
|
|
336
369
|
|
|
337
370
|
out({
|
|
338
371
|
ok: true,
|
|
339
|
-
removed
|
|
340
|
-
detachedMainTree: manifest ? manifest.detachedMainTree : false
|
|
372
|
+
removed,
|
|
373
|
+
detachedMainTree: manifest ? manifest.detachedMainTree : false,
|
|
374
|
+
reason: removed ? undefined : 'remove-failed',
|
|
375
|
+
stashRef: defensiveHarvest && defensiveHarvest.stashRef ? defensiveHarvest.stashRef : null
|
|
341
376
|
});
|
|
342
377
|
}
|
|
343
378
|
|
|
@@ -367,15 +402,26 @@ function cleanupAll(parentDir) {
|
|
|
367
402
|
for (const name of entries) {
|
|
368
403
|
const wtDir = path.join(absParent, name);
|
|
369
404
|
try {
|
|
405
|
+
const managed = isManagedWorktreeDir(wtDir);
|
|
406
|
+
if (!managed) {
|
|
407
|
+
results.push({ name, removed: false, reason: 'not-managed-worktree' });
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
let defensiveHarvest = readJsonFile(harvestPath(wtDir));
|
|
370
411
|
// Defensive harvest before cleanup
|
|
371
|
-
if (!
|
|
372
|
-
try {
|
|
412
|
+
if (!defensiveHarvest) {
|
|
413
|
+
try {
|
|
414
|
+
defensiveHarvest = harvestCore(wtDir);
|
|
415
|
+
} catch (_) {
|
|
416
|
+
results.push({ name, removed: false, reason: 'harvest-failed' });
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
373
419
|
}
|
|
374
420
|
gitSafe(['worktree', 'remove', wtDir, '--force']);
|
|
375
421
|
if (fs.existsSync(wtDir)) {
|
|
376
422
|
fs.rmSync(wtDir, { recursive: true, force: true });
|
|
377
423
|
}
|
|
378
|
-
results.push({ name, removed: true });
|
|
424
|
+
results.push({ name, removed: true, stashRef: defensiveHarvest && defensiveHarvest.stashRef ? defensiveHarvest.stashRef : null });
|
|
379
425
|
} catch (err) {
|
|
380
426
|
results.push({ name, removed: false, error: err.message });
|
|
381
427
|
}
|
|
@@ -410,7 +456,14 @@ function statusCmd(worktreeDir) {
|
|
|
410
456
|
const isStale = age !== null && age > STALE_AGE_MS;
|
|
411
457
|
|
|
412
458
|
const statusOutput = gitSafe(['status', '--porcelain'], absDir);
|
|
413
|
-
const
|
|
459
|
+
const statusLines = statusOutput.ok
|
|
460
|
+
? statusOutput.output.split('\n').filter(Boolean)
|
|
461
|
+
: [];
|
|
462
|
+
const relevantLines = statusLines.filter(line => {
|
|
463
|
+
const fileName = line.slice(3);
|
|
464
|
+
return !META_FILES.some(mf => fileName === mf || fileName.endsWith(`/${mf}`));
|
|
465
|
+
});
|
|
466
|
+
const hasUncommitted = relevantLines.length > 0;
|
|
414
467
|
|
|
415
468
|
let commitCount = 0;
|
|
416
469
|
if (manifest) {
|
package/skills/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Bundled Local Security Skills
|
|
2
|
+
|
|
3
|
+
Bug Hunter ships with a local security pack under `skills/` so the repository stays portable and self-contained.
|
|
4
|
+
|
|
5
|
+
Included skills:
|
|
6
|
+
- `commit-security-scan`
|
|
7
|
+
- `security-review`
|
|
8
|
+
- `threat-model-generation`
|
|
9
|
+
- `vulnerability-validation`
|
|
10
|
+
|
|
11
|
+
## How They Connect to Bug Hunter
|
|
12
|
+
|
|
13
|
+
These skills are part of the main Bug Hunter orchestration flow:
|
|
14
|
+
- PR-focused security review routes into `commit-security-scan`
|
|
15
|
+
- `--threat-model` routes into `threat-model-generation`
|
|
16
|
+
- `--security-review` routes into `security-review`
|
|
17
|
+
- `--validate-security` routes into `vulnerability-validation`
|
|
18
|
+
|
|
19
|
+
Bug Hunter remains the top-level orchestrator. These bundled skills provide focused security workflows and operate on Bug Hunter-native artifacts under `.bug-hunter/`.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: commit-security-scan
|
|
3
|
+
description: Scan code changes for security vulnerabilities using Bug Hunter-native artifacts and STRIDE context. Use whenever the user asks for PR security review, commit-diff scanning, staged-change security checks, branch-comparison security review, or pre-merge security analysis of changed code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Commit Security Scan
|
|
7
|
+
|
|
8
|
+
This is a bundled local Bug Hunter companion skill. It is portable and self-contained: use `.bug-hunter/*` artifacts, never `.factory/*` paths.
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
Review *changed code* for security issues only. This skill is optimized for:
|
|
13
|
+
- PR review
|
|
14
|
+
- staged diff review
|
|
15
|
+
- branch diff review
|
|
16
|
+
- commit / commit-range security scanning
|
|
17
|
+
|
|
18
|
+
## Inputs
|
|
19
|
+
|
|
20
|
+
Resolve the scan scope from the user request:
|
|
21
|
+
- PR review → use `scripts/pr-scope.cjs`
|
|
22
|
+
- staged review → use `git diff --cached --name-only`
|
|
23
|
+
- branch diff → use `git diff --name-only <base>...<head>`
|
|
24
|
+
- commit range → use `git diff --name-only <base>..<head>`
|
|
25
|
+
|
|
26
|
+
## Workflow
|
|
27
|
+
|
|
28
|
+
1. Ensure threat-model context exists.
|
|
29
|
+
- Preferred artifacts:
|
|
30
|
+
- `.bug-hunter/threat-model.md`
|
|
31
|
+
- `.bug-hunter/security-config.json`
|
|
32
|
+
- If missing, run the bundled `threat-model-generation` skill first.
|
|
33
|
+
|
|
34
|
+
2. Resolve the changed-file scope.
|
|
35
|
+
|
|
36
|
+
3. Read the full contents of the changed source files, not just the patch.
|
|
37
|
+
|
|
38
|
+
4. Focus on STRIDE-oriented issues in changed code:
|
|
39
|
+
- Spoofing: auth/session/token mistakes
|
|
40
|
+
- Tampering: SQLi, XSS, path traversal, command injection, mass assignment
|
|
41
|
+
- Repudiation: security-sensitive actions with no auditability
|
|
42
|
+
- Information Disclosure: IDOR, secret exposure, verbose errors
|
|
43
|
+
- DoS: unbounded input, missing limits, expensive regex/queries
|
|
44
|
+
- Elevation of Privilege: missing authorization, role bypass, privilege escalation
|
|
45
|
+
|
|
46
|
+
5. Reuse Bug Hunter-native security conventions:
|
|
47
|
+
- findings should be compatible with `.bug-hunter/findings.json`
|
|
48
|
+
- use STRIDE + CWE labels
|
|
49
|
+
- include confidence scores
|
|
50
|
+
|
|
51
|
+
6. If the user wants only a focused security diff review, stop after the findings report.
|
|
52
|
+
If the user wants deeper validation, hand off to the bundled `vulnerability-validation` skill.
|
|
53
|
+
|
|
54
|
+
## Output
|
|
55
|
+
|
|
56
|
+
Preferred outputs:
|
|
57
|
+
- `.bug-hunter/findings.json` when integrating with the main Bug Hunter pipeline
|
|
58
|
+
- `.bug-hunter/report.md` as a rendered companion if needed
|
|
59
|
+
|
|
60
|
+
## Notes
|
|
61
|
+
|
|
62
|
+
- This skill is intentionally diff-scoped; it does not replace full-repository audits.
|
|
63
|
+
- Use it as the lightweight security fast-path before invoking the broader `security-review` flow.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-review
|
|
3
|
+
description: Run a focused STRIDE-based security review using Bug Hunter-native artifacts. Use whenever the user asks for a full security audit, repository security review, weekly security scan, PR security review with deeper validation, or wants dependency CVEs and threat-model context combined into one workflow.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Security Review
|
|
7
|
+
|
|
8
|
+
This is a bundled local Bug Hunter companion skill. It packages a security-focused review workflow without introducing any external marketplace dependency.
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
Use this skill for deeper security audits than a simple bug hunt, especially when the user wants:
|
|
13
|
+
- a full security review
|
|
14
|
+
- PR security validation
|
|
15
|
+
- weekly security scanning
|
|
16
|
+
- dependency reachability + code review together
|
|
17
|
+
- threat-model-driven analysis
|
|
18
|
+
|
|
19
|
+
## Workflow
|
|
20
|
+
|
|
21
|
+
1. Ensure `.bug-hunter/threat-model.md` exists.
|
|
22
|
+
- If missing, invoke the bundled `threat-model-generation` skill.
|
|
23
|
+
|
|
24
|
+
2. Determine the scan mode from the request:
|
|
25
|
+
- PR → diff-scoped review via `commit-security-scan`
|
|
26
|
+
- staged → staged-only security review
|
|
27
|
+
- weekly → recent commit range on the default branch
|
|
28
|
+
- full → full repository security audit
|
|
29
|
+
|
|
30
|
+
3. If dependency scanning is relevant, run:
|
|
31
|
+
- `node scripts/dep-scan.cjs --target <path> --output .bug-hunter/dep-findings.json`
|
|
32
|
+
|
|
33
|
+
4. Scan code for STRIDE threats using Bug Hunter-native conventions.
|
|
34
|
+
Reuse:
|
|
35
|
+
- `.bug-hunter/triage.json`
|
|
36
|
+
- `.bug-hunter/threat-model.md`
|
|
37
|
+
- `.bug-hunter/security-config.json`
|
|
38
|
+
- `.bug-hunter/dep-findings.json`
|
|
39
|
+
|
|
40
|
+
5. Validate severe findings using the bundled `vulnerability-validation` skill.
|
|
41
|
+
|
|
42
|
+
6. Produce structured outputs compatible with the Bug Hunter pipeline.
|
|
43
|
+
|
|
44
|
+
## Outputs
|
|
45
|
+
|
|
46
|
+
Primary artifacts should stay inside `.bug-hunter/`:
|
|
47
|
+
- `.bug-hunter/findings.json`
|
|
48
|
+
- `.bug-hunter/referee.json`
|
|
49
|
+
- `.bug-hunter/report.md`
|
|
50
|
+
- `.bug-hunter/dep-findings.json` when dependency review is enabled
|
|
51
|
+
- `.bug-hunter/fix-strategy.json` if the user wants remediation planning
|
|
52
|
+
|
|
53
|
+
## Important constraints
|
|
54
|
+
|
|
55
|
+
- Keep all paths Bug Hunter-native; do not emit `.factory/*` artifacts.
|
|
56
|
+
- Prefer validated, exploitability-aware findings over raw volume.
|
|
57
|
+
- For patching requests, hand findings back to the normal Bug Hunter fix pipeline rather than inventing a second patch system.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: threat-model-generation
|
|
3
|
+
description: Generate or refresh a STRIDE-based threat model for the current repository using Bug Hunter-native artifacts. Use whenever the repository has no threat model yet, the architecture changed materially, a security review needs fresh trust-boundary context, or the user explicitly asks for a threat model.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Threat Model Generation
|
|
7
|
+
|
|
8
|
+
This is a bundled local Bug Hunter companion skill. It generates portable threat-model artifacts under `.bug-hunter/`.
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
Create the security context that the other security skills depend on:
|
|
13
|
+
- trust boundaries
|
|
14
|
+
- major components
|
|
15
|
+
- STRIDE threats
|
|
16
|
+
- vulnerability pattern library
|
|
17
|
+
- severity/config defaults
|
|
18
|
+
|
|
19
|
+
## Required outputs
|
|
20
|
+
|
|
21
|
+
Write:
|
|
22
|
+
- `.bug-hunter/threat-model.md`
|
|
23
|
+
- `.bug-hunter/security-config.json`
|
|
24
|
+
|
|
25
|
+
## Workflow
|
|
26
|
+
|
|
27
|
+
1. Read `.bug-hunter/triage.json` if available for file structure and domain hints.
|
|
28
|
+
2. Inspect the repository to identify:
|
|
29
|
+
- languages and frameworks
|
|
30
|
+
- public/authenticated/internal entry points
|
|
31
|
+
- data stores and external integrations
|
|
32
|
+
- sensitive assets and trust boundaries
|
|
33
|
+
3. Generate a concise STRIDE threat model.
|
|
34
|
+
4. Generate a matching security config with thresholds and tech-stack metadata.
|
|
35
|
+
|
|
36
|
+
## Existing implementation hooks
|
|
37
|
+
|
|
38
|
+
Bug Hunter already has a native prompt for this capability:
|
|
39
|
+
- `prompts/threat-model.md`
|
|
40
|
+
|
|
41
|
+
Prefer reusing that prompt structure and artifact conventions rather than inventing a second format.
|
|
42
|
+
|
|
43
|
+
## Output rules
|
|
44
|
+
|
|
45
|
+
- Keep the threat model short enough for downstream agents to consume.
|
|
46
|
+
- Be specific about trust boundaries and vulnerable code patterns.
|
|
47
|
+
- Keep all artifacts under `.bug-hunter/`, never `.factory/`.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vulnerability-validation
|
|
3
|
+
description: Validate security findings for exploitability, reachability, and real-world impact using Bug Hunter-native findings artifacts. Use after security scans, before patch generation, or whenever the user wants confirmation that a suspected vulnerability is actually exploitable.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Vulnerability Validation
|
|
7
|
+
|
|
8
|
+
This is a bundled local Bug Hunter companion skill. It strengthens the security-specific parts of the Skeptic/Referee process.
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
Take suspected or confirmed security findings and answer:
|
|
13
|
+
- Is the vulnerable path reachable?
|
|
14
|
+
- Can an attacker control the input?
|
|
15
|
+
- Are there existing mitigations?
|
|
16
|
+
- How exploitable is it really?
|
|
17
|
+
- What is the CVSS / PoC / impact level?
|
|
18
|
+
|
|
19
|
+
## Inputs
|
|
20
|
+
|
|
21
|
+
Prefer Bug Hunter-native artifacts:
|
|
22
|
+
- `.bug-hunter/findings.json`
|
|
23
|
+
- `.bug-hunter/threat-model.md`
|
|
24
|
+
- `.bug-hunter/security-config.json`
|
|
25
|
+
- `.bug-hunter/dep-findings.json` when dependency issues are involved
|
|
26
|
+
|
|
27
|
+
## Workflow
|
|
28
|
+
|
|
29
|
+
1. Read the findings and isolate the security ones.
|
|
30
|
+
2. Trace reachability:
|
|
31
|
+
- EXTERNAL
|
|
32
|
+
- AUTHENTICATED
|
|
33
|
+
- INTERNAL
|
|
34
|
+
- UNREACHABLE
|
|
35
|
+
3. Trace exploitability:
|
|
36
|
+
- EASY
|
|
37
|
+
- MEDIUM
|
|
38
|
+
- HARD
|
|
39
|
+
- NOT_EXPLOITABLE
|
|
40
|
+
4. Check for mitigations already present in code, framework behavior, or deployment assumptions.
|
|
41
|
+
5. For confirmed HIGH/CRITICAL security bugs, generate:
|
|
42
|
+
- exploitation path
|
|
43
|
+
- benign proof of concept
|
|
44
|
+
- CVSS vector + score
|
|
45
|
+
6. Feed the result back into Bug Hunter-native verdicting.
|
|
46
|
+
|
|
47
|
+
## Outputs
|
|
48
|
+
|
|
49
|
+
When used as a companion to the main pipeline, keep outputs compatible with:
|
|
50
|
+
- `.bug-hunter/referee.json`
|
|
51
|
+
- `.bug-hunter/report.md`
|
|
52
|
+
|
|
53
|
+
If a separate validation artifact is helpful for the run, place it under `.bug-hunter/validated-findings.json`.
|
|
54
|
+
|
|
55
|
+
## Important constraints
|
|
56
|
+
|
|
57
|
+
- This skill validates findings; it does not replace the normal fix pipeline.
|
|
58
|
+
- Keep outputs portable and self-contained under `.bug-hunter/`.
|
|
59
|
+
- Prefer explicit reasoning for false positives so the user can trust dismissals.
|
|
@@ -76,6 +76,8 @@ If worktree rules are provided above (non-empty), these apply:
|
|
|
76
76
|
|
|
77
77
|
**Write your complete output to:** `{OUTPUT_FILE_PATH}`
|
|
78
78
|
|
|
79
|
+
**Artifact name for validation:** `{OUTPUT_ARTIFACT}`
|
|
80
|
+
|
|
79
81
|
Follow the output format specified in your system prompt EXACTLY.
|
|
80
82
|
The orchestrator will read this file to pass your results to the next pipeline phase.
|
|
81
83
|
|
|
@@ -84,12 +86,18 @@ If the file path directory does not exist, create it first:
|
|
|
84
86
|
mkdir -p "$(dirname '{OUTPUT_FILE_PATH}')"
|
|
85
87
|
```
|
|
86
88
|
|
|
89
|
+
After writing the canonical artifact, validate it before you stop:
|
|
90
|
+
```bash
|
|
91
|
+
node "{SKILL_DIR}/scripts/schema-validate.cjs" "{OUTPUT_ARTIFACT}" "{OUTPUT_FILE_PATH}"
|
|
92
|
+
```
|
|
93
|
+
|
|
87
94
|
## Completion
|
|
88
95
|
|
|
89
96
|
When you have finished your analysis:
|
|
90
97
|
1. Write your report to `{OUTPUT_FILE_PATH}`
|
|
91
|
-
2.
|
|
92
|
-
3.
|
|
98
|
+
2. Validate the artifact with `schema-validate.cjs`
|
|
99
|
+
3. Output a brief summary to stdout (one paragraph)
|
|
100
|
+
4. Stop. Do not continue to other phases.
|
|
93
101
|
|
|
94
102
|
---
|
|
95
103
|
|
|
@@ -106,4 +114,5 @@ When you have finished your analysis:
|
|
|
106
114
|
| `{RISK_MAP}` | Recon output risk classification | From `.bug-hunter/recon.md` |
|
|
107
115
|
| `{TECH_STACK}` | Framework, auth, DB, key dependencies | "Express + JWT + Prisma + Redis" |
|
|
108
116
|
| `{PHASE_SPECIFIC_CONTEXT}` | Extra context for this phase | For Skeptic: the Hunter findings. For Referee: findings + Skeptic challenges. |
|
|
109
|
-
| `{OUTPUT_FILE_PATH}` | Where to write the
|
|
117
|
+
| `{OUTPUT_FILE_PATH}` | Where to write the canonical artifact | `.bug-hunter/findings.json` |
|
|
118
|
+
| `{OUTPUT_ARTIFACT}` | Artifact name passed to `schema-validate.cjs` | `findings`, `skeptic`, `referee`, `fix-report` |
|