@vigolium/piolium 0.0.1

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 (271) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +117 -0
  3. package/agents/access-auditor.md +300 -0
  4. package/agents/assumption-breaker.md +154 -0
  5. package/agents/attack-designer.md +116 -0
  6. package/agents/code-scanner.md +139 -0
  7. package/agents/concurrency-auditor.md +238 -0
  8. package/agents/confirm-writer.md +257 -0
  9. package/agents/context-reviewer.md +274 -0
  10. package/agents/cross-verifier.md +165 -0
  11. package/agents/cve-scout.md +381 -0
  12. package/agents/env-builder.md +282 -0
  13. package/agents/env-profiler.md +205 -0
  14. package/agents/evidence-collector.md +140 -0
  15. package/agents/finding-grader.md +142 -0
  16. package/agents/finding-writer.md +148 -0
  17. package/agents/flow-tracer.md +106 -0
  18. package/agents/goal-backtracer.md +146 -0
  19. package/agents/history-miner.md +467 -0
  20. package/agents/independent-verifier.md +118 -0
  21. package/agents/intent-mapper.md +183 -0
  22. package/agents/longshot-collector.md +128 -0
  23. package/agents/longshot-prober.md +126 -0
  24. package/agents/patch-auditor.md +73 -0
  25. package/agents/poc-author.md +124 -0
  26. package/agents/poc-runner.md +194 -0
  27. package/agents/probe-lead.md +269 -0
  28. package/agents/red-challenger.md +101 -0
  29. package/agents/report-composer.md +208 -0
  30. package/agents/review-adjudicator.md +216 -0
  31. package/agents/spec-auditor.md +155 -0
  32. package/agents/taint-tracer.md +265 -0
  33. package/agents/test-locator.md +209 -0
  34. package/agents/threat-modeler.md +132 -0
  35. package/agents/variant-scanner.md +108 -0
  36. package/agents/variant-spotter.md +110 -0
  37. package/bin/piolium.mjs +376 -0
  38. package/extensions/piolium/_vendor/yaml.bundle.d.mts +6 -0
  39. package/extensions/piolium/_vendor/yaml.bundle.mjs +139 -0
  40. package/extensions/piolium/agent-runner.ts +322 -0
  41. package/extensions/piolium/agents.ts +266 -0
  42. package/extensions/piolium/audit-state.ts +522 -0
  43. package/extensions/piolium/bundled-resources.ts +97 -0
  44. package/extensions/piolium/candidate-scan.ts +966 -0
  45. package/extensions/piolium/command-target.ts +177 -0
  46. package/extensions/piolium/console-stream.ts +57 -0
  47. package/extensions/piolium/export-results.ts +380 -0
  48. package/extensions/piolium/findings.ts +448 -0
  49. package/extensions/piolium/heartbeat.ts +182 -0
  50. package/extensions/piolium/help.ts +234 -0
  51. package/extensions/piolium/index.ts +1865 -0
  52. package/extensions/piolium/longshot.ts +530 -0
  53. package/extensions/piolium/matcher-suggestions.ts +196 -0
  54. package/extensions/piolium/matcher-utils.ts +83 -0
  55. package/extensions/piolium/modes/balanced.ts +750 -0
  56. package/extensions/piolium/modes/confirm-bootstrap.ts +186 -0
  57. package/extensions/piolium/modes/confirm.ts +697 -0
  58. package/extensions/piolium/modes/deep.ts +917 -0
  59. package/extensions/piolium/modes/diff.ts +177 -0
  60. package/extensions/piolium/modes/lite.ts +540 -0
  61. package/extensions/piolium/modes/longshot.ts +595 -0
  62. package/extensions/piolium/modes/merge.ts +204 -0
  63. package/extensions/piolium/modes/phase-runner.ts +267 -0
  64. package/extensions/piolium/modes/reinvest.ts +546 -0
  65. package/extensions/piolium/modes/revisit.ts +279 -0
  66. package/extensions/piolium/modes.ts +48 -0
  67. package/extensions/piolium/phase-labels.ts +123 -0
  68. package/extensions/piolium/phase-status-strip.ts +92 -0
  69. package/extensions/piolium/prompt-prefix-editor.ts +39 -0
  70. package/extensions/piolium/providers/anthropic-vertex.ts +836 -0
  71. package/extensions/piolium/recon.ts +409 -0
  72. package/extensions/piolium/result-stats.ts +105 -0
  73. package/extensions/piolium/retry.ts +120 -0
  74. package/extensions/piolium/scheduler.ts +212 -0
  75. package/extensions/piolium/secrets.ts +368 -0
  76. package/extensions/piolium/tools/web-tools.ts +148 -0
  77. package/package.json +77 -0
  78. package/skills/agentic-actions-auditor/SKILL.md +327 -0
  79. package/skills/agentic-actions-auditor/references/action-profiles.md +186 -0
  80. package/skills/agentic-actions-auditor/references/cross-file-resolution.md +209 -0
  81. package/skills/agentic-actions-auditor/references/foundations.md +94 -0
  82. package/skills/agentic-actions-auditor/references/vector-a-env-var-intermediary.md +77 -0
  83. package/skills/agentic-actions-auditor/references/vector-b-direct-expression-injection.md +83 -0
  84. package/skills/agentic-actions-auditor/references/vector-c-cli-data-fetch.md +83 -0
  85. package/skills/agentic-actions-auditor/references/vector-d-pr-target-checkout.md +88 -0
  86. package/skills/agentic-actions-auditor/references/vector-e-error-log-injection.md +88 -0
  87. package/skills/agentic-actions-auditor/references/vector-f-subshell-expansion.md +82 -0
  88. package/skills/agentic-actions-auditor/references/vector-g-eval-of-ai-output.md +91 -0
  89. package/skills/agentic-actions-auditor/references/vector-h-dangerous-sandbox-configs.md +102 -0
  90. package/skills/agentic-actions-auditor/references/vector-i-wildcard-allowlists.md +88 -0
  91. package/skills/audit/SKILL.md +562 -0
  92. package/skills/audit/assets/icon.svg +7 -0
  93. package/skills/audit/hooks/scripts/validate_phase_output.py +550 -0
  94. package/skills/audit/references/adversarial-review.md +148 -0
  95. package/skills/audit/references/architecture-aware-sast.md +306 -0
  96. package/skills/audit/references/audit-workflow.md +737 -0
  97. package/skills/audit/references/chamber-protocol.md +384 -0
  98. package/skills/audit/references/creative-attack-modes.md +221 -0
  99. package/skills/audit/references/deep-analysis.md +273 -0
  100. package/skills/audit/references/domain-attack-playbooks.md +1129 -0
  101. package/skills/audit/references/knowledge-base-template.md +513 -0
  102. package/skills/audit/references/real-env-validation.md +191 -0
  103. package/skills/audit/references/report-templates.md +417 -0
  104. package/skills/audit/references/triage-and-prereqs.md +134 -0
  105. package/skills/audit/scripts/consolidate_drafts.py +554 -0
  106. package/skills/audit/scripts/partition_findings.py +152 -0
  107. package/skills/audit/scripts/rg-hotspots.sh +121 -0
  108. package/skills/audit/scripts/stamp_file_state.py +349 -0
  109. package/skills/code-reviewer/SKILL.md +65 -0
  110. package/skills/codeql/SKILL.md +281 -0
  111. package/skills/codeql/references/build-fixes.md +90 -0
  112. package/skills/codeql/references/diagnostic-query-templates.md +339 -0
  113. package/skills/codeql/references/extension-yaml-format.md +209 -0
  114. package/skills/codeql/references/important-only-suite.md +153 -0
  115. package/skills/codeql/references/language-details.md +207 -0
  116. package/skills/codeql/references/macos-arm64e-workaround.md +179 -0
  117. package/skills/codeql/references/performance-tuning.md +111 -0
  118. package/skills/codeql/references/quality-assessment.md +172 -0
  119. package/skills/codeql/references/ruleset-catalog.md +63 -0
  120. package/skills/codeql/references/run-all-suite.md +92 -0
  121. package/skills/codeql/references/sarif-processing.md +79 -0
  122. package/skills/codeql/references/threat-models.md +51 -0
  123. package/skills/codeql/workflows/build-database.md +280 -0
  124. package/skills/codeql/workflows/create-data-extensions.md +261 -0
  125. package/skills/codeql/workflows/run-analysis.md +301 -0
  126. package/skills/differential-review/SKILL.md +220 -0
  127. package/skills/differential-review/adversarial.md +203 -0
  128. package/skills/differential-review/methodology.md +234 -0
  129. package/skills/differential-review/patterns.md +300 -0
  130. package/skills/differential-review/reporting.md +369 -0
  131. package/skills/fp-check/SKILL.md +125 -0
  132. package/skills/fp-check/references/bug-class-verification.md +114 -0
  133. package/skills/fp-check/references/deep-verification.md +143 -0
  134. package/skills/fp-check/references/evidence-templates.md +91 -0
  135. package/skills/fp-check/references/false-positive-patterns.md +115 -0
  136. package/skills/fp-check/references/gate-reviews.md +27 -0
  137. package/skills/fp-check/references/standard-verification.md +78 -0
  138. package/skills/insecure-defaults/SKILL.md +117 -0
  139. package/skills/insecure-defaults/references/examples.md +409 -0
  140. package/skills/last30days/SKILL.md +444 -0
  141. package/skills/sarif-parsing/SKILL.md +483 -0
  142. package/skills/sarif-parsing/resources/jq-queries.md +162 -0
  143. package/skills/sarif-parsing/resources/sarif_helpers.py +331 -0
  144. package/skills/security-threat-model/LICENSE.txt +201 -0
  145. package/skills/security-threat-model/SKILL.md +81 -0
  146. package/skills/security-threat-model/agents/openai.yaml +4 -0
  147. package/skills/security-threat-model/references/prompt-template.md +255 -0
  148. package/skills/security-threat-model/references/security-controls-and-assets.md +32 -0
  149. package/skills/semgrep/SKILL.md +212 -0
  150. package/skills/semgrep/references/rulesets.md +162 -0
  151. package/skills/semgrep/references/scan-modes.md +110 -0
  152. package/skills/semgrep/references/scanner-task-prompt.md +140 -0
  153. package/skills/semgrep/scripts/merge_sarif.py +203 -0
  154. package/skills/semgrep/workflows/scan-workflow.md +311 -0
  155. package/skills/semgrep-rule-creator/SKILL.md +168 -0
  156. package/skills/semgrep-rule-creator/references/quick-reference.md +202 -0
  157. package/skills/semgrep-rule-creator/references/workflow.md +240 -0
  158. package/skills/semgrep-rule-variant-creator/SKILL.md +205 -0
  159. package/skills/semgrep-rule-variant-creator/references/applicability-analysis.md +250 -0
  160. package/skills/semgrep-rule-variant-creator/references/language-syntax-guide.md +324 -0
  161. package/skills/semgrep-rule-variant-creator/references/workflow.md +518 -0
  162. package/skills/sharp-edges/SKILL.md +292 -0
  163. package/skills/sharp-edges/references/auth-patterns.md +252 -0
  164. package/skills/sharp-edges/references/case-studies.md +274 -0
  165. package/skills/sharp-edges/references/config-patterns.md +333 -0
  166. package/skills/sharp-edges/references/crypto-apis.md +190 -0
  167. package/skills/sharp-edges/references/lang-c.md +205 -0
  168. package/skills/sharp-edges/references/lang-csharp.md +285 -0
  169. package/skills/sharp-edges/references/lang-go.md +270 -0
  170. package/skills/sharp-edges/references/lang-java.md +263 -0
  171. package/skills/sharp-edges/references/lang-javascript.md +269 -0
  172. package/skills/sharp-edges/references/lang-kotlin.md +265 -0
  173. package/skills/sharp-edges/references/lang-php.md +245 -0
  174. package/skills/sharp-edges/references/lang-python.md +274 -0
  175. package/skills/sharp-edges/references/lang-ruby.md +273 -0
  176. package/skills/sharp-edges/references/lang-rust.md +272 -0
  177. package/skills/sharp-edges/references/lang-swift.md +287 -0
  178. package/skills/sharp-edges/references/language-specific.md +588 -0
  179. package/skills/spec-to-code-compliance/SKILL.md +357 -0
  180. package/skills/spec-to-code-compliance/resources/COMPLETENESS_CHECKLIST.md +69 -0
  181. package/skills/spec-to-code-compliance/resources/IR_EXAMPLES.md +417 -0
  182. package/skills/spec-to-code-compliance/resources/OUTPUT_REQUIREMENTS.md +105 -0
  183. package/skills/supply-chain-risk-auditor/SKILL.md +67 -0
  184. package/skills/supply-chain-risk-auditor/resources/results-template.md +41 -0
  185. package/skills/variant-analysis/METHODOLOGY.md +327 -0
  186. package/skills/variant-analysis/SKILL.md +142 -0
  187. package/skills/variant-analysis/resources/codeql/cpp.ql +119 -0
  188. package/skills/variant-analysis/resources/codeql/go.ql +69 -0
  189. package/skills/variant-analysis/resources/codeql/java.ql +71 -0
  190. package/skills/variant-analysis/resources/codeql/javascript.ql +63 -0
  191. package/skills/variant-analysis/resources/codeql/python.ql +80 -0
  192. package/skills/variant-analysis/resources/semgrep/cpp.yaml +98 -0
  193. package/skills/variant-analysis/resources/semgrep/go.yaml +63 -0
  194. package/skills/variant-analysis/resources/semgrep/java.yaml +61 -0
  195. package/skills/variant-analysis/resources/semgrep/javascript.yaml +60 -0
  196. package/skills/variant-analysis/resources/semgrep/python.yaml +72 -0
  197. package/skills/variant-analysis/resources/variant-report-template.md +75 -0
  198. package/skills/vuln-report/SKILL.md +137 -0
  199. package/skills/vuln-report/agents/openai.yaml +4 -0
  200. package/skills/vuln-report/references/report-template.md +135 -0
  201. package/skills/wooyun-legacy/SKILL.md +367 -0
  202. package/skills/wooyun-legacy/references/bank-penetration.md +222 -0
  203. package/skills/wooyun-legacy/references/checklists/command-execution-checklist.md +119 -0
  204. package/skills/wooyun-legacy/references/checklists/csrf-checklist.md +74 -0
  205. package/skills/wooyun-legacy/references/checklists/file-upload-checklist.md +108 -0
  206. package/skills/wooyun-legacy/references/checklists/info-disclosure-checklist.md +114 -0
  207. package/skills/wooyun-legacy/references/checklists/logic-flaws-checklist.md +95 -0
  208. package/skills/wooyun-legacy/references/checklists/misconfig-checklist.md +124 -0
  209. package/skills/wooyun-legacy/references/checklists/path-traversal-checklist.md +87 -0
  210. package/skills/wooyun-legacy/references/checklists/rce-checklist.md +93 -0
  211. package/skills/wooyun-legacy/references/checklists/sql-injection-checklist.md +97 -0
  212. package/skills/wooyun-legacy/references/checklists/ssrf-checklist.md +99 -0
  213. package/skills/wooyun-legacy/references/checklists/unauthorized-access-checklist.md +89 -0
  214. package/skills/wooyun-legacy/references/checklists/weak-password-checklist.md +115 -0
  215. package/skills/wooyun-legacy/references/checklists/xss-checklist.md +103 -0
  216. package/skills/wooyun-legacy/references/checklists/xxe-checklist.md +130 -0
  217. package/skills/wooyun-legacy/references/info-disclosure.md +975 -0
  218. package/skills/wooyun-legacy/references/logic-flaws.md +721 -0
  219. package/skills/wooyun-legacy/references/path-traversal.md +1191 -0
  220. package/skills/wooyun-legacy/references/telecom-penetration.md +156 -0
  221. package/skills/wooyun-legacy/references/unauthorized-access.md +980 -0
  222. package/skills/wooyun-legacy/references/xss.md +746 -0
  223. package/skills/zeroize-audit/SKILL.md +371 -0
  224. package/skills/zeroize-audit/configs/c.yaml +21 -0
  225. package/skills/zeroize-audit/configs/default.yaml +128 -0
  226. package/skills/zeroize-audit/configs/rust.yaml +83 -0
  227. package/skills/zeroize-audit/prompts/report_template.md +238 -0
  228. package/skills/zeroize-audit/prompts/system.md +163 -0
  229. package/skills/zeroize-audit/prompts/task.md +97 -0
  230. package/skills/zeroize-audit/references/compile-commands.md +231 -0
  231. package/skills/zeroize-audit/references/detection-strategy.md +191 -0
  232. package/skills/zeroize-audit/references/ir-analysis.md +252 -0
  233. package/skills/zeroize-audit/references/mcp-analysis.md +221 -0
  234. package/skills/zeroize-audit/references/poc-generation.md +470 -0
  235. package/skills/zeroize-audit/references/rust-zeroization-patterns.md +867 -0
  236. package/skills/zeroize-audit/schemas/input.json +83 -0
  237. package/skills/zeroize-audit/schemas/output.json +140 -0
  238. package/skills/zeroize-audit/tools/analyze_asm.sh +202 -0
  239. package/skills/zeroize-audit/tools/analyze_cfg.py +381 -0
  240. package/skills/zeroize-audit/tools/analyze_heap.sh +211 -0
  241. package/skills/zeroize-audit/tools/analyze_ir_semantic.py +429 -0
  242. package/skills/zeroize-audit/tools/diff_ir.sh +135 -0
  243. package/skills/zeroize-audit/tools/diff_rust_mir.sh +189 -0
  244. package/skills/zeroize-audit/tools/emit_asm.sh +67 -0
  245. package/skills/zeroize-audit/tools/emit_ir.sh +77 -0
  246. package/skills/zeroize-audit/tools/emit_rust_asm.sh +178 -0
  247. package/skills/zeroize-audit/tools/emit_rust_ir.sh +150 -0
  248. package/skills/zeroize-audit/tools/emit_rust_mir.sh +158 -0
  249. package/skills/zeroize-audit/tools/extract_compile_flags.py +284 -0
  250. package/skills/zeroize-audit/tools/generate_poc.py +1329 -0
  251. package/skills/zeroize-audit/tools/mcp/apply_confidence_gates.py +113 -0
  252. package/skills/zeroize-audit/tools/mcp/check_mcp.sh +68 -0
  253. package/skills/zeroize-audit/tools/mcp/normalize_mcp_evidence.py +125 -0
  254. package/skills/zeroize-audit/tools/scripts/check_llvm_patterns.py +481 -0
  255. package/skills/zeroize-audit/tools/scripts/check_mir_patterns.py +554 -0
  256. package/skills/zeroize-audit/tools/scripts/check_rust_asm.py +424 -0
  257. package/skills/zeroize-audit/tools/scripts/check_rust_asm_aarch64.py +300 -0
  258. package/skills/zeroize-audit/tools/scripts/check_rust_asm_x86.py +283 -0
  259. package/skills/zeroize-audit/tools/scripts/find_dangerous_apis.py +375 -0
  260. package/skills/zeroize-audit/tools/scripts/semantic_audit.py +923 -0
  261. package/skills/zeroize-audit/tools/track_dataflow.sh +196 -0
  262. package/skills/zeroize-audit/tools/validate_rust_toolchain.sh +298 -0
  263. package/skills/zeroize-audit/workflows/phase-0-preflight.md +150 -0
  264. package/skills/zeroize-audit/workflows/phase-1-source-analysis.md +144 -0
  265. package/skills/zeroize-audit/workflows/phase-2-compiler-analysis.md +139 -0
  266. package/skills/zeroize-audit/workflows/phase-3-interim-report.md +46 -0
  267. package/skills/zeroize-audit/workflows/phase-4-poc-generation.md +46 -0
  268. package/skills/zeroize-audit/workflows/phase-5-poc-validation.md +136 -0
  269. package/skills/zeroize-audit/workflows/phase-6-final-report.md +44 -0
  270. package/skills/zeroize-audit/workflows/phase-7-test-generation.md +42 -0
  271. package/themes/piolium-srcery.json +94 -0
@@ -0,0 +1,196 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, extname, join } from "node:path";
3
+ import { splitFrontmatter } from "./agents.ts";
4
+ import { listFindingDirs } from "./findings.ts";
5
+ import {
6
+ collectCandidateWords,
7
+ collectExistingMatcherSlugs,
8
+ mergeMatcherConfig,
9
+ } from "./matcher-utils.ts";
10
+
11
+ export interface MatcherSuggestion {
12
+ slug: string;
13
+ description: string;
14
+ noise: "normal";
15
+ include: string[];
16
+ pathIncludes?: string[];
17
+ regex: string;
18
+ flags: "gi";
19
+ label: string;
20
+ originFinding: string;
21
+ }
22
+
23
+ export interface MatcherLearnResult {
24
+ suggestions: MatcherSuggestion[];
25
+ suggestionsPath: string;
26
+ appliedPath?: string;
27
+ lines: string[];
28
+ }
29
+
30
+ export function runMatcherLearn(
31
+ cwd: string,
32
+ options: { apply?: boolean } = {},
33
+ ): MatcherLearnResult {
34
+ const suggestions = buildMatcherSuggestions(cwd);
35
+ const suggestionsPath = join(cwd, "piolium", "attack-surface", "matcher-suggestions.json");
36
+ mkdirSync(dirname(suggestionsPath), { recursive: true });
37
+ writeFileSync(
38
+ suggestionsPath,
39
+ `${JSON.stringify({ generated_at: new Date().toISOString(), matchers: suggestions }, null, 2)}\n`,
40
+ );
41
+
42
+ let appliedPath: string | undefined;
43
+ if (options.apply && suggestions.length > 0) {
44
+ appliedPath = join(cwd, "piolium", "matchers.json");
45
+ const merged = mergeMatcherConfig(appliedPath, suggestions);
46
+ mkdirSync(dirname(appliedPath), { recursive: true });
47
+ writeFileSync(appliedPath, `${JSON.stringify(merged, null, 2)}\n`);
48
+ }
49
+
50
+ return {
51
+ suggestions,
52
+ suggestionsPath,
53
+ ...(appliedPath ? { appliedPath } : {}),
54
+ lines: [
55
+ `Suggestions: ${suggestions.length}`,
56
+ `Output: ${suggestionsPath}`,
57
+ ...(appliedPath ? [`Applied to: ${appliedPath}`] : []),
58
+ "",
59
+ ...suggestions
60
+ .slice(0, 30)
61
+ .map((suggestion) => `- ${suggestion.slug}: ${suggestion.description}`),
62
+ ...(suggestions.length > 30 ? [`... ${suggestions.length - 30} more`] : []),
63
+ ],
64
+ };
65
+ }
66
+
67
+ function buildMatcherSuggestions(cwd: string): MatcherSuggestion[] {
68
+ const existingSlugs = collectExistingMatcherSlugs([
69
+ join(cwd, "piolium", "matchers.json"),
70
+ join(cwd, "piolium", "custom-matchers.json"),
71
+ join(cwd, ".piolium-matchers.json"),
72
+ ]);
73
+ const suggestions: MatcherSuggestion[] = [];
74
+ for (const finding of listFindingDirs(cwd)) {
75
+ const draftPath = join(finding.path, "draft.md");
76
+ if (!existsSync(draftPath)) continue;
77
+ const raw = readFileSync(draftPath, "utf8");
78
+ const { frontmatter, body } = splitFrontmatter(raw);
79
+ const title =
80
+ stringValue(frontmatter.title) ??
81
+ stringValue(frontmatter.name) ??
82
+ body.match(/^#\s+(.+)$/m)?.[1]?.trim() ??
83
+ finding.slug;
84
+ const words = collectCandidateWords(
85
+ `${finding.slug} ${title} ${stringValue(frontmatter.class) ?? ""}`,
86
+ );
87
+ if (words.length === 0) continue;
88
+ const files = collectFiles(frontmatter, body);
89
+ const include = collectExtensions(files);
90
+ const pathIncludes = collectPathHints(files);
91
+ const slug = uniqueSlug(`learned-${finding.slug}`, existingSlugs);
92
+ existingSlugs.add(slug);
93
+ suggestions.push({
94
+ slug,
95
+ description: `Suggested matcher from finding ${finding.id}-${finding.slug}.`,
96
+ noise: "normal",
97
+ include:
98
+ include.length > 0
99
+ ? include
100
+ : [".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rb", ".java", ".php"],
101
+ ...(pathIncludes.length > 0 ? { pathIncludes } : {}),
102
+ regex: `\\b(${words.map(escapeRegex).join("|")})\\b`,
103
+ flags: "gi",
104
+ label: "learned finding keyword",
105
+ originFinding: `${finding.id}-${finding.slug}`,
106
+ });
107
+ }
108
+ return suggestions;
109
+ }
110
+
111
+ function collectFiles(frontmatter: Record<string, unknown>, body: string): string[] {
112
+ const files = new Set<string>();
113
+ for (const value of Object.values(frontmatter)) {
114
+ if (typeof value === "string") addFile(files, value);
115
+ else if (Array.isArray(value)) {
116
+ for (const item of value) {
117
+ if (typeof item === "string") addFile(files, item);
118
+ }
119
+ }
120
+ }
121
+ for (const match of body.matchAll(
122
+ /([A-Za-z0-9_./-]+\.(?:ts|tsx|js|jsx|py|go|rs|rb|java|kt|swift|c|h|cpp|cc|hpp|cs|php|scala|clj|sh|sql|lua|tf|ya?ml))(?:[:#]\d+)?/g,
123
+ )) {
124
+ if (match[1]) addFile(files, match[1]);
125
+ }
126
+ return [...files].sort();
127
+ }
128
+
129
+ function addFile(files: Set<string>, value: string): void {
130
+ const cleaned = value.replace(/[:#]\d+(?::\d+)?$/, "").trim();
131
+ if (!cleaned || cleaned.startsWith("http")) return;
132
+ if (
133
+ /\.(ts|tsx|js|jsx|py|go|rs|rb|java|kt|swift|c|h|cpp|cc|hpp|cs|php|scala|clj|sh|sql|lua|tf|ya?ml)$/i.test(
134
+ cleaned,
135
+ )
136
+ ) {
137
+ files.add(cleaned);
138
+ }
139
+ }
140
+
141
+ function collectExtensions(files: string[]): string[] {
142
+ const exts = [...new Set(files.map((file) => extname(file).toLowerCase()).filter(Boolean))];
143
+ return exts.slice(0, 8);
144
+ }
145
+
146
+ function collectPathHints(files: string[]): string[] {
147
+ const hints = new Set<string>();
148
+ for (const file of files) {
149
+ for (const part of file.toLowerCase().split("/")) {
150
+ if (
151
+ [
152
+ "admin",
153
+ "auth",
154
+ "api",
155
+ "route",
156
+ "routes",
157
+ "handler",
158
+ "handlers",
159
+ "controller",
160
+ "upload",
161
+ "webhook",
162
+ "payment",
163
+ "billing",
164
+ "permission",
165
+ "policy",
166
+ ].includes(part)
167
+ ) {
168
+ hints.add(part);
169
+ }
170
+ }
171
+ }
172
+ return [...hints].slice(0, 8);
173
+ }
174
+
175
+ function uniqueSlug(base: string, seen: Set<string>): string {
176
+ const normalized = base
177
+ .toLowerCase()
178
+ .replace(/[^a-z0-9]+/g, "-")
179
+ .replace(/^-+|-+$/g, "")
180
+ .slice(0, 70);
181
+ let candidate = normalized || "learned-finding";
182
+ let index = 2;
183
+ while (seen.has(candidate)) {
184
+ candidate = `${normalized}-${index}`;
185
+ index++;
186
+ }
187
+ return candidate;
188
+ }
189
+
190
+ function stringValue(value: unknown): string | undefined {
191
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
192
+ }
193
+
194
+ function escapeRegex(value: string): string {
195
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
196
+ }
@@ -0,0 +1,83 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+
3
+ export interface JsonMatcher {
4
+ slug: string;
5
+ description?: string;
6
+ noise?: string;
7
+ include?: string[];
8
+ pathIncludes?: string[];
9
+ regex: string;
10
+ flags?: string;
11
+ label?: string;
12
+ originFinding?: string;
13
+ }
14
+
15
+ export interface MatcherConfig {
16
+ matchers: JsonMatcher[];
17
+ }
18
+
19
+ const STOP_WORDS = new Set([
20
+ "the",
21
+ "and",
22
+ "for",
23
+ "with",
24
+ "from",
25
+ "into",
26
+ "that",
27
+ "this",
28
+ "when",
29
+ "where",
30
+ "finding",
31
+ "issue",
32
+ "bug",
33
+ "vulnerability",
34
+ "allows",
35
+ "allow",
36
+ "using",
37
+ "missing",
38
+ "unsafe",
39
+ "weak",
40
+ ]);
41
+
42
+ export function collectCandidateWords(input: string): string[] {
43
+ const words = input
44
+ .toLowerCase()
45
+ .split(/[^a-z0-9]+/)
46
+ .map((word) => word.trim())
47
+ .filter((word) => word.length >= 4 && !STOP_WORDS.has(word));
48
+ return [...new Set(words)].slice(0, 8);
49
+ }
50
+
51
+ export function collectExistingMatcherSlugs(paths: string[]): Set<string> {
52
+ const slugs = new Set<string>();
53
+ for (const path of paths) {
54
+ if (!existsSync(path)) continue;
55
+ try {
56
+ const config = JSON.parse(readFileSync(path, "utf8")) as Partial<MatcherConfig>;
57
+ for (const matcher of config.matchers ?? []) {
58
+ if (typeof matcher.slug === "string") slugs.add(matcher.slug);
59
+ }
60
+ } catch {}
61
+ }
62
+ return slugs;
63
+ }
64
+
65
+ export function mergeMatcherConfig(path: string, suggestions: JsonMatcher[]): MatcherConfig {
66
+ let existing: MatcherConfig = { matchers: [] };
67
+ if (existsSync(path)) {
68
+ try {
69
+ const parsed = JSON.parse(readFileSync(path, "utf8")) as Partial<MatcherConfig>;
70
+ existing = { matchers: Array.isArray(parsed.matchers) ? parsed.matchers : [] };
71
+ } catch {
72
+ existing = { matchers: [] };
73
+ }
74
+ }
75
+ const seen = new Set(existing.matchers.map((matcher) => matcher.slug));
76
+ const merged = [...existing.matchers];
77
+ for (const suggestion of suggestions) {
78
+ if (seen.has(suggestion.slug)) continue;
79
+ seen.add(suggestion.slug);
80
+ merged.push(suggestion);
81
+ }
82
+ return { matchers: merged };
83
+ }