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
@@ -20,6 +20,7 @@ import { walkBackwardDefs } from '../dfg-walk.js';
20
20
  import { sanitizerCoversSink } from '../sanitizer-index.js';
21
21
  import { HTML_CONTEXT_ENCODER_METHODS } from '../constant-propagation/patterns.js';
22
22
  import { javaCanonicalStartsWithAllBare } from '../java-canonical-prefix.js';
23
+ import { shouldGateInterproceduralParam } from '../entry-point-detection.js';
23
24
  /**
24
25
  * cognium-dev#328 — the const-prop per-step veto, with two scoped exemptions.
25
26
  *
@@ -438,6 +439,40 @@ function indexJavaLocalAssignments(code) {
438
439
  }
439
440
  return index;
440
441
  }
442
+ function goCalleeBaseName(callee) {
443
+ const dot = callee.lastIndexOf('.');
444
+ return dot === -1 ? callee : callee.slice(dot + 1);
445
+ }
446
+ /** cognium-dev#783: callee from a Go `:=` or `var x =` assignment line (unqualified or dotted). */
447
+ function goConstantHelperCalleeOnLine(line) {
448
+ const m = line.match(/:=\s*([\w.]+)\s*\(/) ?? line.match(/\bvar\s+\w+\s*=\s*([\w.]+)\s*\(/);
449
+ if (!m)
450
+ return null;
451
+ return goCalleeBaseName(m[1]);
452
+ }
453
+ /** Same as {@link goConstantHelperCalleeOnLine} when the call spans the next lines after `:=` / `var`. */
454
+ function goConstantHelperCalleeFromLines(lines, lineNumber) {
455
+ const direct = goConstantHelperCalleeOnLine(lines[lineNumber - 1] ?? '');
456
+ if (direct)
457
+ return direct;
458
+ const cur = lines[lineNumber - 1] ?? '';
459
+ const partial = cur.match(/:=\s*([\w.]+)\s*$/) ?? cur.match(/\bvar\s+\w+\s*=\s*([\w.]+)\s*$/);
460
+ if (partial) {
461
+ const next = (lines[lineNumber] ?? '').trimStart();
462
+ if (next.startsWith('('))
463
+ return goCalleeBaseName(partial[1]);
464
+ }
465
+ if (!cur.includes(':=') && !/\bvar\s+\w+\s*=/.test(cur))
466
+ return null;
467
+ if (cur.includes('('))
468
+ return null;
469
+ for (let i = lineNumber + 1; i <= Math.min(lines.length, lineNumber + 4); i++) {
470
+ const m = (lines[i - 1] ?? '').match(/^\s*([\w.]+)\s*\(/);
471
+ if (m)
472
+ return goCalleeBaseName(m[1]);
473
+ }
474
+ return null;
475
+ }
441
476
  function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers) {
442
477
  const lines = typeof code === 'string' ? code.split('\n') : [];
443
478
  const selfReassign = new Map();
@@ -588,8 +623,8 @@ function makeConstPropVeto(constProp, graph, sources, code, language, sanitizers
588
623
  if (step.line === sinkLine &&
589
624
  !constProp.tainted.has(fpLookup)) {
590
625
  for (let ln = sourceLine; ln <= sinkLine; ln++) {
591
- const calleeMatch = (lines[ln - 1] ?? '').match(/:=\s*(\w+)\s*\(/);
592
- if (calleeMatch && constProp.constantReturningMethods?.has(calleeMatch[1])) {
626
+ const callee = goConstantHelperCalleeFromLines(lines, ln);
627
+ if (callee && constProp.constantReturningMethods?.has(callee)) {
593
628
  return true;
594
629
  }
595
630
  }
@@ -908,7 +943,7 @@ export class TaintPropagationPass {
908
943
  }
909
944
  // Supplement: direct parameter-to-sink flows
910
945
  // Sprint 82 (#189) — sink-type-aware dedup (see arrayFlows comment).
911
- const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines, constProp.tainted, ctx.code) ?? [];
946
+ const paramFlows = detectParameterSinkFlows(types, calls, sources, sinks, constProp.unreachableLines, constProp.tainted, ctx.code, ctx.language, graph.ir.meta.file) ?? [];
912
947
  for (const f of paramFlows) {
913
948
  pushIfNew(f);
914
949
  }
@@ -1022,6 +1057,14 @@ export class TaintPropagationPass {
1022
1057
  return false;
1023
1058
  return true;
1024
1059
  });
1060
+ if (ctx.language === 'csharp') {
1061
+ finalFlows = finalFlows.filter((f) => !shouldGateCSharpPatPermsLibraryParamFlow(f, sources, sinks, types, {
1062
+ language: ctx.language,
1063
+ types,
1064
+ filePath: graph.ir.meta.file,
1065
+ calls,
1066
+ }));
1067
+ }
1025
1068
  // Sprint 24 (cognium-dev #102) — uniform line-keyed sanitizer
1026
1069
  // suppression. Drop any flow whose sink_line carries a registered
1027
1070
  // sanitizer that covers the flow's sink_type. Several supplementary
@@ -1713,7 +1756,7 @@ function detectJsModuleScopeFlows(code, sources, sinks, unreachableLines) {
1713
1756
  const stripped = line.replace(/\s+$/, '');
1714
1757
  if (!stripped || /^[ \t]/.test(line))
1715
1758
  continue;
1716
- const m = stripped.match(/^(?:let|var|const)\s+([A-Za-z_$][\w$]*)\b/);
1759
+ const m = stripped.match(/^(?:export\s+)?(?:let|var|const)\s+([A-Za-z_$][\w$]*)\b/);
1717
1760
  if (m)
1718
1761
  moduleVars.add(m[1]);
1719
1762
  }
@@ -1732,7 +1775,7 @@ function detectJsModuleScopeFlows(code, sources, sinks, unreachableLines) {
1732
1775
  if (unreachableLines.has(i + 1))
1733
1776
  continue;
1734
1777
  for (const lhs of moduleVars) {
1735
- const declRe = new RegExp(`\\b(?:let|const|var)\\s+${lhs.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`);
1778
+ const declRe = new RegExp(`\\b(?:export\\s+)?(?:let|const|var)\\s+${lhs.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`);
1736
1779
  if (declRe.test(line))
1737
1780
  continue;
1738
1781
  const m = line.match(new RegExp(`\\b${lhs.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=\\s*([^;]+)`));
@@ -1901,6 +1944,18 @@ function isTaintedInMethod(taintedVars, varName, scopedName, fieldNames, isField
1901
1944
  }
1902
1945
  return true;
1903
1946
  }
1947
+ function mapGetLiteralKeyFromExpr(expr) {
1948
+ const m = expr.match(/\b([A-Za-z_]\w*)\s*\.\s*get\s*\(\s*"([^"]*)"\s*\)/);
1949
+ return m ? { map: m[1], key: m[2] } : null;
1950
+ }
1951
+ function isMapLiteralKeyTainted(constProp, mapName, key, inMethod) {
1952
+ const scoped = inMethod ? `${inMethod}:${mapName}` : mapName;
1953
+ const keys = constProp.taintedCollections.get(mapName) ??
1954
+ constProp.taintedCollections.get(scoped);
1955
+ if (!keys || keys.size === 0)
1956
+ return false;
1957
+ return keys.has(key) || keys.has('*');
1958
+ }
1904
1959
  function detectCollectionFlows(calls, sources, sinks, constProp, code, types, graph) {
1905
1960
  const taintedVars = constProp.tainted;
1906
1961
  const unreachableLines = constProp.unreachableLines;
@@ -1944,7 +1999,12 @@ function detectCollectionFlows(calls, sources, sinks, constProp, code, types, gr
1944
1999
  && expr === varName
1945
2000
  && !methodHasLocalDecl(code, call.in_method, varName, call.location.line);
1946
2001
  const isFieldUse = explicitField || bareFieldRead;
1947
- if (isTaintedInMethod(taintedVars, varName, scopedName, fieldNames, isFieldUse)) {
2002
+ const mapKeyRead = mapGetLiteralKeyFromExpr(expr);
2003
+ let argTainted = isTaintedInMethod(taintedVars, varName, scopedName, fieldNames, isFieldUse);
2004
+ if (mapKeyRead && mapKeyRead.map === varName) {
2005
+ argTainted = isMapLiteralKeyTainted(constProp, varName, mapKeyRead.key, call.in_method);
2006
+ }
2007
+ if (argTainted) {
1948
2008
  // Sprint 13 #70 — pick a source in the same method scope as the
1949
2009
  // sink, not blanket sources[0]. The varName-based match is
1950
2010
  // primary; method-scope is the tiebreaker; sources[0] is the
@@ -1999,12 +2059,19 @@ function detectCollectionFlows(calls, sources, sinks, constProp, code, types, gr
1999
2059
  { method: 'peek', re: /(\w+)\.peek\(/ },
2000
2060
  { method: 'toArray', re: /(\w+)\.toArray\(/ },
2001
2061
  ];
2002
- for (const { re } of collectionPatterns) {
2062
+ for (const { method, re } of collectionPatterns) {
2003
2063
  const match = expr.match(re);
2004
2064
  if (match) {
2005
2065
  const collectionVar = match[1];
2006
2066
  const scopedCollection = call.in_method ? `${call.in_method}:${collectionVar}` : collectionVar;
2007
- if (taintedVars.has(collectionVar) || taintedVars.has(scopedCollection)) {
2067
+ const mapKey = method === 'get' ? mapGetLiteralKeyFromExpr(expr) : null;
2068
+ let collectionTainted = taintedVars.has(collectionVar) || taintedVars.has(scopedCollection);
2069
+ if (mapKey && mapKey.map === collectionVar) {
2070
+ collectionTainted = isMapLiteralKeyTainted(constProp, collectionVar, mapKey.key, call.in_method);
2071
+ }
2072
+ if (!collectionTainted)
2073
+ continue;
2074
+ {
2008
2075
  // Sprint 13 #70 — same method-scoped picker as above.
2009
2076
  const source = pickScopedSource(sources, sink.line, call.in_method ?? null, types, collectionVar);
2010
2077
  if (source) {
@@ -2090,6 +2157,40 @@ function detectArrayElementFlows(calls, sources, sinks, taintedArrayElements, un
2090
2157
  }
2091
2158
  return flows;
2092
2159
  }
2160
+ /**
2161
+ * cognium-dev #816 — PAT-PERMS-01: suppress only direct library-parameter →
2162
+ * FileStream path_traversal on non-entry methods. Derived paths (Path.Combine,
2163
+ * GetFullPath chains) and other sinks stay gated only by sanitizer / DFG logic.
2164
+ */
2165
+ function csharpPatPermsDirectParamSink(sink, paramVar) {
2166
+ const method = (sink.method ?? '').replace(/^.*\./, '');
2167
+ if (method !== 'FileStream')
2168
+ return false;
2169
+ const code = sink.code ?? '';
2170
+ if (!code)
2171
+ return true;
2172
+ const escaped = paramVar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2173
+ return new RegExp(`\\bFileStream\\s*\\(\\s*${escaped}\\b`).test(code);
2174
+ }
2175
+ function shouldGateCSharpPatPermsLibraryParamFlow(flow, sources, sinks, types, ctx) {
2176
+ if (flow.source_type !== 'interprocedural_param' || flow.sink_type !== 'path_traversal') {
2177
+ return false;
2178
+ }
2179
+ const srcVar = flow.path?.[0]?.variable;
2180
+ const sinkVar = flow.path?.[flow.path.length - 1]?.variable;
2181
+ if (!srcVar || srcVar !== sinkVar)
2182
+ return false;
2183
+ const sink = sinks.find((s) => s.line === flow.sink_line && s.type === 'path_traversal');
2184
+ if (!sink || !csharpPatPermsDirectParamSink(sink, srcVar))
2185
+ return false;
2186
+ const src = sources.find((s) => s.line === flow.source_line && s.type === 'interprocedural_param');
2187
+ const methodName = src?.in_method;
2188
+ if (!methodName)
2189
+ return false;
2190
+ const enclosingType = types.find((t) => t.methods.some((m) => m.name === methodName));
2191
+ const enclosingMethod = enclosingType?.methods.find((m) => m.name === methodName);
2192
+ return shouldGateInterproceduralParam(flow.source_type, enclosingMethod, enclosingType, ctx);
2193
+ }
2093
2194
  /** Parameter names referenced as identifiers inside a compound sink argument (#685). */
2094
2195
  function compoundArgParamNames(expression, paramNames) {
2095
2196
  const stripped = expression.replace(/(['"`])(?:\\.|(?!\1).)*\1/g, (m) => ' '.repeat(m.length));
@@ -2104,7 +2205,7 @@ function compoundArgParamNames(expression, paramNames) {
2104
2205
  }
2105
2206
  return matched;
2106
2207
  }
2107
- function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines, tainted, code) {
2208
+ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines, tainted, code, language, filePath) {
2108
2209
  const flows = [];
2109
2210
  const paramSourcesByMethod = new Map();
2110
2211
  for (const source of sources) {
@@ -2154,6 +2255,16 @@ function detectParameterSinkFlows(types, calls, sources, sinks, unreachableLines
2154
2255
  for (const varName of varNames) {
2155
2256
  const paramSource = methodParamSources.get(varName);
2156
2257
  if (paramSource) {
2258
+ if (language === 'csharp' &&
2259
+ paramSource.type === 'interprocedural_param' &&
2260
+ sink.type === 'path_traversal' &&
2261
+ csharpPatPermsDirectParamSink(sink, varName)) {
2262
+ const enclosingType = types.find((t) => t.methods.some((m) => m.name === methodName));
2263
+ const enclosingMethod = enclosingType?.methods.find((m) => m.name === methodName);
2264
+ if (shouldGateInterproceduralParam(paramSource.type, enclosingMethod, enclosingType, { language, types, filePath, calls })) {
2265
+ continue;
2266
+ }
2267
+ }
2157
2268
  // Sprint 82 (#189) — sink-type-aware dedup so a sink registered
2158
2269
  // under multiple types at the same call site (e.g. `redirect` as
2159
2270
  // both `open_redirect` and `crlf`) emits a flow for each type.
@@ -3660,10 +3771,22 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
3660
3771
  realSourceByVar.set(s.variable, s);
3661
3772
  }
3662
3773
  for (const [varName, derivedLine] of derived) {
3663
- if (!varName || existingVars.has(varName))
3774
+ if (!varName)
3664
3775
  continue;
3665
3776
  const owner = methodAt(derivedLine);
3666
3777
  const fieldAlias = isDeclaredField(derivedLine, varName);
3778
+ if (existingVars.has(varName)) {
3779
+ if (!fieldAlias)
3780
+ continue;
3781
+ // cognium-dev#626: a bean setter's `interprocedural_param` stub must not
3782
+ // block the declared-field alias derived from a tainted `setFoo(...)` /
3783
+ // `this.foo =` write — otherwise sinks in other methods never see `foo`.
3784
+ const stubIdx = sourcesWithVar.findIndex(s => s.variable === varName && s.type === 'interprocedural_param');
3785
+ if (stubIdx < 0)
3786
+ continue;
3787
+ sourcesWithVar.splice(stubIdx, 1);
3788
+ existingVars.delete(varName);
3789
+ }
3667
3790
  let scopedAnchor = anchor;
3668
3791
  if (owner) {
3669
3792
  let best;