dravix-agent 0.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.
Files changed (208) hide show
  1. package/.claude/settings.example.json +30 -0
  2. package/ARCHITECTURE.md +410 -0
  3. package/LICENSE +21 -0
  4. package/README.md +153 -0
  5. package/ROADMAP.md +117 -0
  6. package/data/vulnkb.json +666 -0
  7. package/dist/bin/aegis.d.ts +3 -0
  8. package/dist/bin/aegis.d.ts.map +1 -0
  9. package/dist/bin/aegis.js +489 -0
  10. package/dist/bin/aegis.js.map +1 -0
  11. package/dist/cache.d.ts +9 -0
  12. package/dist/cache.d.ts.map +1 -0
  13. package/dist/cache.js +146 -0
  14. package/dist/cache.js.map +1 -0
  15. package/dist/engines/ai-sinks.d.ts +52 -0
  16. package/dist/engines/ai-sinks.d.ts.map +1 -0
  17. package/dist/engines/ai-sinks.js +204 -0
  18. package/dist/engines/ai-sinks.js.map +1 -0
  19. package/dist/engines/eslint.d.ts +9 -0
  20. package/dist/engines/eslint.d.ts.map +1 -0
  21. package/dist/engines/eslint.js +245 -0
  22. package/dist/engines/eslint.js.map +1 -0
  23. package/dist/engines/joern.d.ts +3 -0
  24. package/dist/engines/joern.d.ts.map +1 -0
  25. package/dist/engines/joern.js +98 -0
  26. package/dist/engines/joern.js.map +1 -0
  27. package/dist/engines/js-sinks.d.ts +70 -0
  28. package/dist/engines/js-sinks.d.ts.map +1 -0
  29. package/dist/engines/js-sinks.js +370 -0
  30. package/dist/engines/js-sinks.js.map +1 -0
  31. package/dist/engines/llm-critic.d.ts +130 -0
  32. package/dist/engines/llm-critic.d.ts.map +1 -0
  33. package/dist/engines/llm-critic.js +551 -0
  34. package/dist/engines/llm-critic.js.map +1 -0
  35. package/dist/engines/pragma.d.ts +20 -0
  36. package/dist/engines/pragma.d.ts.map +1 -0
  37. package/dist/engines/pragma.js +83 -0
  38. package/dist/engines/pragma.js.map +1 -0
  39. package/dist/engines/property-test.d.ts +3 -0
  40. package/dist/engines/property-test.d.ts.map +1 -0
  41. package/dist/engines/property-test.js +134 -0
  42. package/dist/engines/property-test.js.map +1 -0
  43. package/dist/engines/pyright.d.ts +10 -0
  44. package/dist/engines/pyright.d.ts.map +1 -0
  45. package/dist/engines/pyright.js +143 -0
  46. package/dist/engines/pyright.js.map +1 -0
  47. package/dist/engines/pysa.d.ts +3 -0
  48. package/dist/engines/pysa.d.ts.map +1 -0
  49. package/dist/engines/pysa.js +83 -0
  50. package/dist/engines/pysa.js.map +1 -0
  51. package/dist/engines/python-sinks.d.ts +82 -0
  52. package/dist/engines/python-sinks.d.ts.map +1 -0
  53. package/dist/engines/python-sinks.js +459 -0
  54. package/dist/engines/python-sinks.js.map +1 -0
  55. package/dist/engines/registry.d.ts +26 -0
  56. package/dist/engines/registry.d.ts.map +1 -0
  57. package/dist/engines/registry.js +70 -0
  58. package/dist/engines/registry.js.map +1 -0
  59. package/dist/engines/secret-scan.d.ts +22 -0
  60. package/dist/engines/secret-scan.d.ts.map +1 -0
  61. package/dist/engines/secret-scan.js +179 -0
  62. package/dist/engines/secret-scan.js.map +1 -0
  63. package/dist/engines/semgrep.d.ts +10 -0
  64. package/dist/engines/semgrep.d.ts.map +1 -0
  65. package/dist/engines/semgrep.js +200 -0
  66. package/dist/engines/semgrep.js.map +1 -0
  67. package/dist/engines/treesitter.d.ts +18 -0
  68. package/dist/engines/treesitter.d.ts.map +1 -0
  69. package/dist/engines/treesitter.js +135 -0
  70. package/dist/engines/treesitter.js.map +1 -0
  71. package/dist/engines/tsc.d.ts +10 -0
  72. package/dist/engines/tsc.d.ts.map +1 -0
  73. package/dist/engines/tsc.js +142 -0
  74. package/dist/engines/tsc.js.map +1 -0
  75. package/dist/engines/types.d.ts +47 -0
  76. package/dist/engines/types.d.ts.map +1 -0
  77. package/dist/engines/types.js +27 -0
  78. package/dist/engines/types.js.map +1 -0
  79. package/dist/findings.d.ts +121 -0
  80. package/dist/findings.d.ts.map +1 -0
  81. package/dist/findings.js +98 -0
  82. package/dist/findings.js.map +1 -0
  83. package/dist/hooks/claude-code.d.ts +3 -0
  84. package/dist/hooks/claude-code.d.ts.map +1 -0
  85. package/dist/hooks/claude-code.js +187 -0
  86. package/dist/hooks/claude-code.js.map +1 -0
  87. package/dist/index/context.d.ts +127 -0
  88. package/dist/index/context.d.ts.map +1 -0
  89. package/dist/index/context.js +267 -0
  90. package/dist/index/context.js.map +1 -0
  91. package/dist/index/embeddings.d.ts +68 -0
  92. package/dist/index/embeddings.d.ts.map +1 -0
  93. package/dist/index/embeddings.js +570 -0
  94. package/dist/index/embeddings.js.map +1 -0
  95. package/dist/index/graph_routing.d.ts +36 -0
  96. package/dist/index/graph_routing.d.ts.map +1 -0
  97. package/dist/index/graph_routing.js +170 -0
  98. package/dist/index/graph_routing.js.map +1 -0
  99. package/dist/index/joern.d.ts +76 -0
  100. package/dist/index/joern.d.ts.map +1 -0
  101. package/dist/index/joern.js +782 -0
  102. package/dist/index/joern.js.map +1 -0
  103. package/dist/index/property-test.d.ts +88 -0
  104. package/dist/index/property-test.d.ts.map +1 -0
  105. package/dist/index/property-test.js +466 -0
  106. package/dist/index/property-test.js.map +1 -0
  107. package/dist/index/proto/scip.proto +897 -0
  108. package/dist/index/pysa.d.ts +91 -0
  109. package/dist/index/pysa.d.ts.map +1 -0
  110. package/dist/index/pysa.js +617 -0
  111. package/dist/index/pysa.js.map +1 -0
  112. package/dist/index/scip.d.ts +76 -0
  113. package/dist/index/scip.d.ts.map +1 -0
  114. package/dist/index/scip.js +541 -0
  115. package/dist/index/scip.js.map +1 -0
  116. package/dist/index/vulrag.d.ts +86 -0
  117. package/dist/index/vulrag.d.ts.map +1 -0
  118. package/dist/index/vulrag.js +242 -0
  119. package/dist/index/vulrag.js.map +1 -0
  120. package/dist/index.d.ts +9 -0
  121. package/dist/index.d.ts.map +1 -0
  122. package/dist/index.js +8 -0
  123. package/dist/index.js.map +1 -0
  124. package/dist/install/claude-code.d.ts +31 -0
  125. package/dist/install/claude-code.d.ts.map +1 -0
  126. package/dist/install/claude-code.js +447 -0
  127. package/dist/install/claude-code.js.map +1 -0
  128. package/dist/lang.d.ts +5 -0
  129. package/dist/lang.d.ts.map +1 -0
  130. package/dist/lang.js +52 -0
  131. package/dist/lang.js.map +1 -0
  132. package/dist/learning/suppressions.d.ts +70 -0
  133. package/dist/learning/suppressions.d.ts.map +1 -0
  134. package/dist/learning/suppressions.js +179 -0
  135. package/dist/learning/suppressions.js.map +1 -0
  136. package/dist/mcp/server.d.ts +2 -0
  137. package/dist/mcp/server.d.ts.map +1 -0
  138. package/dist/mcp/server.js +187 -0
  139. package/dist/mcp/server.js.map +1 -0
  140. package/dist/mcp/tools/explain.d.ts +58 -0
  141. package/dist/mcp/tools/explain.d.ts.map +1 -0
  142. package/dist/mcp/tools/explain.js +60 -0
  143. package/dist/mcp/tools/explain.js.map +1 -0
  144. package/dist/mcp/tools/precheck.d.ts +29 -0
  145. package/dist/mcp/tools/precheck.d.ts.map +1 -0
  146. package/dist/mcp/tools/precheck.js +42 -0
  147. package/dist/mcp/tools/precheck.js.map +1 -0
  148. package/dist/mcp/tools/validate.d.ts +73 -0
  149. package/dist/mcp/tools/validate.d.ts.map +1 -0
  150. package/dist/mcp/tools/validate.js +66 -0
  151. package/dist/mcp/tools/validate.js.map +1 -0
  152. package/dist/mcp/warm.d.ts +88 -0
  153. package/dist/mcp/warm.d.ts.map +1 -0
  154. package/dist/mcp/warm.js +331 -0
  155. package/dist/mcp/warm.js.map +1 -0
  156. package/dist/orchestrator.d.ts +46 -0
  157. package/dist/orchestrator.d.ts.map +1 -0
  158. package/dist/orchestrator.js +596 -0
  159. package/dist/orchestrator.js.map +1 -0
  160. package/dist/policy.d.ts +51 -0
  161. package/dist/policy.d.ts.map +1 -0
  162. package/dist/policy.js +201 -0
  163. package/dist/policy.js.map +1 -0
  164. package/dist/risk.d.ts +31 -0
  165. package/dist/risk.d.ts.map +1 -0
  166. package/dist/risk.js +92 -0
  167. package/dist/risk.js.map +1 -0
  168. package/dist/stats.d.ts +72 -0
  169. package/dist/stats.d.ts.map +1 -0
  170. package/dist/stats.js +217 -0
  171. package/dist/stats.js.map +1 -0
  172. package/dist/telemetry/collector.d.ts +10 -0
  173. package/dist/telemetry/collector.d.ts.map +1 -0
  174. package/dist/telemetry/collector.js +75 -0
  175. package/dist/telemetry/collector.js.map +1 -0
  176. package/dist/telemetry/consent.d.ts +9 -0
  177. package/dist/telemetry/consent.d.ts.map +1 -0
  178. package/dist/telemetry/consent.js +42 -0
  179. package/dist/telemetry/consent.js.map +1 -0
  180. package/dist/telemetry/installation.d.ts +2 -0
  181. package/dist/telemetry/installation.d.ts.map +1 -0
  182. package/dist/telemetry/installation.js +32 -0
  183. package/dist/telemetry/installation.js.map +1 -0
  184. package/dist/telemetry/sanitizer.d.ts +5 -0
  185. package/dist/telemetry/sanitizer.d.ts.map +1 -0
  186. package/dist/telemetry/sanitizer.js +60 -0
  187. package/dist/telemetry/sanitizer.js.map +1 -0
  188. package/dist/telemetry/types.d.ts +39 -0
  189. package/dist/telemetry/types.d.ts.map +1 -0
  190. package/dist/telemetry/types.js +4 -0
  191. package/dist/telemetry/types.js.map +1 -0
  192. package/dist/telemetry/uploader.d.ts +12 -0
  193. package/dist/telemetry/uploader.d.ts.map +1 -0
  194. package/dist/telemetry/uploader.js +92 -0
  195. package/dist/telemetry/uploader.js.map +1 -0
  196. package/dist/util/logger.d.ts +19 -0
  197. package/dist/util/logger.d.ts.map +1 -0
  198. package/dist/util/logger.js +58 -0
  199. package/dist/util/logger.js.map +1 -0
  200. package/dist/util/safe-paths.d.ts +8 -0
  201. package/dist/util/safe-paths.d.ts.map +1 -0
  202. package/dist/util/safe-paths.js +102 -0
  203. package/dist/util/safe-paths.js.map +1 -0
  204. package/dist/util/subprocess.d.ts +32 -0
  205. package/dist/util/subprocess.d.ts.map +1 -0
  206. package/dist/util/subprocess.js +137 -0
  207. package/dist/util/subprocess.js.map +1 -0
  208. package/package.json +93 -0
@@ -0,0 +1,370 @@
1
+ import { makeFindingId } from "../findings.js";
2
+ import { isLineSuppressed, isTestOrFixturePath } from "./pragma.js";
3
+ // ── Helpers ──────────────────────────────────────────────────────────────
4
+ /** A JS arg "looks tainted" if it is NOT a pure string/number literal. */
5
+ function looksTainted(arg) {
6
+ const s = arg.trim();
7
+ if (s === "")
8
+ return false;
9
+ // Template literal with substitution: `...${x}...` is ALWAYS tainted.
10
+ if (/^`[^`]*\$\{/.test(s))
11
+ return true;
12
+ // Plain string literal (no concat with var indicators).
13
+ if (/^['"`]/.test(s)) {
14
+ if (/[+]\s*[a-zA-Z_$]/.test(s))
15
+ return true; // "...".concat-like
16
+ return false;
17
+ }
18
+ // Numeric literal.
19
+ if (/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(s))
20
+ return false;
21
+ // boolean / null / undefined literals.
22
+ if (/^(?:true|false|null|undefined)$/.test(s))
23
+ return false;
24
+ // Anything else: variable, function call, property access → tainted.
25
+ return true;
26
+ }
27
+ /** First positional arg from a parenthesized call body. */
28
+ function firstPositionalArg(body) {
29
+ let depth = 0;
30
+ let inStr = null;
31
+ for (let i = 0; i < body.length; i++) {
32
+ const c = body[i];
33
+ if (inStr) {
34
+ if (c === inStr && body[i - 1] !== "\\")
35
+ inStr = null;
36
+ continue;
37
+ }
38
+ if (c === '"' || c === "'" || c === "`") {
39
+ inStr = c;
40
+ continue;
41
+ }
42
+ if (c === "(" || c === "[" || c === "{")
43
+ depth++;
44
+ else if (c === ")" || c === "]" || c === "}")
45
+ depth--;
46
+ else if (c === "," && depth === 0)
47
+ return body.slice(0, i).trim();
48
+ }
49
+ return body.trim();
50
+ }
51
+ /** Common taint sources in browser/Node code. Used by SSRF / open-redirect /
52
+ * path-traversal rules to confirm input flows from user-controlled places. */
53
+ const TAINT_SOURCE_RE = /\b(?:req\.(?:params|query|body|headers|cookies)|request\.(?:params|query|body)|process\.argv|process\.env\b(?!\.NODE_ENV\b)|location\.(?:hash|search|href|pathname)|document\.URL|window\.name|new\s+URL\s*\([^)]*location\.)/;
54
+ function containsTaintSource(s) {
55
+ return TAINT_SOURCE_RE.test(s);
56
+ }
57
+ // ── Rules ────────────────────────────────────────────────────────────────
58
+ const RULES = [
59
+ // ── Code execution ─────────────────────────────────────────────────────
60
+ {
61
+ id: "eval-tainted",
62
+ cwe: "CWE-95",
63
+ severity: "critical",
64
+ what: "eval() called with non-literal argument",
65
+ // (?<![\w.]) avoids matching obj.eval (own-eval methods)
66
+ pattern: /(?<![\w.])eval\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
67
+ isDangerous: (first) => looksTainted(first),
68
+ remediation: "Never eval user input. For JSON use JSON.parse; for dynamic dispatch use a name→function map. " +
69
+ "If a constant calculation is required, use a vetted expression evaluator (mathjs, expr-eval) " +
70
+ "with a sandbox.",
71
+ },
72
+ {
73
+ id: "new-function-tainted",
74
+ cwe: "CWE-95",
75
+ severity: "critical",
76
+ what: "new Function(...) — eval-equivalent",
77
+ pattern: /\bnew\s+Function\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
78
+ isDangerous: () => true,
79
+ remediation: "new Function() compiles a string into a function and is identical to eval in capability. " +
80
+ "Refactor to a pre-compiled lookup table; for plugin systems consider iframes/Workers with " +
81
+ "structured-clone messaging.",
82
+ },
83
+ {
84
+ id: "settimer-string",
85
+ cwe: "CWE-95",
86
+ severity: "high",
87
+ what: "setTimeout / setInterval called with a string body (implicit eval)",
88
+ pattern: /\b(?:setTimeout|setInterval)\s*\(\s*(['"`])/g,
89
+ isDangerous: () => true,
90
+ remediation: "Pass a function literal: setTimeout(() => { ... }, 1000). String-form setTimeout implicitly " +
91
+ "evals its first argument and is treated as eval by browsers and CSP.",
92
+ },
93
+ // ── XSS ────────────────────────────────────────────────────────────────
94
+ {
95
+ id: "innerhtml-tainted",
96
+ cwe: "CWE-79",
97
+ severity: "critical",
98
+ what: ".innerHTML assigned a non-literal value — DOM XSS",
99
+ // Capture the RHS of the assignment (heuristic: up to ; or newline).
100
+ pattern: /\.innerHTML\s*=\s*([^;\n]+)/g,
101
+ isDangerous: (rhs) => looksTainted(rhs),
102
+ remediation: "Use .textContent for text (auto-escaped) or DOMPurify.sanitize(html) before assigning to innerHTML. " +
103
+ "For rich content build the DOM imperatively: createElement + textContent + appendChild.",
104
+ },
105
+ {
106
+ id: "outerhtml-tainted",
107
+ cwe: "CWE-79",
108
+ severity: "critical",
109
+ what: ".outerHTML assigned a non-literal value — DOM XSS",
110
+ pattern: /\.outerHTML\s*=\s*([^;\n]+)/g,
111
+ isDangerous: (rhs) => looksTainted(rhs),
112
+ remediation: "Replace the element with replaceWith(textNode) or replaceChildren() — do not assign outerHTML " +
113
+ "with attacker-controlled markup.",
114
+ },
115
+ {
116
+ id: "document-write",
117
+ cwe: "CWE-79",
118
+ severity: "critical",
119
+ what: "document.write / writeln called with non-literal — XSS + page-parse corruption",
120
+ pattern: /\bdocument\.(?:write|writeln)\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
121
+ isDangerous: (first) => looksTainted(first),
122
+ remediation: "Build the DOM via document.createElement + appendChild. document.write is sync, blocks the " +
123
+ "parser, and turns any HTML string into live nodes — XSS-sink by design.",
124
+ },
125
+ {
126
+ id: "react-dangerously-set-innerhtml",
127
+ cwe: "CWE-79",
128
+ severity: "critical",
129
+ what: "React dangerouslySetInnerHTML — XSS sink unless sanitized",
130
+ // Match the prop with a __html that references a variable (not literal).
131
+ pattern: /dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:\s*([^}]+)\}/g,
132
+ isDangerous: (val) => looksTainted(val),
133
+ remediation: "Always run user-controlled HTML through DOMPurify.sanitize() before passing as __html. Better: " +
134
+ "represent content as React children (auto-escaped) and avoid raw HTML entirely.",
135
+ },
136
+ // ── Command injection ──────────────────────────────────────────────────
137
+ {
138
+ id: "child-process-exec",
139
+ cwe: "CWE-78",
140
+ severity: "critical",
141
+ what: "child_process.exec / execSync called with non-literal command — OS command injection",
142
+ // Match BOTH child_process.exec(...) AND bare exec(...). Bare exec is
143
+ // very common (RegExp.exec, jest .exec) — gate to files that actually
144
+ // import child_process so the rule doesn't fire on regex / matcher use.
145
+ pattern: /\b(?:child_process\.|cp\.)?(?:exec|execSync)\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
146
+ fileGate: (c) => /\bchild_process\b|require\s*\(\s*['"`]child_process['"`]\s*\)|from\s+['"`]child_process['"`]/.test(c),
147
+ isDangerous: (first) => looksTainted(first),
148
+ remediation: "exec / execSync invoke /bin/sh -c. Use execFile / execFileSync (no shell) with an arg array: " +
149
+ "execFile('git', ['log', '--oneline'], cb). Validate the binary against an allow-list.",
150
+ },
151
+ {
152
+ id: "spawn-shell-true",
153
+ cwe: "CWE-78",
154
+ severity: "critical",
155
+ what: "spawn / spawnSync called with shell:true and tainted command",
156
+ pattern: /\b(?:child_process\.)?(?:spawn|spawnSync)\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
157
+ isDangerous: (first, all) => /shell\s*:\s*true\b/.test(all) && looksTainted(first),
158
+ remediation: "Drop shell:true (the default is false — safer). Pass the command + args as separate values: " +
159
+ "spawn('nslookup', [domain]). shell:true means the runtime invokes /bin/sh -c <string>, opening " +
160
+ "the gate for ; | & $() chains.",
161
+ },
162
+ // ── Path traversal ─────────────────────────────────────────────────────
163
+ {
164
+ id: "fs-tainted",
165
+ cwe: "CWE-22",
166
+ severity: "high",
167
+ what: "fs.{readFile,writeFile,unlink,createReadStream,...} called with path derived from request input",
168
+ pattern: /\bfs\.(?:readFile|readFileSync|createReadStream|writeFile|writeFileSync|unlink|unlinkSync)\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
169
+ isDangerous: (first) => containsTaintSource(first),
170
+ remediation: "Use path.resolve(safeBase, path.basename(userInput)) and verify the resolved path STARTS WITH " +
171
+ "the safe base directory. Even better: maintain a map of allowed file ids → static paths.",
172
+ },
173
+ // ── SSRF ───────────────────────────────────────────────────────────────
174
+ {
175
+ id: "ssrf-fetch-tainted",
176
+ cwe: "CWE-918",
177
+ severity: "high",
178
+ what: "fetch / axios / got / http.get / request called with URL from user input — SSRF",
179
+ pattern: /\b(?:fetch|axios(?:\.[a-z]+)?|got(?:\.[a-z]+)?|http\.get|https\.get|request)\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
180
+ isDangerous: (first) => containsTaintSource(first),
181
+ remediation: "Parse the URL with new URL(input), require scheme to be http(s), and reject targets in " +
182
+ "loopback / link-local / RFC1918 / metadata-service ranges (169.254.169.254). Maintain " +
183
+ "an allow-list of permitted hosts per integration.",
184
+ },
185
+ // ── Weak crypto ────────────────────────────────────────────────────────
186
+ {
187
+ id: "weak-crypto-createhash",
188
+ cwe: "CWE-327",
189
+ severity: "high",
190
+ what: "crypto.createHash called with md5 or sha1",
191
+ pattern: /\bcrypto\.createHash\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
192
+ isDangerous: (alg) => /^(md5|sha1|md4)$/i.test(alg),
193
+ remediation: "Use crypto.createHash('sha256') or createHash('sha3-256'). For password hashing use " +
194
+ "argon2 (npm: argon2 or argon2-cffi) or bcrypt. MD5 and SHA-1 are practically collisionable.",
195
+ },
196
+ {
197
+ id: "math-random-for-security",
198
+ cwe: "CWE-338",
199
+ severity: "high",
200
+ what: "Math.random() used in a security-sensitive context",
201
+ pattern: /\bMath\.random\s*\(\s*\)/g,
202
+ // Only fire if the file contains a security keyword somewhere — random
203
+ // for dice / UI / animation is fine.
204
+ // Case-insensitive substring (camelCase aware via plain substring match).
205
+ fileGate: (c) => /(?:token|password|nonce|csrf|sessionid|api[_-]?key|authkey|signingkey)/i.test(c) ||
206
+ /\bsecret\b/i.test(c),
207
+ isDangerous: () => true,
208
+ remediation: "Use crypto.randomBytes(N).toString('hex') or crypto.randomUUID(). Math.random is a PRNG " +
209
+ "(typically Xorshift128+) — predictable and unsuitable for any token or secret.",
210
+ },
211
+ // ── JWT misuse ─────────────────────────────────────────────────────────
212
+ {
213
+ id: "jwt-alg-none",
214
+ cwe: "CWE-347",
215
+ severity: "critical",
216
+ what: "jwt.verify called with algorithms:['none'] — accepts any forged token",
217
+ pattern: /\bjwt\.verify\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
218
+ isDangerous: (_first, all) => /algorithms\s*:\s*\[[^\]]*['"]none['"]/i.test(all),
219
+ remediation: "Pin exactly one algorithm: jwt.verify(token, key, { algorithms: ['HS256'] }). 'none' bypasses " +
220
+ "signature verification entirely — the attacker can forge any payload.",
221
+ },
222
+ {
223
+ id: "jwt-no-algorithms",
224
+ cwe: "CWE-327",
225
+ severity: "high",
226
+ what: "jwt.verify without an algorithms allowlist — enables algorithm-confusion attacks",
227
+ pattern: /\bjwt\.verify\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
228
+ isDangerous: (_first, all) => !/\balgorithms\s*:/.test(all),
229
+ remediation: "Always set { algorithms: ['HS256'] } (or your one true algorithm). Without it the library " +
230
+ "trusts the JWT header's 'alg' field — an attacker can swap RS256 → HS256 and sign with the " +
231
+ "public key as the HMAC secret.",
232
+ },
233
+ {
234
+ id: "jwt-hardcoded-secret",
235
+ cwe: "CWE-798",
236
+ severity: "critical",
237
+ what: "jwt.sign / verify with a hardcoded short secret (likely test/placeholder)",
238
+ pattern: /\bjwt\.(?:sign|verify)\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
239
+ isDangerous: (_first, all) => {
240
+ // Look for second-arg pattern: , "shortstring" or , 'shortstring'
241
+ const m = all.match(/,\s*['"`]([^'"`]{1,15})['"`]/);
242
+ return Boolean(m && m[1] && !/^[A-Z_]+$/.test(m[1])); // exclude env-var ALL_CAPS placeholders
243
+ },
244
+ remediation: "Load the secret from env (process.env.JWT_SECRET) and generate it with at least 32 random bytes " +
245
+ "(crypto.randomBytes(32).toString('hex')). Short or hardcoded secrets are crackable offline in seconds.",
246
+ },
247
+ // ── Open redirect ──────────────────────────────────────────────────────
248
+ {
249
+ id: "open-redirect-res",
250
+ cwe: "CWE-601",
251
+ severity: "high",
252
+ what: "res.redirect called with user-controlled URL — open redirect / phishing pivot",
253
+ pattern: /\bres\.redirect\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
254
+ isDangerous: (first) => containsTaintSource(first),
255
+ remediation: "Validate the destination: parse with new URL(value, app-base), confirm origin matches your " +
256
+ "own host, or maintain an allow-list of safe relative paths. Never redirect to a raw req.query value.",
257
+ },
258
+ // ── CORS misconfig ─────────────────────────────────────────────────────
259
+ {
260
+ id: "cors-wildcard-credentials",
261
+ cwe: "CWE-942",
262
+ severity: "critical",
263
+ what: "CORS Access-Control-Allow-Origin: * combined with Allow-Credentials: true — data exfiltration",
264
+ // Multi-line match: ACAO wildcard within 400 chars of credentials true
265
+ pattern: /['"`]Access-Control-Allow-Origin['"`]\s*[,:]\s*['"`]\*['"`][\s\S]{0,400}?['"`]Access-Control-Allow-Credentials['"`]\s*[,:]\s*['"`]true['"`]/g,
266
+ isDangerous: () => true,
267
+ remediation: "Set ACAO to a SPECIFIC origin (or a tight allow-list of origins) when credentials are allowed. " +
268
+ "The combination ACAO=* + ACAC=true is invalid per spec and exposes authenticated endpoints to " +
269
+ "every origin — modern browsers refuse it but proxies / caches may strip the credentials header.",
270
+ },
271
+ // ── Prototype pollution ────────────────────────────────────────────────
272
+ {
273
+ id: "proto-pollution-lodash",
274
+ cwe: "CWE-1321",
275
+ severity: "high",
276
+ what: "lodash _.merge / setWith / defaultsDeep with attacker-controlled source — prototype pollution",
277
+ pattern: /\b_\.(?:merge|mergeWith|set|setWith|defaultsDeep)\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)/g,
278
+ isDangerous: (first, all) => containsTaintSource(first) || containsTaintSource(all),
279
+ remediation: "Switch to Object.assign({}, ...) or structuredClone + targeted merge. If you must use lodash, " +
280
+ "lock to >=4.17.21 AND use _.cloneDeep + an allow-list of keys. Prototype pollution gives RCE in " +
281
+ "many popular libraries (express, mongoose).",
282
+ },
283
+ {
284
+ id: "proto-pollution-assign-prototype",
285
+ cwe: "CWE-1321",
286
+ severity: "high",
287
+ what: "Object.assign(<obj>.prototype, ...) — mutates the prototype chain",
288
+ pattern: /\bObject\.assign\s*\(\s*[a-zA-Z_$][\w$]*\.prototype\s*,/g,
289
+ isDangerous: () => true,
290
+ remediation: "Don't mutate built-in or library prototypes. Add methods on instances or via class extension. " +
291
+ "Mutating prototypes affects every object in the program and is a common privilege-escalation primitive.",
292
+ },
293
+ ];
294
+ // ── Engine ───────────────────────────────────────────────────────────────
295
+ export class JsSinksEngine {
296
+ name = "js-sinks";
297
+ languages = ["javascript", "typescript"];
298
+ async available() {
299
+ return true;
300
+ }
301
+ async run(input) {
302
+ const t0 = Date.now();
303
+ const findings = [];
304
+ if (input.lang !== "javascript" && input.lang !== "typescript") {
305
+ return { engine: this.name, findings: [], unavailable: false, durationMs: 0 };
306
+ }
307
+ // Auto-skip test / fixture / sink-engine source files: they contain
308
+ // the very patterns we look for by definition. The LLM critic still
309
+ // runs on those files so genuine planted bugs are still caught.
310
+ if (isTestOrFixturePath(input.filePath)) {
311
+ return { engine: this.name, findings: [], unavailable: false, durationMs: 0 };
312
+ }
313
+ const content = input.content;
314
+ const lines = content.split(/\r?\n/);
315
+ const seen = new Set(); // dedup overlapping rules on same site
316
+ for (const rule of RULES) {
317
+ if (rule.fileGate && !rule.fileGate(content))
318
+ continue;
319
+ rule.pattern.lastIndex = 0;
320
+ let m;
321
+ while ((m = rule.pattern.exec(content)) !== null) {
322
+ const allArgs = m[1] ?? "";
323
+ const firstArg = firstPositionalArg(allArgs);
324
+ if (!rule.isDangerous(firstArg, allArgs, content))
325
+ continue;
326
+ const upTo = content.slice(0, m.index);
327
+ const line = upTo.split(/\r?\n/).length;
328
+ const snippet = (lines[line - 1] ?? "").slice(0, 400);
329
+ if (isLineSuppressed(snippet, rule.id))
330
+ continue;
331
+ const id = makeFindingId({
332
+ engine: this.name,
333
+ file: input.filePath,
334
+ line,
335
+ rule_id: rule.id,
336
+ });
337
+ if (seen.has(id))
338
+ continue;
339
+ seen.add(id);
340
+ findings.push({
341
+ id,
342
+ engine: "js-sinks",
343
+ file: input.filePath,
344
+ line,
345
+ rule_id: rule.id,
346
+ cwe: rule.cwe,
347
+ severity: rule.severity,
348
+ message: `${rule.what}. Likely ${rule.cwe} — review and remediate before merging.`,
349
+ evidence: { snippet },
350
+ confidence: 0.75,
351
+ source: "pattern",
352
+ remediation: rule.remediation,
353
+ });
354
+ }
355
+ }
356
+ return {
357
+ engine: this.name,
358
+ findings,
359
+ unavailable: false,
360
+ durationMs: Date.now() - t0,
361
+ };
362
+ }
363
+ }
364
+ export const _testing = {
365
+ RULES,
366
+ looksTainted,
367
+ firstPositionalArg,
368
+ containsTaintSource,
369
+ };
370
+ //# sourceMappingURL=js-sinks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"js-sinks.js","sourceRoot":"","sources":["../../src/engines/js-sinks.ts"],"names":[],"mappings":"AAwCA,OAAO,EAAW,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAapE,4EAA4E;AAE5E,0EAA0E;AAC1E,SAAS,YAAY,CAAC,GAAW;IAC/B,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACrB,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC3B,sEAAsE;IACtE,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,wDAAwD;IACxD,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,oBAAoB;QACjE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,mBAAmB;IACnB,IAAI,oCAAoC,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/D,uCAAuC;IACvC,IAAI,iCAAiC,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5D,qEAAqE;IACrE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,2DAA2D;AAC3D,SAAS,kBAAkB,CAAC,IAAY;IACtC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACnB,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI;gBAAE,KAAK,GAAG,IAAI,CAAC;YACtD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACxC,KAAK,GAAG,CAAC,CAAC;YACV,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,KAAK,EAAE,CAAC;aAC5C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,KAAK,EAAE,CAAC;aACjD,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpE,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;AACrB,CAAC;AAED;8EAC8E;AAC9E,MAAM,eAAe,GACnB,+NAA+N,CAAC;AAElO,SAAS,mBAAmB,CAAC,CAAS;IACpC,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,4EAA4E;AAE5E,MAAM,KAAK,GAA4B;IACrC,0EAA0E;IAC1E;QACE,EAAE,EAAE,cAAc;QAClB,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,yCAAyC;QAC/C,yDAAyD;QACzD,OAAO,EAAE,qDAAqD;QAC9D,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC;QAC3C,WAAW,EACT,gGAAgG;YAChG,+FAA+F;YAC/F,iBAAiB;KACpB;IACD;QACE,EAAE,EAAE,sBAAsB;QAC1B,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,qCAAqC;QAC3C,OAAO,EAAE,uDAAuD;QAChE,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;QACvB,WAAW,EACT,2FAA2F;YAC3F,4FAA4F;YAC5F,6BAA6B;KAChC;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,oEAAoE;QAC1E,OAAO,EAAE,8CAA8C;QACvD,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;QACvB,WAAW,EACT,8FAA8F;YAC9F,sEAAsE;KACzE;IACD,0EAA0E;IAC1E;QACE,EAAE,EAAE,mBAAmB;QACvB,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,mDAAmD;QACzD,qEAAqE;QACrE,OAAO,EAAE,8BAA8B;QACvC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC;QACvC,WAAW,EACT,sGAAsG;YACtG,yFAAyF;KAC5F;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,mDAAmD;QACzD,OAAO,EAAE,8BAA8B;QACvC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC;QACvC,WAAW,EACT,gGAAgG;YAChG,kCAAkC;KACrC;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,gFAAgF;QACtF,OAAO,EACL,oEAAoE;QACtE,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC;QAC3C,WAAW,EACT,6FAA6F;YAC7F,yEAAyE;KAC5E;IACD;QACE,EAAE,EAAE,iCAAiC;QACrC,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,2DAA2D;QACjE,yEAAyE;QACzE,OAAO,EAAE,iEAAiE;QAC1E,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC;QACvC,WAAW,EACT,iGAAiG;YACjG,iFAAiF;KACpF;IACD,0EAA0E;IAC1E;QACE,EAAE,EAAE,oBAAoB;QACxB,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,sFAAsF;QAC5F,sEAAsE;QACtE,sEAAsE;QACtE,wEAAwE;QACxE,OAAO,EACL,mFAAmF;QACrF,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CACd,8FAA8F,CAAC,IAAI,CACjG,CAAC,CACF;QACH,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC;QAC3C,WAAW,EACT,+FAA+F;YAC/F,uFAAuF;KAC1F;IACD;QACE,EAAE,EAAE,kBAAkB;QACtB,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,8DAA8D;QACpE,OAAO,EACL,gFAAgF;QAClF,WAAW,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC;QAClF,WAAW,EACT,8FAA8F;YAC9F,iGAAiG;YACjG,gCAAgC;KACnC;IACD,0EAA0E;IAC1E;QACE,EAAE,EAAE,YAAY;QAChB,GAAG,EAAE,QAAQ;QACb,QAAQ,EAAE,MAAM;QAChB,IAAI,EACF,iGAAiG;QACnG,OAAO,EACL,iIAAiI;QACnI,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC;QAClD,WAAW,EACT,gGAAgG;YAChG,0FAA0F;KAC7F;IACD,0EAA0E;IAC1E;QACE,EAAE,EAAE,oBAAoB;QACxB,GAAG,EAAE,SAAS;QACd,QAAQ,EAAE,MAAM;QAChB,IAAI,EACF,iFAAiF;QACnF,OAAO,EACL,mHAAmH;QACrH,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC;QAClD,WAAW,EACT,yFAAyF;YACzF,wFAAwF;YACxF,mDAAmD;KACtD;IACD,0EAA0E;IAC1E;QACE,EAAE,EAAE,wBAAwB;QAC5B,GAAG,EAAE,SAAS;QACd,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,2CAA2C;QACjD,OAAO,EACL,uDAAuD;QACzD,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;QACnD,WAAW,EACT,sFAAsF;YACtF,6FAA6F;KAChG;IACD;QACE,EAAE,EAAE,0BAA0B;QAC9B,GAAG,EAAE,SAAS;QACd,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,oDAAoD;QAC1D,OAAO,EAAE,2BAA2B;QACpC,uEAAuE;QACvE,qCAAqC;QACrC,0EAA0E;QAC1E,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CACd,yEAAyE,CAAC,IAAI,CAAC,CAAC,CAAC;YACjF,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;QACvB,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;QACvB,WAAW,EACT,0FAA0F;YAC1F,gFAAgF;KACnF;IACD,0EAA0E;IAC1E;QACE,EAAE,EAAE,cAAc;QAClB,GAAG,EAAE,SAAS;QACd,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,uEAAuE;QAC7E,OAAO,EAAE,oDAAoD;QAC7D,WAAW,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAC3B,wCAAwC,CAAC,IAAI,CAAC,GAAG,CAAC;QACpD,WAAW,EACT,gGAAgG;YAChG,uEAAuE;KAC1E;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,GAAG,EAAE,SAAS;QACd,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,kFAAkF;QACxF,OAAO,EAAE,oDAAoD;QAC7D,WAAW,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;QAC3D,WAAW,EACT,4FAA4F;YAC5F,6FAA6F;YAC7F,gCAAgC;KACnC;IACD;QACE,EAAE,EAAE,sBAAsB;QAC1B,GAAG,EAAE,SAAS;QACd,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,2EAA2E;QACjF,OAAO,EACL,6DAA6D;QAC/D,WAAW,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;YAC3B,kEAAkE;YAClE,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;YACpD,OAAO,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,wCAAwC;QAChG,CAAC;QACD,WAAW,EACT,kGAAkG;YAClG,wGAAwG;KAC3G;IACD,0EAA0E;IAC1E;QACE,EAAE,EAAE,mBAAmB;QACvB,GAAG,EAAE,SAAS;QACd,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,+EAA+E;QACrF,OAAO,EAAE,sDAAsD;QAC/D,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC;QAClD,WAAW,EACT,6FAA6F;YAC7F,sGAAsG;KACzG;IACD,0EAA0E;IAC1E;QACE,EAAE,EAAE,2BAA2B;QAC/B,GAAG,EAAE,SAAS;QACd,QAAQ,EAAE,UAAU;QACpB,IAAI,EACF,+FAA+F;QACjG,uEAAuE;QACvE,OAAO,EACL,8IAA8I;QAChJ,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;QACvB,WAAW,EACT,iGAAiG;YACjG,gGAAgG;YAChG,iGAAiG;KACpG;IACD,0EAA0E;IAC1E;QACE,EAAE,EAAE,wBAAwB;QAC5B,GAAG,EAAE,UAAU;QACf,QAAQ,EAAE,MAAM;QAChB,IAAI,EACF,+FAA+F;QACjG,OAAO,EACL,wFAAwF;QAC1F,WAAW,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,mBAAmB,CAAC,GAAG,CAAC;QACnF,WAAW,EACT,gGAAgG;YAChG,kGAAkG;YAClG,6CAA6C;KAChD;IACD;QACE,EAAE,EAAE,kCAAkC;QACtC,GAAG,EAAE,UAAU;QACf,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,mEAAmE;QACzE,OAAO,EACL,0DAA0D;QAC5D,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;QACvB,WAAW,EACT,gGAAgG;YAChG,yGAAyG;KAC5G;CACF,CAAC;AAEF,4EAA4E;AAE5E,MAAM,OAAO,aAAa;IACf,IAAI,GAAG,UAAmB,CAAC;IAC3B,SAAS,GAAwB,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAEvE,KAAK,CAAC,SAAS;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAqB;QAC7B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC/D,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;QAChF,CAAC;QACD,oEAAoE;QACpE,oEAAoE;QACpE,gEAAgE;QAChE,IAAI,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;QAChF,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC,CAAC,uCAAuC;QAEvE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,SAAS;YACvD,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;YAC3B,IAAI,CAAyB,CAAC;YAC9B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBACjD,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC3B,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;gBAC7C,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC;oBAAE,SAAS;gBAE5D,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;gBACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;gBACxC,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;gBACtD,IAAI,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;oBAAE,SAAS;gBACjD,MAAM,EAAE,GAAG,aAAa,CAAC;oBACvB,MAAM,EAAE,IAAI,CAAC,IAAI;oBACjB,IAAI,EAAE,KAAK,CAAC,QAAQ;oBACpB,IAAI;oBACJ,OAAO,EAAE,IAAI,CAAC,EAAE;iBACjB,CAAC,CAAC;gBACH,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,SAAS;gBAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACb,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE;oBACF,MAAM,EAAE,UAAU;oBAClB,IAAI,EAAE,KAAK,CAAC,QAAQ;oBACpB,IAAI;oBACJ,OAAO,EAAE,IAAI,CAAC,EAAE;oBAChB,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC,GAAG,yCAAyC;oBAClF,QAAQ,EAAE,EAAE,OAAO,EAAE;oBACrB,UAAU,EAAE,IAAI;oBAChB,MAAM,EAAE,SAAS;oBACjB,WAAW,EAAE,IAAI,CAAC,WAAW;iBAC9B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,IAAI;YACjB,QAAQ;YACR,WAAW,EAAE,KAAK;YAClB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;SAC5B,CAAC;IACJ,CAAC;CACF;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,KAAK;IACL,YAAY;IACZ,kBAAkB;IAClB,mBAAmB;CACpB,CAAC"}
@@ -0,0 +1,130 @@
1
+ import { z } from "zod";
2
+ import { Finding } from "../findings.js";
3
+ import { type VulRagHit } from "../index/vulrag.js";
4
+ import type { Engine } from "./types.js";
5
+ export declare class CriticAuthError extends Error {
6
+ }
7
+ export declare class CriticUnavailableError extends Error {
8
+ }
9
+ declare const VerdictRow: z.ZodObject<{
10
+ rule_id: z.ZodString;
11
+ cwe: z.ZodOptional<z.ZodNullable<z.ZodString>>;
12
+ severity: z.ZodEnum<["info", "low", "medium", "high", "critical"]>;
13
+ line: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
14
+ message: z.ZodString;
15
+ evidence: z.ZodDefault<z.ZodOptional<z.ZodString>>;
16
+ remediation: z.ZodDefault<z.ZodOptional<z.ZodString>>;
17
+ confidence: z.ZodNumber;
18
+ }, "strip", z.ZodTypeAny, {
19
+ message: string;
20
+ rule_id: string;
21
+ severity: "info" | "low" | "medium" | "high" | "critical";
22
+ evidence: string;
23
+ confidence: number;
24
+ remediation: string;
25
+ line?: number | null | undefined;
26
+ cwe?: string | null | undefined;
27
+ }, {
28
+ message: string;
29
+ rule_id: string;
30
+ severity: "info" | "low" | "medium" | "high" | "critical";
31
+ confidence: number;
32
+ line?: number | null | undefined;
33
+ cwe?: string | null | undefined;
34
+ evidence?: string | undefined;
35
+ remediation?: string | undefined;
36
+ }>;
37
+ declare function findClaudeCli(): Promise<string | null>;
38
+ /** Test-only: reset the cached PATH lookup. */
39
+ export declare function _resetCliCacheForTests(): void;
40
+ /** Strip ASCII control chars (Cc), bidi overrides, and the Unicode tag block
41
+ * (U+E0000..U+E007F) which is invisible to humans but encodes instructions
42
+ * to LLMs (trojan-source / invisible-tag attack research, 2024-2025). */
43
+ declare function sanitizeForPrompt(s: string, maxLen: number): string;
44
+ interface PromptInputs {
45
+ filePath: string;
46
+ content: string;
47
+ lang: string;
48
+ priorFindings: ReadonlyArray<Finding>;
49
+ projectContextJson?: string;
50
+ vulrag: ReadonlyArray<VulRagHit>;
51
+ }
52
+ declare function buildPrompt(inputs: PromptInputs): string;
53
+ declare function extractJsonObject(text: string): unknown | null;
54
+ declare function verdictRowsToFindings(rows: z.infer<typeof VerdictRow>[], ctx: {
55
+ file: string;
56
+ contentLineCount: number;
57
+ vulragHits: ReadonlyArray<VulRagHit>;
58
+ }): Finding[];
59
+ declare function critiqueCacheKey(parts: {
60
+ contentHash: string;
61
+ priorFindingsHash: string;
62
+ contextHash: string;
63
+ vulragHash: string;
64
+ model: string;
65
+ effort: string;
66
+ }): string;
67
+ export declare const llmCriticEngine: Engine;
68
+ export declare const _testing: {
69
+ buildPrompt: typeof buildPrompt;
70
+ sanitizeForPrompt: typeof sanitizeForPrompt;
71
+ extractJsonObject: typeof extractJsonObject;
72
+ verdictRowsToFindings: typeof verdictRowsToFindings;
73
+ critiqueCacheKey: typeof critiqueCacheKey;
74
+ VerdictEnvelope: z.ZodObject<{
75
+ findings: z.ZodDefault<z.ZodArray<z.ZodObject<{
76
+ rule_id: z.ZodString;
77
+ cwe: z.ZodOptional<z.ZodNullable<z.ZodString>>;
78
+ severity: z.ZodEnum<["info", "low", "medium", "high", "critical"]>;
79
+ line: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
80
+ message: z.ZodString;
81
+ evidence: z.ZodDefault<z.ZodOptional<z.ZodString>>;
82
+ remediation: z.ZodDefault<z.ZodOptional<z.ZodString>>;
83
+ confidence: z.ZodNumber;
84
+ }, "strip", z.ZodTypeAny, {
85
+ message: string;
86
+ rule_id: string;
87
+ severity: "info" | "low" | "medium" | "high" | "critical";
88
+ evidence: string;
89
+ confidence: number;
90
+ remediation: string;
91
+ line?: number | null | undefined;
92
+ cwe?: string | null | undefined;
93
+ }, {
94
+ message: string;
95
+ rule_id: string;
96
+ severity: "info" | "low" | "medium" | "high" | "critical";
97
+ confidence: number;
98
+ line?: number | null | undefined;
99
+ cwe?: string | null | undefined;
100
+ evidence?: string | undefined;
101
+ remediation?: string | undefined;
102
+ }>, "many">>;
103
+ }, "strip", z.ZodTypeAny, {
104
+ findings: {
105
+ message: string;
106
+ rule_id: string;
107
+ severity: "info" | "low" | "medium" | "high" | "critical";
108
+ evidence: string;
109
+ confidence: number;
110
+ remediation: string;
111
+ line?: number | null | undefined;
112
+ cwe?: string | null | undefined;
113
+ }[];
114
+ }, {
115
+ findings?: {
116
+ message: string;
117
+ rule_id: string;
118
+ severity: "info" | "low" | "medium" | "high" | "critical";
119
+ confidence: number;
120
+ line?: number | null | undefined;
121
+ cwe?: string | null | undefined;
122
+ evidence?: string | undefined;
123
+ remediation?: string | undefined;
124
+ }[] | undefined;
125
+ }>;
126
+ findClaudeCli: typeof findClaudeCli;
127
+ PROMPT_VERSION: string;
128
+ };
129
+ export {};
130
+ //# sourceMappingURL=llm-critic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-critic.d.ts","sourceRoot":"","sources":["../../src/engines/llm-critic.ts"],"names":[],"mappings":"AAiDA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,OAAO,EAA8C,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG/D,OAAO,KAAK,EAAE,MAAM,EAAmC,MAAM,YAAY,CAAC;AAkC1E,qBAAa,eAAgB,SAAQ,KAAK;CAAG;AAC7C,qBAAa,sBAAuB,SAAQ,KAAK;CAAG;AAOpD,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;EASd,CAAC;AAeH,iBAAe,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2BrD;AAED,+CAA+C;AAC/C,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAID;;yEAEyE;AACzE,iBAAS,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAiB5D;AAID,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,iBAAS,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAiFjD;AAiED,iBAAS,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAYvD;AAED,iBAAS,qBAAqB,CAC5B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,EAAE,EAClC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,aAAa,CAAC,SAAS,CAAC,CAAA;CAAE,GACpF,OAAO,EAAE,CAmDX;AAID,iBAAS,gBAAgB,CAAC,KAAK,EAAE;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CAgBT;AAkLD,eAAO,MAAM,eAAe,EAAE,MAA8B,CAAC;AAG7D,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CASpB,CAAC"}