@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.
Files changed (78) hide show
  1. package/CHANGELOG.md +149 -83
  2. package/README.md +150 -15
  3. package/SKILL.md +94 -27
  4. package/agents/openai.yaml +4 -0
  5. package/bin/bug-hunter +9 -3
  6. package/docs/images/2026-03-12-fix-plan-rollout.png +0 -0
  7. package/docs/images/2026-03-12-hero-bug-hunter-overview.png +0 -0
  8. package/docs/images/2026-03-12-machine-readable-artifacts.png +0 -0
  9. package/docs/images/2026-03-12-pr-review-flow.png +0 -0
  10. package/docs/images/2026-03-12-security-pack.png +0 -0
  11. package/docs/images/adversarial-debate.png +0 -0
  12. package/docs/images/doc-verify-fix-plan.png +0 -0
  13. package/docs/images/hero.png +0 -0
  14. package/docs/images/pipeline-overview.png +0 -0
  15. package/docs/images/security-finding-card.png +0 -0
  16. package/docs/plans/2026-03-11-structured-output-migration-plan.md +288 -0
  17. package/docs/plans/2026-03-12-audit-bug-fixes-surgical-plan.md +193 -0
  18. package/docs/plans/2026-03-12-enterprise-security-pack-e2e-plan.md +59 -0
  19. package/docs/plans/2026-03-12-local-security-skills-integration-plan.md +39 -0
  20. package/docs/plans/2026-03-12-pr-review-strategic-fix-flow.md +78 -0
  21. package/evals/evals.json +366 -102
  22. package/modes/extended.md +2 -2
  23. package/modes/fix-loop.md +30 -30
  24. package/modes/fix-pipeline.md +32 -6
  25. package/modes/large-codebase.md +14 -15
  26. package/modes/local-sequential.md +44 -20
  27. package/modes/loop.md +56 -56
  28. package/modes/parallel.md +3 -3
  29. package/modes/scaled.md +2 -2
  30. package/modes/single-file.md +3 -3
  31. package/modes/small.md +11 -11
  32. package/package.json +11 -1
  33. package/prompts/fixer.md +37 -23
  34. package/prompts/hunter.md +39 -20
  35. package/prompts/referee.md +34 -20
  36. package/prompts/skeptic.md +25 -22
  37. package/schemas/coverage.schema.json +67 -0
  38. package/schemas/examples/findings.invalid.json +13 -0
  39. package/schemas/examples/findings.valid.json +17 -0
  40. package/schemas/findings.schema.json +76 -0
  41. package/schemas/fix-plan.schema.json +94 -0
  42. package/schemas/fix-report.schema.json +105 -0
  43. package/schemas/fix-strategy.schema.json +99 -0
  44. package/schemas/recon.schema.json +31 -0
  45. package/schemas/referee.schema.json +46 -0
  46. package/schemas/shared.schema.json +51 -0
  47. package/schemas/skeptic.schema.json +21 -0
  48. package/scripts/bug-hunter-state.cjs +35 -12
  49. package/scripts/code-index.cjs +11 -4
  50. package/scripts/fix-lock.cjs +95 -25
  51. package/scripts/payload-guard.cjs +24 -10
  52. package/scripts/pr-scope.cjs +181 -0
  53. package/scripts/prepublish-guard.cjs +82 -0
  54. package/scripts/render-report.cjs +346 -0
  55. package/scripts/run-bug-hunter.cjs +669 -33
  56. package/scripts/schema-runtime.cjs +273 -0
  57. package/scripts/schema-validate.cjs +40 -0
  58. package/scripts/tests/bug-hunter-state.test.cjs +68 -3
  59. package/scripts/tests/code-index.test.cjs +15 -0
  60. package/scripts/tests/fix-lock.test.cjs +60 -2
  61. package/scripts/tests/fixtures/flaky-worker.cjs +6 -1
  62. package/scripts/tests/fixtures/low-confidence-worker.cjs +8 -2
  63. package/scripts/tests/fixtures/success-worker.cjs +6 -1
  64. package/scripts/tests/payload-guard.test.cjs +154 -2
  65. package/scripts/tests/pr-scope.test.cjs +212 -0
  66. package/scripts/tests/render-report.test.cjs +180 -0
  67. package/scripts/tests/run-bug-hunter.test.cjs +686 -2
  68. package/scripts/tests/security-skills-integration.test.cjs +29 -0
  69. package/scripts/tests/skills-packaging.test.cjs +30 -0
  70. package/scripts/tests/worktree-harvest.test.cjs +67 -1
  71. package/scripts/worktree-harvest.cjs +62 -9
  72. package/skills/README.md +19 -0
  73. package/skills/commit-security-scan/SKILL.md +63 -0
  74. package/skills/security-review/SKILL.md +57 -0
  75. package/skills/threat-model-generation/SKILL.md +47 -0
  76. package/skills/vulnerability-validation/SKILL.md +59 -0
  77. package/templates/subagent-wrapper.md +12 -3
  78. 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 (!readJsonFile(harvestPath(absDir))) {
324
- try { harvestCore(absDir); } catch (_) { /* best-effort */ }
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: true,
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 (!readJsonFile(harvestPath(wtDir))) {
372
- try { harvestCore(wtDir); } catch (_) { /* best-effort */ }
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 hasUncommitted = statusOutput.ok && statusOutput.output.length > 0;
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) {
@@ -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. Output a brief summary to stdout (one paragraph)
92
- 3. Stop. Do not continue to other phases.
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 output | `.bug-hunter/findings.md` |
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` |