circle-ir 4.17.0 → 4.18.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 (280) hide show
  1. package/dist/analysis/advisories.d.ts +186 -0
  2. package/dist/analysis/advisories.d.ts.map +1 -0
  3. package/dist/analysis/advisories.js +391 -0
  4. package/dist/analysis/advisories.js.map +1 -0
  5. package/dist/analysis/comment-lines.d.ts +14 -0
  6. package/dist/analysis/comment-lines.d.ts.map +1 -0
  7. package/dist/analysis/comment-lines.js +34 -0
  8. package/dist/analysis/comment-lines.js.map +1 -0
  9. package/dist/analysis/config-loader.d.ts.map +1 -1
  10. package/dist/analysis/config-loader.js +59 -5
  11. package/dist/analysis/config-loader.js.map +1 -1
  12. package/dist/analysis/constant-caller-gate.d.ts +2 -2
  13. package/dist/analysis/constant-caller-gate.d.ts.map +1 -1
  14. package/dist/analysis/constant-caller-gate.js +205 -29
  15. package/dist/analysis/constant-caller-gate.js.map +1 -1
  16. package/dist/analysis/constant-propagation/propagator.d.ts +4 -0
  17. package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
  18. package/dist/analysis/constant-propagation/propagator.js +99 -0
  19. package/dist/analysis/constant-propagation/propagator.js.map +1 -1
  20. package/dist/analysis/entry-point-detection.d.ts.map +1 -1
  21. package/dist/analysis/entry-point-detection.js +72 -0
  22. package/dist/analysis/entry-point-detection.js.map +1 -1
  23. package/dist/analysis/interprocedural.d.ts.map +1 -1
  24. package/dist/analysis/interprocedural.js +75 -8
  25. package/dist/analysis/interprocedural.js.map +1 -1
  26. package/dist/analysis/maven-declared.d.ts +72 -0
  27. package/dist/analysis/maven-declared.d.ts.map +1 -0
  28. package/dist/analysis/maven-declared.js +216 -0
  29. package/dist/analysis/maven-declared.js.map +1 -0
  30. package/dist/analysis/maven-version.d.ts +28 -0
  31. package/dist/analysis/maven-version.d.ts.map +1 -0
  32. package/dist/analysis/maven-version.js +172 -0
  33. package/dist/analysis/maven-version.js.map +1 -0
  34. package/dist/analysis/passes/constant-propagation-pass.d.ts.map +1 -1
  35. package/dist/analysis/passes/constant-propagation-pass.js +29 -1
  36. package/dist/analysis/passes/constant-propagation-pass.js.map +1 -1
  37. package/dist/analysis/passes/cross-file-pass.d.ts.map +1 -1
  38. package/dist/analysis/passes/cross-file-pass.js +81 -1
  39. package/dist/analysis/passes/cross-file-pass.js.map +1 -1
  40. package/dist/analysis/passes/csharp-sanitizer-credit.d.ts +7 -0
  41. package/dist/analysis/passes/csharp-sanitizer-credit.d.ts.map +1 -1
  42. package/dist/analysis/passes/csharp-sanitizer-credit.js +44 -1
  43. package/dist/analysis/passes/csharp-sanitizer-credit.js.map +1 -1
  44. package/dist/analysis/passes/insecure-cookie-pass.d.ts +2 -0
  45. package/dist/analysis/passes/insecure-cookie-pass.d.ts.map +1 -1
  46. package/dist/analysis/passes/insecure-cookie-pass.js +25 -3
  47. package/dist/analysis/passes/insecure-cookie-pass.js.map +1 -1
  48. package/dist/analysis/passes/interprocedural-pass.d.ts +11 -14
  49. package/dist/analysis/passes/interprocedural-pass.d.ts.map +1 -1
  50. package/dist/analysis/passes/interprocedural-pass.js +562 -0
  51. package/dist/analysis/passes/interprocedural-pass.js.map +1 -1
  52. package/dist/analysis/passes/language-sources-pass.d.ts +18 -1
  53. package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
  54. package/dist/analysis/passes/language-sources-pass.js +1057 -14
  55. package/dist/analysis/passes/language-sources-pass.js.map +1 -1
  56. package/dist/analysis/passes/param-source-flow-gate-pass.d.ts.map +1 -1
  57. package/dist/analysis/passes/param-source-flow-gate-pass.js +66 -3
  58. package/dist/analysis/passes/param-source-flow-gate-pass.js.map +1 -1
  59. package/dist/analysis/passes/sink-filter-pass.d.ts +44 -2
  60. package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
  61. package/dist/analysis/passes/sink-filter-pass.js +690 -12
  62. package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
  63. package/dist/analysis/passes/taint-propagation-pass.d.ts.map +1 -1
  64. package/dist/analysis/passes/taint-propagation-pass.js +133 -10
  65. package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
  66. package/dist/analysis/passes/tls-verify-disabled-pass.d.ts +18 -0
  67. package/dist/analysis/passes/tls-verify-disabled-pass.d.ts.map +1 -1
  68. package/dist/analysis/passes/tls-verify-disabled-pass.js +161 -0
  69. package/dist/analysis/passes/tls-verify-disabled-pass.js.map +1 -1
  70. package/dist/analysis/passes/weak-random-pass.d.ts +6 -0
  71. package/dist/analysis/passes/weak-random-pass.d.ts.map +1 -1
  72. package/dist/analysis/passes/weak-random-pass.js +29 -0
  73. package/dist/analysis/passes/weak-random-pass.js.map +1 -1
  74. package/dist/analysis/promote-cross-file-flows.d.ts +17 -0
  75. package/dist/analysis/promote-cross-file-flows.d.ts.map +1 -0
  76. package/dist/analysis/promote-cross-file-flows.js +32 -0
  77. package/dist/analysis/promote-cross-file-flows.js.map +1 -0
  78. package/dist/analysis/sbom.d.ts +100 -0
  79. package/dist/analysis/sbom.d.ts.map +1 -1
  80. package/dist/analysis/sbom.js +514 -3
  81. package/dist/analysis/sbom.js.map +1 -1
  82. package/dist/analysis/semver-version.d.ts +30 -0
  83. package/dist/analysis/semver-version.d.ts.map +1 -0
  84. package/dist/analysis/semver-version.js +76 -0
  85. package/dist/analysis/semver-version.js.map +1 -0
  86. package/dist/analysis/taint-matcher.d.ts +5 -0
  87. package/dist/analysis/taint-matcher.d.ts.map +1 -1
  88. package/dist/analysis/taint-matcher.js +225 -4
  89. package/dist/analysis/taint-matcher.js.map +1 -1
  90. package/dist/analyzer.d.ts +57 -0
  91. package/dist/analyzer.d.ts.map +1 -1
  92. package/dist/analyzer.js +69 -6
  93. package/dist/analyzer.js.map +1 -1
  94. package/dist/browser/circle-ir.js +2936 -291
  95. package/dist/core/circle-ir-core.cjs +634 -203
  96. package/dist/core/circle-ir-core.js +634 -203
  97. package/dist/core/extractors/dfg.js +205 -2
  98. package/dist/core/extractors/dfg.js.map +1 -1
  99. package/dist/core/extractors/ts-declaration-detail.d.ts +39 -0
  100. package/dist/core/extractors/ts-declaration-detail.d.ts.map +1 -0
  101. package/dist/core/extractors/ts-declaration-detail.js +266 -0
  102. package/dist/core/extractors/ts-declaration-detail.js.map +1 -0
  103. package/dist/graph/analysis-pass.d.ts +4 -1
  104. package/dist/graph/analysis-pass.d.ts.map +1 -1
  105. package/dist/graph/analysis-pass.js +4 -1
  106. package/dist/graph/analysis-pass.js.map +1 -1
  107. package/dist/graph-facts/dead-code.d.ts +62 -0
  108. package/dist/graph-facts/dead-code.d.ts.map +1 -0
  109. package/dist/graph-facts/dead-code.js +90 -0
  110. package/dist/graph-facts/dead-code.js.map +1 -0
  111. package/dist/graph-facts/dependency-calls.d.ts +86 -0
  112. package/dist/graph-facts/dependency-calls.d.ts.map +1 -0
  113. package/dist/graph-facts/dependency-calls.js +93 -0
  114. package/dist/graph-facts/dependency-calls.js.map +1 -0
  115. package/dist/graph-facts/index.d.ts +10 -0
  116. package/dist/graph-facts/index.d.ts.map +1 -0
  117. package/dist/graph-facts/index.js +10 -0
  118. package/dist/graph-facts/index.js.map +1 -0
  119. package/dist/graph-facts/maven-packages.d.ts +42 -0
  120. package/dist/graph-facts/maven-packages.d.ts.map +1 -0
  121. package/dist/graph-facts/maven-packages.js +133 -0
  122. package/dist/graph-facts/maven-packages.js.map +1 -0
  123. package/dist/graph-facts/reach.d.ts +203 -0
  124. package/dist/graph-facts/reach.d.ts.map +1 -0
  125. package/dist/graph-facts/reach.js +219 -0
  126. package/dist/graph-facts/reach.js.map +1 -0
  127. package/dist/graph-facts/removal-reach.d.ts +73 -0
  128. package/dist/graph-facts/removal-reach.d.ts.map +1 -0
  129. package/dist/graph-facts/removal-reach.js +64 -0
  130. package/dist/graph-facts/removal-reach.js.map +1 -0
  131. package/dist/index.d.ts +21 -6
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +20 -3
  134. package/dist/index.js.map +1 -1
  135. package/dist/navigation/go-graph.d.ts +68 -0
  136. package/dist/navigation/go-graph.d.ts.map +1 -0
  137. package/dist/navigation/go-graph.js +753 -0
  138. package/dist/navigation/go-graph.js.map +1 -0
  139. package/dist/navigation/index.d.ts +7 -1
  140. package/dist/navigation/index.d.ts.map +1 -1
  141. package/dist/navigation/index.js +6 -0
  142. package/dist/navigation/index.js.map +1 -1
  143. package/dist/navigation/java-entry-tables.d.ts +43 -0
  144. package/dist/navigation/java-entry-tables.d.ts.map +1 -0
  145. package/dist/navigation/java-entry-tables.js +56 -0
  146. package/dist/navigation/java-entry-tables.js.map +1 -0
  147. package/dist/navigation/java-graph.d.ts +64 -0
  148. package/dist/navigation/java-graph.d.ts.map +1 -0
  149. package/dist/navigation/java-graph.js +466 -0
  150. package/dist/navigation/java-graph.js.map +1 -0
  151. package/dist/navigation/navigation-index.d.ts +51 -1
  152. package/dist/navigation/navigation-index.d.ts.map +1 -1
  153. package/dist/navigation/navigation-index.js +72 -0
  154. package/dist/navigation/navigation-index.js.map +1 -1
  155. package/dist/navigation/python-graph.d.ts +73 -0
  156. package/dist/navigation/python-graph.d.ts.map +1 -0
  157. package/dist/navigation/python-graph.js +867 -0
  158. package/dist/navigation/python-graph.js.map +1 -0
  159. package/dist/navigation/ts-call-edges.d.ts +193 -0
  160. package/dist/navigation/ts-call-edges.d.ts.map +1 -0
  161. package/dist/navigation/ts-call-edges.js +2089 -0
  162. package/dist/navigation/ts-call-edges.js.map +1 -0
  163. package/dist/navigation/ts-entry-tables.d.ts +88 -0
  164. package/dist/navigation/ts-entry-tables.d.ts.map +1 -0
  165. package/dist/navigation/ts-entry-tables.js +50 -0
  166. package/dist/navigation/ts-entry-tables.js.map +1 -0
  167. package/dist/path-set/branches.d.ts +52 -0
  168. package/dist/path-set/branches.d.ts.map +1 -0
  169. package/dist/path-set/branches.js +124 -0
  170. package/dist/path-set/branches.js.map +1 -0
  171. package/dist/path-set/finding-diff.d.ts +63 -0
  172. package/dist/path-set/finding-diff.d.ts.map +1 -0
  173. package/dist/path-set/finding-diff.js +150 -0
  174. package/dist/path-set/finding-diff.js.map +1 -0
  175. package/dist/path-set/index.d.ts +18 -0
  176. package/dist/path-set/index.d.ts.map +1 -0
  177. package/dist/path-set/index.js +12 -0
  178. package/dist/path-set/index.js.map +1 -0
  179. package/dist/path-set/line-diff.d.ts +19 -0
  180. package/dist/path-set/line-diff.d.ts.map +1 -0
  181. package/dist/path-set/line-diff.js +98 -0
  182. package/dist/path-set/line-diff.js.map +1 -0
  183. package/dist/path-set/path-set.d.ts +88 -0
  184. package/dist/path-set/path-set.d.ts.map +1 -0
  185. package/dist/path-set/path-set.js +716 -0
  186. package/dist/path-set/path-set.js.map +1 -0
  187. package/dist/path-set/templates.d.ts +42 -0
  188. package/dist/path-set/templates.d.ts.map +1 -0
  189. package/dist/path-set/templates.js +119 -0
  190. package/dist/path-set/templates.js.map +1 -0
  191. package/dist/path-set/types.d.ts +195 -0
  192. package/dist/path-set/types.d.ts.map +1 -0
  193. package/dist/path-set/types.js +8 -0
  194. package/dist/path-set/types.js.map +1 -0
  195. package/dist/remedy-class/index.d.ts +12 -0
  196. package/dist/remedy-class/index.d.ts.map +1 -0
  197. package/dist/remedy-class/index.js +9 -0
  198. package/dist/remedy-class/index.js.map +1 -0
  199. package/dist/remedy-class/match.d.ts +165 -0
  200. package/dist/remedy-class/match.d.ts.map +1 -0
  201. package/dist/remedy-class/match.js +705 -0
  202. package/dist/remedy-class/match.js.map +1 -0
  203. package/dist/remedy-class/output-context.d.ts +25 -0
  204. package/dist/remedy-class/output-context.d.ts.map +1 -0
  205. package/dist/remedy-class/output-context.js +150 -0
  206. package/dist/remedy-class/output-context.js.map +1 -0
  207. package/dist/remedy-class/sha256.d.ts +18 -0
  208. package/dist/remedy-class/sha256.d.ts.map +1 -0
  209. package/dist/remedy-class/sha256.js +89 -0
  210. package/dist/remedy-class/sha256.js.map +1 -0
  211. package/dist/remedy-class/table.d.ts +130 -0
  212. package/dist/remedy-class/table.d.ts.map +1 -0
  213. package/dist/remedy-class/table.js +457 -0
  214. package/dist/remedy-class/table.js.map +1 -0
  215. package/dist/resolution/cross-file.d.ts +9 -0
  216. package/dist/resolution/cross-file.d.ts.map +1 -1
  217. package/dist/resolution/cross-file.js +60 -5
  218. package/dist/resolution/cross-file.js.map +1 -1
  219. package/dist/stable-ids/builder.d.ts +20 -0
  220. package/dist/stable-ids/builder.d.ts.map +1 -0
  221. package/dist/stable-ids/builder.js +65 -0
  222. package/dist/stable-ids/builder.js.map +1 -0
  223. package/dist/stable-ids/diff.d.ts +18 -0
  224. package/dist/stable-ids/diff.d.ts.map +1 -0
  225. package/dist/stable-ids/diff.js +37 -0
  226. package/dist/stable-ids/diff.js.map +1 -0
  227. package/dist/stable-ids/go.d.ts +28 -0
  228. package/dist/stable-ids/go.d.ts.map +1 -0
  229. package/dist/stable-ids/go.js +160 -0
  230. package/dist/stable-ids/go.js.map +1 -0
  231. package/dist/stable-ids/hash.d.ts +22 -0
  232. package/dist/stable-ids/hash.d.ts.map +1 -0
  233. package/dist/stable-ids/hash.js +167 -0
  234. package/dist/stable-ids/hash.js.map +1 -0
  235. package/dist/stable-ids/index.d.ts +30 -0
  236. package/dist/stable-ids/index.d.ts.map +1 -0
  237. package/dist/stable-ids/index.js +59 -0
  238. package/dist/stable-ids/index.js.map +1 -0
  239. package/dist/stable-ids/java.d.ts +28 -0
  240. package/dist/stable-ids/java.d.ts.map +1 -0
  241. package/dist/stable-ids/java.js +224 -0
  242. package/dist/stable-ids/java.js.map +1 -0
  243. package/dist/stable-ids/moves.d.ts +87 -0
  244. package/dist/stable-ids/moves.d.ts.map +1 -0
  245. package/dist/stable-ids/moves.js +222 -0
  246. package/dist/stable-ids/moves.js.map +1 -0
  247. package/dist/stable-ids/nodes.d.ts +32 -0
  248. package/dist/stable-ids/nodes.d.ts.map +1 -0
  249. package/dist/stable-ids/nodes.js +92 -0
  250. package/dist/stable-ids/nodes.js.map +1 -0
  251. package/dist/stable-ids/normalize.d.ts +35 -0
  252. package/dist/stable-ids/normalize.d.ts.map +1 -0
  253. package/dist/stable-ids/normalize.js +174 -0
  254. package/dist/stable-ids/normalize.js.map +1 -0
  255. package/dist/stable-ids/parse-errors.d.ts +24 -0
  256. package/dist/stable-ids/parse-errors.d.ts.map +1 -0
  257. package/dist/stable-ids/parse-errors.js +46 -0
  258. package/dist/stable-ids/parse-errors.js.map +1 -0
  259. package/dist/stable-ids/python.d.ts +27 -0
  260. package/dist/stable-ids/python.d.ts.map +1 -0
  261. package/dist/stable-ids/python.js +212 -0
  262. package/dist/stable-ids/python.js.map +1 -0
  263. package/dist/stable-ids/ts-parse-errors.d.ts +27 -0
  264. package/dist/stable-ids/ts-parse-errors.d.ts.map +1 -0
  265. package/dist/stable-ids/ts-parse-errors.js +75 -0
  266. package/dist/stable-ids/ts-parse-errors.js.map +1 -0
  267. package/dist/stable-ids/types.d.ts +106 -0
  268. package/dist/stable-ids/types.d.ts.map +1 -0
  269. package/dist/stable-ids/types.js +16 -0
  270. package/dist/stable-ids/types.js.map +1 -0
  271. package/dist/stable-ids/typescript.d.ts +30 -0
  272. package/dist/stable-ids/typescript.d.ts.map +1 -0
  273. package/dist/stable-ids/typescript.js +362 -0
  274. package/dist/stable-ids/typescript.js.map +1 -0
  275. package/dist/types/config.d.ts +9 -0
  276. package/dist/types/config.d.ts.map +1 -1
  277. package/dist/types/index.d.ts +35 -0
  278. package/dist/types/index.d.ts.map +1 -1
  279. package/dist/types/index.js.map +1 -1
  280. package/package.json +3 -3
@@ -10456,6 +10456,79 @@ function processStatement2(stmt, startDefId, startUseId, scopeStack, isJavaScrip
10456
10456
  carryBranchDefsOut(popped, scopeStack, alwaysRuns);
10457
10457
  break;
10458
10458
  }
10459
+ case "try_statement":
10460
+ case "try_with_resources_statement": {
10461
+ if (isJavaScript) {
10462
+ const stmtUses = extractUses(stmt, useId, scopeStack, isJavaScript);
10463
+ uses.push(...stmtUses.uses);
10464
+ useId = stmtUses.nextId;
10465
+ break;
10466
+ }
10467
+ scopeStack.push(/* @__PURE__ */ new Map());
10468
+ const resources = stmt.childForFieldName("resources");
10469
+ for (const res of resources?.namedChildren ?? []) {
10470
+ if (!res || res.type !== "resource") continue;
10471
+ const nameNode = res.childForFieldName("name");
10472
+ const value = res.childForFieldName("value");
10473
+ if (value) {
10474
+ const valueUses = extractUses(value, useId, scopeStack, isJavaScript);
10475
+ uses.push(...valueUses.uses);
10476
+ useId = valueUses.nextId;
10477
+ }
10478
+ if (nameNode && nameNode.type === "identifier") {
10479
+ const def = { id: defId++, variable: getNodeText(nameNode), line: nameNode.startPosition.row + 1, column: nameNode.startPosition.column, kind: "local" };
10480
+ defs.push(def);
10481
+ currentScope(scopeStack).set(def.variable, def.id);
10482
+ markDeclared(currentScope(scopeStack), def.variable);
10483
+ } else if (!value) {
10484
+ const resUses = extractUses(res, useId, scopeStack, isJavaScript);
10485
+ uses.push(...resUses.uses);
10486
+ useId = resUses.nextId;
10487
+ }
10488
+ }
10489
+ for (const part of stmt.namedChildren) {
10490
+ if (!part) continue;
10491
+ if (part.type === "block") {
10492
+ const r = processStatement2(part, defId, useId, scopeStack, isJavaScript);
10493
+ defs.push(...r.defs);
10494
+ uses.push(...r.uses);
10495
+ defId = r.nextDefId;
10496
+ useId = r.nextUseId;
10497
+ } else if (part.type === "catch_clause") {
10498
+ scopeStack.push(/* @__PURE__ */ new Map());
10499
+ for (const param of part.namedChildren) {
10500
+ if (!param || param.type !== "catch_formal_parameter") continue;
10501
+ const pname = param.childForFieldName("name");
10502
+ if (pname) {
10503
+ const def = { id: defId++, variable: getNodeText(pname), line: pname.startPosition.row + 1, column: pname.startPosition.column, kind: "local" };
10504
+ defs.push(def);
10505
+ currentScope(scopeStack).set(def.variable, def.id);
10506
+ markDeclared(currentScope(scopeStack), def.variable);
10507
+ }
10508
+ }
10509
+ const body2 = part.childForFieldName("body");
10510
+ if (body2) {
10511
+ const r = processStatement2(body2, defId, useId, scopeStack, isJavaScript);
10512
+ defs.push(...r.defs);
10513
+ uses.push(...r.uses);
10514
+ defId = r.nextDefId;
10515
+ useId = r.nextUseId;
10516
+ }
10517
+ carryBranchDefsOut(scopeStack.pop(), scopeStack, false);
10518
+ } else if (part.type === "finally_clause") {
10519
+ for (const b of part.namedChildren) {
10520
+ if (!b || b.type !== "block") continue;
10521
+ const r = processStatement2(b, defId, useId, scopeStack, isJavaScript);
10522
+ defs.push(...r.defs);
10523
+ uses.push(...r.uses);
10524
+ defId = r.nextDefId;
10525
+ useId = r.nextUseId;
10526
+ }
10527
+ }
10528
+ }
10529
+ carryBranchDefsOut(scopeStack.pop(), scopeStack, false);
10530
+ break;
10531
+ }
10459
10532
  case "else_clause": {
10460
10533
  for (let i2 = 0; i2 < stmt.childCount; i2++) {
10461
10534
  const arm = stmt.child(i2);
@@ -11071,6 +11144,42 @@ function buildBashDFG(tree) {
11071
11144
  defs.push(def);
11072
11145
  currentScope(scopeStack).set(p, def.id);
11073
11146
  }
11147
+ const bashFnDefs = /* @__PURE__ */ new Map();
11148
+ const bashFnByNodeId = /* @__PURE__ */ new Map();
11149
+ for (const fn of findNodes(tree.rootNode, "function_definition")) {
11150
+ const nameNode = fn.childForFieldName("name");
11151
+ if (!nameNode) continue;
11152
+ const fnName = getNodeText(nameNode);
11153
+ if (!fnName || bashFnDefs.has(fnName)) continue;
11154
+ const m = /* @__PURE__ */ new Map();
11155
+ for (const p of positionalParams) {
11156
+ const def = {
11157
+ id: defIdCounter++,
11158
+ variable: p,
11159
+ line: fn.startPosition.row + 1,
11160
+ kind: "param",
11161
+ expression: `bash-fn:${fnName}`
11162
+ };
11163
+ defs.push(def);
11164
+ m.set(p, def.id);
11165
+ }
11166
+ bashFnDefs.set(fnName, m);
11167
+ bashFnByNodeId.set(fn.id, fnName);
11168
+ }
11169
+ const enclosingBashFn = (node) => {
11170
+ let cur = node.parent;
11171
+ while (cur) {
11172
+ if (cur.type === "function_definition") return bashFnByNodeId.get(cur.id) ?? null;
11173
+ cur = cur.parent;
11174
+ }
11175
+ return null;
11176
+ };
11177
+ const positionalDef = (varName, node, fallback) => {
11178
+ if (!positionalParams.includes(varName)) return fallback;
11179
+ const fnName = enclosingBashFn(node);
11180
+ if (!fnName) return fallback;
11181
+ return bashFnDefs.get(fnName)?.get(varName) ?? fallback;
11182
+ };
11074
11183
  walkTree(tree.rootNode, (node) => {
11075
11184
  if (node.type === "variable_assignment") {
11076
11185
  const nameNode = node.childForFieldName("name");
@@ -11123,7 +11232,7 @@ function buildBashDFG(tree) {
11123
11232
  if (varNameNode) {
11124
11233
  const varName = getNodeText(varNameNode);
11125
11234
  if (varName && !varName.startsWith("?") && !varName.startsWith("#")) {
11126
- let reachingDef = findReachingDef(varName, scopeStack);
11235
+ let reachingDef = positionalDef(varName, node, findReachingDef(varName, scopeStack));
11127
11236
  if (reachingDef === null && !positionalParams.includes(varName)) {
11128
11237
  const def = {
11129
11238
  id: defIdCounter++,
@@ -11147,7 +11256,7 @@ function buildBashDFG(tree) {
11147
11256
  const varNameNode = node.namedChildCount > 0 ? node.namedChild(0) : null;
11148
11257
  if (varNameNode && varNameNode.type === "variable_name") {
11149
11258
  const varName = getNodeText(varNameNode);
11150
- let reachingDef = findReachingDef(varName, scopeStack);
11259
+ let reachingDef = positionalDef(varName, node, findReachingDef(varName, scopeStack));
11151
11260
  if (reachingDef === null && !positionalParams.includes(varName)) {
11152
11261
  const def = {
11153
11262
  id: defIdCounter++,
@@ -11169,8 +11278,56 @@ function buildBashDFG(tree) {
11169
11278
  }
11170
11279
  });
11171
11280
  const chains = computeChains(defs, uses);
11281
+ chains.push(...bashCallArgChains(tree, bashFnDefs, uses, positionalParams));
11172
11282
  return { defs, uses, chains };
11173
11283
  }
11284
+ function bashCallArgChains(tree, bashFnDefs, uses, positionalParams) {
11285
+ const chains = [];
11286
+ if (bashFnDefs.size === 0) return chains;
11287
+ const renumbers = /* @__PURE__ */ new Set();
11288
+ for (const fn of findNodes(tree.rootNode, "function_definition")) {
11289
+ const nameNode = fn.childForFieldName("name");
11290
+ if (!nameNode) continue;
11291
+ if (/\bshift\b|\bset\s+--/.test(getNodeText(fn))) renumbers.add(getNodeText(nameNode));
11292
+ }
11293
+ const usesByLine = /* @__PURE__ */ new Map();
11294
+ for (const u of uses) {
11295
+ const l = usesByLine.get(u.line) ?? [];
11296
+ l.push(u);
11297
+ usesByLine.set(u.line, l);
11298
+ }
11299
+ for (const cmd of findNodes(tree.rootNode, "command")) {
11300
+ const nameNode = cmd.childForFieldName("name");
11301
+ if (!nameNode) continue;
11302
+ const fnDefs = bashFnDefs.get(getNodeText(nameNode));
11303
+ if (!fnDefs) continue;
11304
+ const fnName = getNodeText(nameNode);
11305
+ let position = 0;
11306
+ for (let i2 = 0; i2 < cmd.childCount; i2++) {
11307
+ const arg = cmd.child(i2);
11308
+ if (!arg || arg.id === nameNode.id) continue;
11309
+ if (arg.type.includes("redirect") || arg.type === "heredoc_body" || arg.type === "file_descriptor") continue;
11310
+ const text = getNodeText(arg);
11311
+ if (!text.trim()) continue;
11312
+ position++;
11313
+ const names = /* @__PURE__ */ new Set();
11314
+ const re = /\$\{?([A-Za-z_][A-Za-z0-9_]*|[0-9@*])/g;
11315
+ let m;
11316
+ while ((m = re.exec(text)) !== null) names.add(m[1]);
11317
+ if (names.size === 0) continue;
11318
+ const spreads = /\$\{?[@*]/.test(text);
11319
+ const targets = spreads || renumbers.has(fnName) ? positionalParams.map((p) => fnDefs.get(p)).filter((d) => d !== void 0) : [fnDefs.get(String(position))].filter((d) => d !== void 0);
11320
+ const lineUses = usesByLine.get(arg.startPosition.row + 1) ?? [];
11321
+ for (const name2 of names) {
11322
+ for (const u of lineUses) {
11323
+ if (u.variable !== name2 || u.def_id === null) continue;
11324
+ for (const t of targets) chains.push({ from_def: u.def_id, to_def: t, via: name2 });
11325
+ }
11326
+ }
11327
+ }
11328
+ }
11329
+ return chains;
11330
+ }
11174
11331
  function buildRustDFG(tree, cache) {
11175
11332
  const defs = [];
11176
11333
  const uses = [];
@@ -11880,6 +12037,198 @@ function javaReplaceCrLfCoverage(method, rawFrom) {
11880
12037
  return { cr, lf };
11881
12038
  }
11882
12039
 
12040
+ // src/analysis/js-crlf-strip.ts
12041
+ var MAX_PATTERN_LENGTH = 200;
12042
+ var NEUTRAL_AFTER_REPLACE = /* @__PURE__ */ new Set([
12043
+ "replace",
12044
+ "replaceAll",
12045
+ "trim",
12046
+ "trimStart",
12047
+ "trimEnd",
12048
+ "toString",
12049
+ "toLowerCase",
12050
+ "toUpperCase",
12051
+ "slice",
12052
+ "substring"
12053
+ ]);
12054
+ var PROBE = "a\r\rb\n\nc\r\nd\n\re x ";
12055
+ function readQuoted(s, i2) {
12056
+ const q = s[i2];
12057
+ if (q !== '"' && q !== "'" && q !== "`") return null;
12058
+ let out2 = "";
12059
+ for (let j = i2 + 1; j < s.length; j++) {
12060
+ const c = s[j];
12061
+ if (c === q) return { value: out2, end: j + 1 };
12062
+ if (q === "`" && c === "$" && s[j + 1] === "{") return null;
12063
+ if (c === "\\") {
12064
+ const n = s[j + 1];
12065
+ if (n === void 0) return null;
12066
+ switch (n) {
12067
+ case "n":
12068
+ out2 += "\n";
12069
+ j++;
12070
+ continue;
12071
+ case "r":
12072
+ out2 += "\r";
12073
+ j++;
12074
+ continue;
12075
+ case "t":
12076
+ out2 += " ";
12077
+ j++;
12078
+ continue;
12079
+ case "0":
12080
+ out2 += "\0";
12081
+ j++;
12082
+ continue;
12083
+ case "x": {
12084
+ const h = /^[0-9a-fA-F]{2}/.exec(s.slice(j + 2, j + 4));
12085
+ if (!h) return null;
12086
+ out2 += String.fromCharCode(parseInt(h[0], 16));
12087
+ j += 3;
12088
+ continue;
12089
+ }
12090
+ case "u": {
12091
+ const h = /^[0-9a-fA-F]{4}/.exec(s.slice(j + 2, j + 6));
12092
+ if (!h) return null;
12093
+ out2 += String.fromCharCode(parseInt(h[0], 16));
12094
+ j += 5;
12095
+ continue;
12096
+ }
12097
+ default:
12098
+ out2 += n;
12099
+ j++;
12100
+ continue;
12101
+ }
12102
+ }
12103
+ if (c === "\n" && q !== "`") return null;
12104
+ out2 += c;
12105
+ }
12106
+ return null;
12107
+ }
12108
+ function readRegexLiteral(s, i2) {
12109
+ if (s[i2] !== "/") return null;
12110
+ let inClass = false;
12111
+ for (let j = i2 + 1; j < s.length; j++) {
12112
+ const c = s[j];
12113
+ if (c === "\n") return null;
12114
+ if (c === "\\") {
12115
+ j++;
12116
+ continue;
12117
+ }
12118
+ if (inClass) {
12119
+ if (c === "]") inClass = false;
12120
+ continue;
12121
+ }
12122
+ if (c === "[") {
12123
+ inClass = true;
12124
+ continue;
12125
+ }
12126
+ if (c === "/") {
12127
+ const flags2 = /^[dgimsuvy]*/.exec(s.slice(j + 1))[0];
12128
+ return { source: s.slice(i2 + 1, j), flags: flags2, end: j + 1 + flags2.length };
12129
+ }
12130
+ }
12131
+ return null;
12132
+ }
12133
+ var skipWs = (s, i2) => {
12134
+ while (i2 < s.length && /\s/.test(s[i2])) i2++;
12135
+ return i2;
12136
+ };
12137
+ function parseSteps(chainText) {
12138
+ const steps = [];
12139
+ const callRe = /\.\s*(replaceAll|replace)\s*\(/g;
12140
+ let m;
12141
+ while ((m = callRe.exec(chainText)) !== null) {
12142
+ const kind = m[1];
12143
+ let i2 = skipWs(chainText, m.index + m[0].length);
12144
+ let pattern;
12145
+ const rx = readRegexLiteral(chainText, i2);
12146
+ if (rx) {
12147
+ if (rx.source.length > MAX_PATTERN_LENGTH) return null;
12148
+ try {
12149
+ pattern = new RegExp(rx.source, rx.flags);
12150
+ } catch {
12151
+ return null;
12152
+ }
12153
+ i2 = rx.end;
12154
+ } else {
12155
+ const str = readQuoted(chainText, i2);
12156
+ if (!str) return null;
12157
+ pattern = str.value;
12158
+ i2 = str.end;
12159
+ }
12160
+ i2 = skipWs(chainText, i2);
12161
+ if (chainText[i2] !== ",") return null;
12162
+ i2 = skipWs(chainText, i2 + 1);
12163
+ const rep = readQuoted(chainText, i2);
12164
+ if (!rep) return null;
12165
+ i2 = skipWs(chainText, rep.end);
12166
+ if (chainText[i2] !== ")") return null;
12167
+ steps.push({ kind, pattern, replacement: rep.value });
12168
+ callRe.lastIndex = i2 + 1;
12169
+ }
12170
+ return steps;
12171
+ }
12172
+ function run2(steps, input) {
12173
+ let s = input;
12174
+ try {
12175
+ for (const st of steps) {
12176
+ if (st.kind === "replaceAll" && st.pattern instanceof RegExp && !st.pattern.global) return null;
12177
+ s = st.kind === "replaceAll" ? s.replaceAll(st.pattern, st.replacement) : s.replace(st.pattern, st.replacement);
12178
+ }
12179
+ } catch {
12180
+ return null;
12181
+ }
12182
+ return s;
12183
+ }
12184
+ function jsReplaceChainVerdict(chainText) {
12185
+ const steps = parseSteps(chainText);
12186
+ if (!steps || steps.length === 0) return "undecided";
12187
+ const first = chainText.search(/\.\s*(?:replaceAll|replace)\s*\(/);
12188
+ for (const seg of chainText.slice(first + 1).matchAll(/\.\s*([A-Za-z_$][\w$]*)\s*\(/g)) {
12189
+ if (!NEUTRAL_AFTER_REPLACE.has(seg[1])) return "undecided";
12190
+ }
12191
+ const out2 = run2(steps, PROBE);
12192
+ if (out2 === null) return "undecided";
12193
+ return out2.includes("\r") || out2.includes("\n") ? "leaves-crlf" : "strips-crlf";
12194
+ }
12195
+ function jsChainTextAt(sourceLines, line, column) {
12196
+ const text = [(sourceLines[line - 1] ?? "").slice(column), ...sourceLines.slice(line, line + 7)].join("\n");
12197
+ let depth = 0;
12198
+ let prevSig = "";
12199
+ let i2 = 0;
12200
+ for (; i2 < text.length; i2++) {
12201
+ const c = text[i2];
12202
+ if (c === '"' || c === "'" || c === "`") {
12203
+ for (i2++; i2 < text.length && text[i2] !== c; i2++) if (text[i2] === "\\") i2++;
12204
+ prevSig = c;
12205
+ continue;
12206
+ }
12207
+ if (c === "/" && depth > 0 && (prevSig === "(" || prevSig === ",")) {
12208
+ const rx = readRegexLiteral(text, i2);
12209
+ if (rx) {
12210
+ i2 = rx.end - 1;
12211
+ prevSig = "/";
12212
+ continue;
12213
+ }
12214
+ }
12215
+ if (c === "(" || c === "[" || c === "{") {
12216
+ depth++;
12217
+ prevSig = c;
12218
+ continue;
12219
+ }
12220
+ if (c === ")" || c === "]" || c === "}") {
12221
+ if (depth === 0) break;
12222
+ depth--;
12223
+ prevSig = c;
12224
+ continue;
12225
+ }
12226
+ if (depth === 0 && !/[\w$.\s]/.test(c)) break;
12227
+ if (!/\s/.test(c)) prevSig = c;
12228
+ }
12229
+ return text.slice(0, i2);
12230
+ }
12231
+
11883
12232
  // src/analysis/passes/language-sources-pass.ts
11884
12233
  var JINJA_LITERAL_ARG_RE_FRAG = `(?:"[^"\\\\]*"|'[^'\\\\]*')`;
11885
12234
  var JINJA_SAFE_RTS_RE = new RegExp(
@@ -12663,6 +13012,10 @@ var DEFAULT_SOURCES = [
12663
13012
  { method: "get", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
12664
13013
  { method: "hget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
12665
13014
  { method: "mget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
13015
+ // Node redis clients are often bound to `cache` from `createClient()`; class
13016
+ // `cache` is scoped to JS/TS (Python Django cache above) and Map.get is
13017
+ // filtered in sink-filter-pass (#818 src14).
13018
+ { method: "get", class: "cache", type: "db_input", severity: "medium", return_tainted: true, languages: ["javascript", "typescript"] },
12666
13019
  { method: "get", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
12667
13020
  { method: "hget", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
12668
13021
  { method: "mget", class: "Jedis", type: "db_input", severity: "medium", return_tainted: true, languages: ["java"] },
@@ -12842,7 +13195,9 @@ var TRUST_BOUNDARY_FRAMEWORK_SINKS = [
12842
13195
  // on arg[1] (the *Cookie) catches struct-literal composite tainting.
12843
13196
  { method: "SetCookie", class: "http", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["go"] },
12844
13197
  // gin: c.SetCookie(name, value, maxAge, path, domain, secure, httpOnly).
12845
- { method: "SetCookie", class: "Context", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["go"] }
13198
+ { method: "SetCookie", class: "Context", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["go"] },
13199
+ // --- Rust: actix-session / tower-sessions write -------------------------
13200
+ { method: "insert", class: "Session", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["rust"] }
12846
13201
  ];
12847
13202
  var DEFAULT_SINKS = [
12848
13203
  // SQL Injection (CWE-89)
@@ -13503,7 +13858,9 @@ var DEFAULT_SINKS = [
13503
13858
  { method: "addParametersToContext", class: "StaticParametersInterceptor", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
13504
13859
  { method: "build", class: "HttpParameters", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
13505
13860
  // Cron expression parsing (DoS/ReDoS)
13506
- { method: "parse", class: "CronParser", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0] },
13861
+ // cognium-dev#998 — Java cron-utils `CronParser`; scoped to Java so a JS/TS
13862
+ // `parser.parse(...)` (tree-sitter, a lockfile parser) never matches it by name.
13863
+ { method: "parse", class: "CronParser", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0], languages: ["java"] },
13507
13864
  { method: "isValid", class: "CronValidator", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0] },
13508
13865
  // ff4j feature flags
13509
13866
  { method: "check", class: "FF4j", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0] },
@@ -13600,9 +13957,13 @@ var DEFAULT_SINKS = [
13600
13957
  { method: "selectNodes", class: "Node", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0] },
13601
13958
  { method: "selectSingleNode", class: "Node", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0] },
13602
13959
  // XXE - XML External Entity (CWE-611)
13603
- { method: "parse", class: "DocumentBuilder", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
13604
- { method: "parse", class: "SAXParser", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
13605
- { method: "parse", class: "XMLReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
13960
+ // cognium-dev#998 — JAXP parsers are JVM classes. Scoped to Java so the
13961
+ // receiver-name heuristic (`parser` → SAXParser) cannot turn a JS/TS
13962
+ // `parser.parse(text)` into an XXE sink. JS/TS XML parsers (sax, xml2js,
13963
+ // libxmljs, DOMParser) have their own entries.
13964
+ { method: "parse", class: "DocumentBuilder", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["java"] },
13965
+ { method: "parse", class: "SAXParser", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["java"] },
13966
+ { method: "parse", class: "XMLReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["java"] },
13606
13967
  { method: "unmarshal", class: "Unmarshaller", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
13607
13968
  { method: "newTransformer", class: "TransformerFactory", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
13608
13969
  { method: "transform", class: "Transformer", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0] },
@@ -13857,6 +14218,46 @@ var DEFAULT_SINKS = [
13857
14218
  { method: "each", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
13858
14219
  { method: "get", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
13859
14220
  { method: "exec", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], allow_unresolved_receiver: true },
14221
+ // DuckDB from JS/TS: `@duckdb/node-api` (DuckDBConnection), the classic
14222
+ // `duckdb` / `duckdb-async` bindings (Database / Connection) and
14223
+ // `react-native-duckdb` (Database / Transaction). The method names are
14224
+ // generic and the receivers come from factories (`await instance.connect()`,
14225
+ // `open({...})`), so the receiver cannot be typed: the import is the gate
14226
+ // (`requires_import`). Only the SQL text (arg 0) is a sink — values passed
14227
+ // as bound parameters (`run(sql, [v])`, `execute(sql, params)`,
14228
+ // `executeNamed(sql, {k: v})`, `stmt.bind(...)`) are parameterised and do
14229
+ // not reach it.
14230
+ { method: "run", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api", "duckdb", "duckdb-async"] },
14231
+ { method: "prepare", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api", "duckdb", "duckdb-async", "react-native-duckdb"] },
14232
+ { method: "stream", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api", "duckdb", "duckdb-async", "react-native-duckdb"] },
14233
+ { method: "runAndRead", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
14234
+ { method: "runAndReadAll", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
14235
+ { method: "runAndReadUntil", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
14236
+ { method: "streamAndRead", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
14237
+ { method: "streamAndReadAll", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
14238
+ { method: "streamAndReadUntil", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
14239
+ { method: "start", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
14240
+ { method: "startStream", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
14241
+ { method: "extractStatements", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["@duckdb/node-api"] },
14242
+ { method: "all", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
14243
+ { method: "each", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
14244
+ { method: "exec", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
14245
+ { method: "arrowIPCAll", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
14246
+ { method: "arrowIPCStream", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["duckdb", "duckdb-async"] },
14247
+ { method: "execute", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
14248
+ { method: "executeSync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
14249
+ { method: "executeNamed", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
14250
+ { method: "executeSyncNamed", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
14251
+ { method: "streamNamed", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["react-native-duckdb"] },
14252
+ // RxDB (Mango queries, any storage incl. SQLite): a selector or query
14253
+ // object, a field name passed to the query builder, or a `$regex` pattern
14254
+ // under caller control is NoSQL operator injection (CWE-943). Gated on the
14255
+ // `rxdb` import for the same reason as DuckDB above. `find` is already a
14256
+ // classless JS NoSQL sink in the JavaScript plugin.
14257
+ { method: "findOne", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
14258
+ { method: "count", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
14259
+ { method: "where", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
14260
+ { method: "regex", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"], requires_import: ["rxdb"] },
13860
14261
  // TypeORM QueryBuilder raw fragments (CWE-89). TypeORM's raw `.query(sql)`
13861
14262
  // escape hatch is already covered by the classless `query` sink in the JS
13862
14263
  // plugin (getBuiltinSinks), so it is NOT re-registered here — the dedup
@@ -13967,6 +14368,8 @@ var DEFAULT_SINKS = [
13967
14368
  { method: "get", class: "http", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
13968
14369
  { method: "request", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
13969
14370
  { method: "get", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
14371
+ // Node.js dns.promises (CWE-918 / NUL-in-name SSRF class — #895)
14372
+ { method: "resolve", class: "dns.promises", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["javascript", "typescript"] },
13970
14373
  // needle library (used in NodeGoat)
13971
14374
  { method: "get", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
13972
14375
  { method: "post", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
@@ -14461,7 +14864,7 @@ var DEFAULT_SINKS = [
14461
14864
  // receiver-taint or operator-LHS-taint tracking — the format string is the
14462
14865
  // receiver, not an argument. Deferred to Sprint 6 (#86 follow-up).
14463
14866
  // C-style: printf / fprintf / sprintf / snprintf via ctypes/cffi.
14464
- { method: "printf", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["python"] },
14867
+ { method: "printf", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [0], languages: ["python", "rust"] },
14465
14868
  { method: "fprintf", type: "format_string", cwe: "CWE-134", severity: "high", arg_positions: [1], languages: ["python"] },
14466
14869
  // Go: fmt.Sprintf/Printf/Fprintf/Errorf — format string is first/second arg
14467
14870
  { method: "Sprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
@@ -15530,198 +15933,6 @@ function getDefaultConfig() {
15530
15933
  };
15531
15934
  }
15532
15935
 
15533
- // src/analysis/js-crlf-strip.ts
15534
- var MAX_PATTERN_LENGTH = 200;
15535
- var NEUTRAL_AFTER_REPLACE = /* @__PURE__ */ new Set([
15536
- "replace",
15537
- "replaceAll",
15538
- "trim",
15539
- "trimStart",
15540
- "trimEnd",
15541
- "toString",
15542
- "toLowerCase",
15543
- "toUpperCase",
15544
- "slice",
15545
- "substring"
15546
- ]);
15547
- var PROBE = "a\r\rb\n\nc\r\nd\n\re x ";
15548
- function readQuoted(s, i2) {
15549
- const q = s[i2];
15550
- if (q !== '"' && q !== "'" && q !== "`") return null;
15551
- let out2 = "";
15552
- for (let j = i2 + 1; j < s.length; j++) {
15553
- const c = s[j];
15554
- if (c === q) return { value: out2, end: j + 1 };
15555
- if (q === "`" && c === "$" && s[j + 1] === "{") return null;
15556
- if (c === "\\") {
15557
- const n = s[j + 1];
15558
- if (n === void 0) return null;
15559
- switch (n) {
15560
- case "n":
15561
- out2 += "\n";
15562
- j++;
15563
- continue;
15564
- case "r":
15565
- out2 += "\r";
15566
- j++;
15567
- continue;
15568
- case "t":
15569
- out2 += " ";
15570
- j++;
15571
- continue;
15572
- case "0":
15573
- out2 += "\0";
15574
- j++;
15575
- continue;
15576
- case "x": {
15577
- const h = /^[0-9a-fA-F]{2}/.exec(s.slice(j + 2, j + 4));
15578
- if (!h) return null;
15579
- out2 += String.fromCharCode(parseInt(h[0], 16));
15580
- j += 3;
15581
- continue;
15582
- }
15583
- case "u": {
15584
- const h = /^[0-9a-fA-F]{4}/.exec(s.slice(j + 2, j + 6));
15585
- if (!h) return null;
15586
- out2 += String.fromCharCode(parseInt(h[0], 16));
15587
- j += 5;
15588
- continue;
15589
- }
15590
- default:
15591
- out2 += n;
15592
- j++;
15593
- continue;
15594
- }
15595
- }
15596
- if (c === "\n" && q !== "`") return null;
15597
- out2 += c;
15598
- }
15599
- return null;
15600
- }
15601
- function readRegexLiteral(s, i2) {
15602
- if (s[i2] !== "/") return null;
15603
- let inClass = false;
15604
- for (let j = i2 + 1; j < s.length; j++) {
15605
- const c = s[j];
15606
- if (c === "\n") return null;
15607
- if (c === "\\") {
15608
- j++;
15609
- continue;
15610
- }
15611
- if (inClass) {
15612
- if (c === "]") inClass = false;
15613
- continue;
15614
- }
15615
- if (c === "[") {
15616
- inClass = true;
15617
- continue;
15618
- }
15619
- if (c === "/") {
15620
- const flags2 = /^[dgimsuvy]*/.exec(s.slice(j + 1))[0];
15621
- return { source: s.slice(i2 + 1, j), flags: flags2, end: j + 1 + flags2.length };
15622
- }
15623
- }
15624
- return null;
15625
- }
15626
- var skipWs = (s, i2) => {
15627
- while (i2 < s.length && /\s/.test(s[i2])) i2++;
15628
- return i2;
15629
- };
15630
- function parseSteps(chainText) {
15631
- const steps = [];
15632
- const callRe = /\.\s*(replaceAll|replace)\s*\(/g;
15633
- let m;
15634
- while ((m = callRe.exec(chainText)) !== null) {
15635
- const kind = m[1];
15636
- let i2 = skipWs(chainText, m.index + m[0].length);
15637
- let pattern;
15638
- const rx = readRegexLiteral(chainText, i2);
15639
- if (rx) {
15640
- if (rx.source.length > MAX_PATTERN_LENGTH) return null;
15641
- try {
15642
- pattern = new RegExp(rx.source, rx.flags);
15643
- } catch {
15644
- return null;
15645
- }
15646
- i2 = rx.end;
15647
- } else {
15648
- const str = readQuoted(chainText, i2);
15649
- if (!str) return null;
15650
- pattern = str.value;
15651
- i2 = str.end;
15652
- }
15653
- i2 = skipWs(chainText, i2);
15654
- if (chainText[i2] !== ",") return null;
15655
- i2 = skipWs(chainText, i2 + 1);
15656
- const rep = readQuoted(chainText, i2);
15657
- if (!rep) return null;
15658
- i2 = skipWs(chainText, rep.end);
15659
- if (chainText[i2] !== ")") return null;
15660
- steps.push({ kind, pattern, replacement: rep.value });
15661
- callRe.lastIndex = i2 + 1;
15662
- }
15663
- return steps;
15664
- }
15665
- function run2(steps, input) {
15666
- let s = input;
15667
- try {
15668
- for (const st of steps) {
15669
- if (st.kind === "replaceAll" && st.pattern instanceof RegExp && !st.pattern.global) return null;
15670
- s = st.kind === "replaceAll" ? s.replaceAll(st.pattern, st.replacement) : s.replace(st.pattern, st.replacement);
15671
- }
15672
- } catch {
15673
- return null;
15674
- }
15675
- return s;
15676
- }
15677
- function jsReplaceChainVerdict(chainText) {
15678
- const steps = parseSteps(chainText);
15679
- if (!steps || steps.length === 0) return "undecided";
15680
- const first = chainText.search(/\.\s*(?:replaceAll|replace)\s*\(/);
15681
- for (const seg of chainText.slice(first + 1).matchAll(/\.\s*([A-Za-z_$][\w$]*)\s*\(/g)) {
15682
- if (!NEUTRAL_AFTER_REPLACE.has(seg[1])) return "undecided";
15683
- }
15684
- const out2 = run2(steps, PROBE);
15685
- if (out2 === null) return "undecided";
15686
- return out2.includes("\r") || out2.includes("\n") ? "leaves-crlf" : "strips-crlf";
15687
- }
15688
- function jsChainTextAt(sourceLines, line, column) {
15689
- const text = [(sourceLines[line - 1] ?? "").slice(column), ...sourceLines.slice(line, line + 7)].join("\n");
15690
- let depth = 0;
15691
- let prevSig = "";
15692
- let i2 = 0;
15693
- for (; i2 < text.length; i2++) {
15694
- const c = text[i2];
15695
- if (c === '"' || c === "'" || c === "`") {
15696
- for (i2++; i2 < text.length && text[i2] !== c; i2++) if (text[i2] === "\\") i2++;
15697
- prevSig = c;
15698
- continue;
15699
- }
15700
- if (c === "/" && depth > 0 && (prevSig === "(" || prevSig === ",")) {
15701
- const rx = readRegexLiteral(text, i2);
15702
- if (rx) {
15703
- i2 = rx.end - 1;
15704
- prevSig = "/";
15705
- continue;
15706
- }
15707
- }
15708
- if (c === "(" || c === "[" || c === "{") {
15709
- depth++;
15710
- prevSig = c;
15711
- continue;
15712
- }
15713
- if (c === ")" || c === "]" || c === "}") {
15714
- if (depth === 0) break;
15715
- depth--;
15716
- prevSig = c;
15717
- continue;
15718
- }
15719
- if (depth === 0 && !/[\w$.\s]/.test(c)) break;
15720
- if (!/\s/.test(c)) prevSig = c;
15721
- }
15722
- return text.slice(0, i2);
15723
- }
15724
-
15725
15936
  // src/analysis/constant-propagation/patterns.ts
15726
15937
  var TAINT_PATTERNS = [
15727
15938
  // HTTP Sources (Servlet API)
@@ -16111,13 +16322,35 @@ function dropCSharpObjectCarriedWaypoints(sinks, calls) {
16111
16322
  function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy, language, code, imports) {
16112
16323
  const sourceLines = code !== void 0 ? code.split("\n") : void 0;
16113
16324
  const sources = findSources(calls, types, config.sources, sourceLines, language, imports);
16114
- let sinkPatterns = expandPromisifyAliases(config.sinks, sourceLines, language);
16325
+ let sinkPatterns = config.sinks.filter((p) => sinkPatternImportApplies(p, imports));
16326
+ sinkPatterns = expandPromisifyAliases(sinkPatterns, sourceLines, language);
16115
16327
  sinkPatterns = expandIndirectEvalAliases(sinkPatterns, sourceLines, language);
16116
16328
  const sinks = findSinks(calls, sinkPatterns, typeHierarchy, language, sourceLines, types);
16117
16329
  const gatedSinks = language === "csharp" ? dropCSharpObjectCarriedWaypoints(sinks, calls) : sinks;
16118
16330
  const sanitizers = findSanitizers(calls, types, config.sanitizers, sourceLines, language);
16119
16331
  return { sources, sinks: gatedSinks, sanitizers };
16120
16332
  }
16333
+ function isBoundParameterCall(call, pattern, sourceLines) {
16334
+ if (!pattern.requires_import || pattern.type !== "sql_injection") return false;
16335
+ const first = call.arguments[0]?.expression?.trim() ?? "";
16336
+ if (first.startsWith("[") || first.startsWith("{")) return true;
16337
+ const receiver = call.receiver;
16338
+ if (!receiver || !sourceLines || !/^[A-Za-z_$][\w$]*$/.test(receiver)) return false;
16339
+ const assignedFromPrepare = new RegExp(
16340
+ `\\b${receiver.replace(/\$/g, "\\$")}\\s*(?::[^=]+)?=\\s*(?:await\\s+)?[\\w$.]+\\.prepare\\s*\\(`
16341
+ );
16342
+ return sourceLines.some((line) => assignedFromPrepare.test(line));
16343
+ }
16344
+ function sinkPatternImportApplies(pattern, imports) {
16345
+ const required = pattern.requires_import;
16346
+ if (!required || required.length === 0) return true;
16347
+ if (!imports || imports.length === 0) return false;
16348
+ return imports.some((imp) => {
16349
+ const from = imp.from_package;
16350
+ if (!from) return false;
16351
+ return required.some((m) => from === m || from.startsWith(m + "/"));
16352
+ });
16353
+ }
16121
16354
  function sinkPatternAppliesTo(pattern, language) {
16122
16355
  if (language === void 0) return true;
16123
16356
  if (pattern.languages && pattern.languages.length > 0 && !pattern.languages.includes(language)) {
@@ -16535,6 +16768,46 @@ function isInterproceduralTaintableType(typeName, language) {
16535
16768
  const leaf = baseType.split(".").pop() ?? baseType;
16536
16769
  if (csharpTaintable.includes(baseType) || csharpTaintable.includes(leaf)) return true;
16537
16770
  }
16771
+ if (language === "rust") {
16772
+ const trimmed = typeName.trim();
16773
+ const unref = trimmed.replace(/^&mut\s+/, "").replace(/^&\s*/, "");
16774
+ const rustBase = unref.split("<")[0].trim();
16775
+ const rustScalars = [
16776
+ "String",
16777
+ "str",
16778
+ "bool",
16779
+ "char",
16780
+ "i8",
16781
+ "i16",
16782
+ "i32",
16783
+ "i64",
16784
+ "i128",
16785
+ "isize",
16786
+ "u8",
16787
+ "u16",
16788
+ "u32",
16789
+ "u64",
16790
+ "u128",
16791
+ "usize",
16792
+ "f32",
16793
+ "f64"
16794
+ ];
16795
+ if (rustScalars.includes(rustBase)) return true;
16796
+ if (rustBase === "()" || rustBase === "!") return false;
16797
+ if (unref.startsWith("Vec<") || unref.startsWith("Option<") || unref.startsWith("Result<")) {
16798
+ return true;
16799
+ }
16800
+ if (/^[A-Z][A-Za-z0-9_]*$/.test(rustBase)) return true;
16801
+ }
16802
+ if (language === "javascript" || language === "typescript") {
16803
+ const trimmed = typeName.trim();
16804
+ const jsScalars = ["string", "number", "boolean", "bigint", "any", "unknown", "object"];
16805
+ if (jsScalars.includes(baseType)) return true;
16806
+ if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.startsWith("(")) return true;
16807
+ if (/\bRecord\s*</.test(trimmed)) return true;
16808
+ if (["void", "undefined", "null", "never"].includes(baseType)) return false;
16809
+ if (/^[A-Za-z_][\w.$]*$/.test(baseType)) return true;
16810
+ }
16538
16811
  if (typeName.endsWith("[]")) {
16539
16812
  const elementType = typeName.slice(0, -2);
16540
16813
  if (elementType === "String" || elementType === "Object" || elementType === "byte") {
@@ -16649,6 +16922,31 @@ function isSafeGoExecCommandCall(call, pattern, language) {
16649
16922
  return false;
16650
16923
  }
16651
16924
  }
16925
+ if (program === "git") {
16926
+ const subcommandArgPos = pattern.method === "CommandContext" ? 2 : 1;
16927
+ const subcommandArg = call.arguments.find((a) => a.position === subcommandArgPos);
16928
+ let subcommand;
16929
+ if (subcommandArg?.literal !== null && subcommandArg?.literal !== void 0) {
16930
+ subcommand = String(subcommandArg.literal);
16931
+ } else {
16932
+ const expr = (subcommandArg?.expression ?? "").trim();
16933
+ if (expr.startsWith('"') || expr.startsWith("`") || expr.startsWith("'")) {
16934
+ subcommand = expr.slice(1, -1);
16935
+ }
16936
+ }
16937
+ if (subcommand === "ls-remote") {
16938
+ const hasEndOfOptions = call.arguments.some((a) => {
16939
+ if (a.literal !== null && a.literal !== void 0) {
16940
+ return String(a.literal) === "--";
16941
+ }
16942
+ const expr = (a.expression ?? "").trim();
16943
+ return expr === '"--"' || expr === "'--'" || expr === "`--`";
16944
+ });
16945
+ if (!hasEndOfOptions) {
16946
+ return false;
16947
+ }
16948
+ }
16949
+ }
16652
16950
  return true;
16653
16951
  }
16654
16952
  function isSafeJSChildProcessCall(call, pattern, language) {
@@ -17052,6 +17350,29 @@ function isRegexReceiver(receiver, sourceLines) {
17052
17350
  const decl = new RegExp(`(?:const|let|var)\\s+${escapeRe(r)}\\s*=\\s*(?:/|(?:new\\s+)?RegExp\\s*\\()`);
17053
17351
  return sourceLines.some((l) => decl.test(l));
17054
17352
  }
17353
+ function isNavigationReceiverName(receiver) {
17354
+ const r = (receiver ?? "").trim();
17355
+ if (!r) return false;
17356
+ const last = r.slice(r.lastIndexOf(".") + 1).replace(/\(\)$/, "");
17357
+ return /router|history|navigat|^nav$/i.test(last);
17358
+ }
17359
+ function isInMemoryMapGetReceiver(receiver, sourceLines) {
17360
+ const raw = (receiver ?? "").trim();
17361
+ if (!raw || !sourceLines) return false;
17362
+ if (/\.properties$/i.test(raw)) return true;
17363
+ const lastDot = raw.lastIndexOf(".");
17364
+ const r = (lastDot >= 0 ? raw.slice(lastDot + 1) : raw).trim();
17365
+ if (/^(properties|memoryCache)$/i.test(r)) return true;
17366
+ if (!/^[A-Za-z_$][\w$]*$/.test(r)) return false;
17367
+ const n = escapeRe(r);
17368
+ const mapCtor = new RegExp(
17369
+ `\\b${n}\\s*(?::[^=;]*)?=\\s*new\\s+(?:Weak)?Map\\s*(?:<.*>)?\\s*\\(`
17370
+ );
17371
+ const mapType = new RegExp(
17372
+ `\\b${n}\\s*\\??\\s*:\\s*(?:Readonly)?(?:Weak)?Map\\s*<`
17373
+ );
17374
+ return sourceLines.some((l) => mapCtor.test(l) || mapType.test(l));
17375
+ }
17055
17376
  function isDeclaredFunctionReference(arg, sourceLines) {
17056
17377
  if (arg.literal !== null && arg.literal !== void 0) return false;
17057
17378
  if (!sourceLines) return false;
@@ -17256,6 +17577,12 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
17256
17577
  if (isParameterizedQueryCall(call, pattern)) {
17257
17578
  continue;
17258
17579
  }
17580
+ if ((language === "javascript" || language === "typescript") && (pattern.type === "sql_injection" || pattern.type === "nosql_injection") && call.arguments.length === 0 && pattern.arg_positions.every((p) => p >= 0)) {
17581
+ continue;
17582
+ }
17583
+ if (isBoundParameterCall(call, pattern, sourceLines)) {
17584
+ continue;
17585
+ }
17259
17586
  if (isSafePythonSubprocessCall(call, pattern, language)) {
17260
17587
  continue;
17261
17588
  }
@@ -17313,9 +17640,15 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
17313
17640
  if (isArgumentlessJsResponseWriter(call, pattern, language)) {
17314
17641
  continue;
17315
17642
  }
17643
+ if (pattern.type === "open_redirect" && (language === "javascript" || language === "typescript") && (call.method_name === "push" || call.method_name === "replace") && (pattern.class ?? "").toLowerCase() === "router" && !call.receiver_type && !call.receiver_type_fqn && !isNavigationReceiverName(call.receiver)) {
17644
+ continue;
17645
+ }
17316
17646
  if ((pattern.type === "command_injection" || pattern.type === "sql_injection") && call.method_name === "exec" && (language === "javascript" || language === "typescript") && isRegexReceiver(call.receiver, sourceLines)) {
17317
17647
  continue;
17318
17648
  }
17649
+ if (pattern.type === "sql_injection" && call.method_name === "get" && pattern.class === "Connection" && (language === "javascript" || language === "typescript") && isInMemoryMapGetReceiver(call.receiver, sourceLines)) {
17650
+ continue;
17651
+ }
17319
17652
  if (pattern.type === "code_injection" && language === "python" && call.method_name === "compile" && call.receiver && call.receiver !== "builtins") {
17320
17653
  continue;
17321
17654
  }
@@ -17332,7 +17665,7 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
17332
17665
  continue;
17333
17666
  }
17334
17667
  const location = formatCallLocation(call);
17335
- const key = `${location}:${call.location.line}:${pattern.cwe}`;
17668
+ const key = `${location}:${call.location.line}:${call.location.column}:${pattern.cwe}`;
17336
17669
  const confidence = calculateSinkConfidence(call, pattern);
17337
17670
  const existing = sinkMap.get(key);
17338
17671
  if (!existing || confidence > existing.confidence) {
@@ -17343,6 +17676,7 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
17343
17676
  cwe: pattern.cwe,
17344
17677
  location,
17345
17678
  line: call.location.line,
17679
+ callColumn: call.location.column,
17346
17680
  confidence,
17347
17681
  method: call.method_name,
17348
17682
  argPositions: pattern.arg_positions,
@@ -17380,6 +17714,15 @@ function matchesSourcePattern(call, pattern, language, types, imports) {
17380
17714
  return false;
17381
17715
  }
17382
17716
  }
17717
+ if (!pattern.class && pattern.return_tainted && pattern.method && (language === "javascript" || language === "typescript")) {
17718
+ const expressRequestAccessors = /* @__PURE__ */ new Set(["query", "body", "params", "headers", "cookies"]);
17719
+ if (expressRequestAccessors.has(pattern.method)) {
17720
+ const recv = call.receiver?.toLowerCase() ?? "";
17721
+ if (recv !== "req" && recv !== "request" && recv !== "ctx") {
17722
+ return false;
17723
+ }
17724
+ }
17725
+ }
17383
17726
  return pattern.return_tainted === true;
17384
17727
  }
17385
17728
  return false;
@@ -17473,6 +17816,7 @@ var SAFE_RECEIVERS_BY_METHOD = {
17473
17816
  "database",
17474
17817
  "sqlite",
17475
17818
  "conn",
17819
+ "con",
17476
17820
  "connection",
17477
17821
  "client",
17478
17822
  "pool",
@@ -17749,7 +18093,7 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
17749
18093
  }
17750
18094
  }
17751
18095
  }
17752
- if (!pattern.class && call.receiver) {
18096
+ if (!pattern.class && call.receiver && !pattern.requires_import) {
17753
18097
  if (isKnownSafeReceiverForMethod(call.receiver, call.method_name, pattern.type)) {
17754
18098
  return false;
17755
18099
  }
@@ -18482,6 +18826,8 @@ function javaReplaceChainCredit(chainText) {
18482
18826
  let lf = false;
18483
18827
  let lt = false;
18484
18828
  let gt = false;
18829
+ let openBrace = false;
18830
+ let closeBrace = false;
18485
18831
  for (const m of chainText.matchAll(JAVA_REPLACE_ARGS_RE)) {
18486
18832
  const kind = m[1];
18487
18833
  const rawFrom = m[2] ?? m[3] ?? "";
@@ -18492,10 +18838,13 @@ function javaReplaceChainCredit(chainText) {
18492
18838
  if (cov.lf) lf = true;
18493
18839
  if (from === "<" && to.startsWith("&")) lt = true;
18494
18840
  if (from === ">" && to.startsWith("&")) gt = true;
18841
+ if (from === "{") openBrace = true;
18842
+ if (from === "}") closeBrace = true;
18495
18843
  }
18496
18844
  const out2 = [];
18497
18845
  if (cr && lf) out2.push("log_injection");
18498
18846
  if (lt && gt) out2.push("xss");
18847
+ if (openBrace && closeBrace) out2.push("code_injection");
18499
18848
  return out2;
18500
18849
  }
18501
18850
  function matchesSanitizerPattern(call, pattern) {
@@ -20609,6 +20958,9 @@ var ConstantPropagator = class _ConstantPropagator {
20609
20958
  case "assignment_statement":
20610
20959
  this.handleGoAssignment(node);
20611
20960
  return false;
20961
+ case "var_declaration":
20962
+ this.handleGoVarDeclaration(node);
20963
+ return false;
20612
20964
  case "update_expression":
20613
20965
  this.handleUpdateExpression(node);
20614
20966
  return false;
@@ -21016,6 +21368,64 @@ var ConstantPropagator = class _ConstantPropagator {
21016
21368
  * helper that always returns a constant — clears the variable. Anything else stops being tracked here, which is what Go
21017
21369
  * assignments got before this handler existed: the DFG decides.
21018
21370
  */
21371
+ /** `var x = expr` / `var x T = expr` — same clean/taint rules as `:=` (#783). */
21372
+ handleGoVarDeclaration(node) {
21373
+ for (let i2 = 0; i2 < node.childCount; i2++) {
21374
+ const spec = node.child(i2);
21375
+ if (!spec || spec.type !== "var_spec") continue;
21376
+ const nameNode = spec.childForFieldName("name");
21377
+ let valueNode = spec.childForFieldName("value");
21378
+ if (!nameNode || !valueNode) continue;
21379
+ const names = [];
21380
+ if (nameNode.type === "identifier") {
21381
+ names.push(getNodeText2(nameNode, this.source));
21382
+ } else if (nameNode.type === "identifier_list") {
21383
+ for (const c of nameNode.children) {
21384
+ if (c.type === "identifier") names.push(getNodeText2(c, this.source));
21385
+ }
21386
+ }
21387
+ if (valueNode.type === "expression_list") {
21388
+ valueNode = valueNode.children.find((c) => c.type !== "," && c.type !== "comment") ?? valueNode;
21389
+ }
21390
+ if (names.length === 0) continue;
21391
+ const line = getNodeLine(node);
21392
+ for (const varName of names) {
21393
+ if (varName === "_") continue;
21394
+ const scopedName = this.getScopedName(varName);
21395
+ this.goScopedNames.add(scopedName);
21396
+ if (this.isTaintedExpression(valueNode)) {
21397
+ this.goUnknownVars.add(scopedName);
21398
+ this.tainted.add(scopedName);
21399
+ this.sanitizedVars.delete(scopedName);
21400
+ this.sanitizedVarUncovered.delete(scopedName);
21401
+ this.definitionNodes.set(scopedName, valueNode);
21402
+ this.symbols.set(scopedName, createUnknown(line));
21403
+ continue;
21404
+ }
21405
+ if (!this.isGoKnownCleanRhs(valueNode) || this.goUnknownVars.has(scopedName)) {
21406
+ this.goUnknownVars.add(scopedName);
21407
+ this.symbols.delete(scopedName);
21408
+ this.sanitizedVars.delete(scopedName);
21409
+ this.sanitizedVarUncovered.delete(scopedName);
21410
+ if (!this.inConditionalBranch) {
21411
+ this.tainted.delete(scopedName);
21412
+ this.definitionNodes.delete(scopedName);
21413
+ }
21414
+ continue;
21415
+ }
21416
+ if (this.inConditionalBranch) {
21417
+ this.goUnknownVars.add(scopedName);
21418
+ this.symbols.delete(scopedName);
21419
+ continue;
21420
+ }
21421
+ this.tainted.delete(scopedName);
21422
+ this.definitionNodes.delete(scopedName);
21423
+ this.symbols.set(scopedName, this.evaluateExpression(valueNode));
21424
+ this.sanitizedVars.delete(scopedName);
21425
+ this.sanitizedVarUncovered.delete(scopedName);
21426
+ }
21427
+ }
21428
+ }
21019
21429
  handleGoAssignment(node) {
21020
21430
  const left = node.childForFieldName("left");
21021
21431
  const right = node.childForFieldName("right");
@@ -21832,6 +22242,24 @@ var ConstantPropagator = class _ConstantPropagator {
21832
22242
  cache.set(rootId, false);
21833
22243
  return false;
21834
22244
  }
22245
+ /** Is an argument of `call` an inline sanitizer call over tainted input (itself possibly nested)? */
22246
+ argIsSanitizedTaint(call) {
22247
+ const argsNode = call.childForFieldName("arguments");
22248
+ if (!argsNode) return false;
22249
+ for (const arg of argsNode.children) {
22250
+ if (arg.type !== "method_invocation") continue;
22251
+ const name2 = arg.childForFieldName("name");
22252
+ if (!name2 || !SANITIZER_METHODS.has(getNodeText2(name2, this.source))) continue;
22253
+ const inner = arg.childForFieldName("arguments");
22254
+ if (!inner) continue;
22255
+ for (const a of inner.children) {
22256
+ if (a.type === "(" || a.type === ")" || a.type === ",") continue;
22257
+ if (this.isTaintedExpression(a)) return true;
22258
+ }
22259
+ if (this.argIsSanitizedTaint(arg)) return true;
22260
+ }
22261
+ return false;
22262
+ }
21835
22263
  isTaintedExpressionStep(node) {
21836
22264
  const text = getNodeText2(node, this.source);
21837
22265
  if (node.type === "method_invocation") {
@@ -21839,6 +22267,9 @@ var ConstantPropagator = class _ConstantPropagator {
21839
22267
  const objectNode = node.childForFieldName("object");
21840
22268
  if (nameNode) {
21841
22269
  const methodName = getNodeText2(nameNode, this.source);
22270
+ if (ANTI_SANITIZER_METHODS.has(methodName) && this.argIsSanitizedTaint(node)) {
22271
+ return true;
22272
+ }
21842
22273
  if (SANITIZER_METHODS.has(methodName) && !this.localNonSanitizing.has(methodName)) {
21843
22274
  return false;
21844
22275
  }