circle-ir 4.16.1 → 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 (295) 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/index.d.ts +2 -0
  17. package/dist/analysis/constant-propagation/index.d.ts.map +1 -1
  18. package/dist/analysis/constant-propagation/index.js +10 -0
  19. package/dist/analysis/constant-propagation/index.js.map +1 -1
  20. package/dist/analysis/constant-propagation/propagator.d.ts +15 -0
  21. package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
  22. package/dist/analysis/constant-propagation/propagator.js +224 -20
  23. package/dist/analysis/constant-propagation/propagator.js.map +1 -1
  24. package/dist/analysis/constant-propagation/types.d.ts +2 -0
  25. package/dist/analysis/constant-propagation/types.d.ts.map +1 -1
  26. package/dist/analysis/constant-propagation.d.ts +1 -1
  27. package/dist/analysis/constant-propagation.d.ts.map +1 -1
  28. package/dist/analysis/constant-propagation.js +1 -1
  29. package/dist/analysis/constant-propagation.js.map +1 -1
  30. package/dist/analysis/entry-point-detection.d.ts.map +1 -1
  31. package/dist/analysis/entry-point-detection.js +72 -0
  32. package/dist/analysis/entry-point-detection.js.map +1 -1
  33. package/dist/analysis/interprocedural.d.ts.map +1 -1
  34. package/dist/analysis/interprocedural.js +75 -8
  35. package/dist/analysis/interprocedural.js.map +1 -1
  36. package/dist/analysis/maven-declared.d.ts +72 -0
  37. package/dist/analysis/maven-declared.d.ts.map +1 -0
  38. package/dist/analysis/maven-declared.js +216 -0
  39. package/dist/analysis/maven-declared.js.map +1 -0
  40. package/dist/analysis/maven-version.d.ts +28 -0
  41. package/dist/analysis/maven-version.d.ts.map +1 -0
  42. package/dist/analysis/maven-version.js +172 -0
  43. package/dist/analysis/maven-version.js.map +1 -0
  44. package/dist/analysis/passes/constant-propagation-pass.d.ts.map +1 -1
  45. package/dist/analysis/passes/constant-propagation-pass.js +29 -1
  46. package/dist/analysis/passes/constant-propagation-pass.js.map +1 -1
  47. package/dist/analysis/passes/cross-file-pass.d.ts.map +1 -1
  48. package/dist/analysis/passes/cross-file-pass.js +84 -2
  49. package/dist/analysis/passes/cross-file-pass.js.map +1 -1
  50. package/dist/analysis/passes/csharp-sanitizer-credit.d.ts +7 -0
  51. package/dist/analysis/passes/csharp-sanitizer-credit.d.ts.map +1 -1
  52. package/dist/analysis/passes/csharp-sanitizer-credit.js +44 -1
  53. package/dist/analysis/passes/csharp-sanitizer-credit.js.map +1 -1
  54. package/dist/analysis/passes/insecure-cookie-pass.d.ts +2 -0
  55. package/dist/analysis/passes/insecure-cookie-pass.d.ts.map +1 -1
  56. package/dist/analysis/passes/insecure-cookie-pass.js +25 -3
  57. package/dist/analysis/passes/insecure-cookie-pass.js.map +1 -1
  58. package/dist/analysis/passes/interprocedural-pass.d.ts +11 -14
  59. package/dist/analysis/passes/interprocedural-pass.d.ts.map +1 -1
  60. package/dist/analysis/passes/interprocedural-pass.js +562 -0
  61. package/dist/analysis/passes/interprocedural-pass.js.map +1 -1
  62. package/dist/analysis/passes/language-sources-pass.d.ts +18 -1
  63. package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
  64. package/dist/analysis/passes/language-sources-pass.js +1079 -14
  65. package/dist/analysis/passes/language-sources-pass.js.map +1 -1
  66. package/dist/analysis/passes/param-source-flow-gate-pass.d.ts.map +1 -1
  67. package/dist/analysis/passes/param-source-flow-gate-pass.js +82 -3
  68. package/dist/analysis/passes/param-source-flow-gate-pass.js.map +1 -1
  69. package/dist/analysis/passes/sink-filter-pass.d.ts +44 -2
  70. package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
  71. package/dist/analysis/passes/sink-filter-pass.js +690 -12
  72. package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
  73. package/dist/analysis/passes/taint-propagation-pass.d.ts.map +1 -1
  74. package/dist/analysis/passes/taint-propagation-pass.js +298 -19
  75. package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
  76. package/dist/analysis/passes/tls-verify-disabled-pass.d.ts +18 -0
  77. package/dist/analysis/passes/tls-verify-disabled-pass.d.ts.map +1 -1
  78. package/dist/analysis/passes/tls-verify-disabled-pass.js +161 -0
  79. package/dist/analysis/passes/tls-verify-disabled-pass.js.map +1 -1
  80. package/dist/analysis/passes/weak-random-pass.d.ts +6 -0
  81. package/dist/analysis/passes/weak-random-pass.d.ts.map +1 -1
  82. package/dist/analysis/passes/weak-random-pass.js +29 -0
  83. package/dist/analysis/passes/weak-random-pass.js.map +1 -1
  84. package/dist/analysis/promote-cross-file-flows.d.ts +17 -0
  85. package/dist/analysis/promote-cross-file-flows.d.ts.map +1 -0
  86. package/dist/analysis/promote-cross-file-flows.js +32 -0
  87. package/dist/analysis/promote-cross-file-flows.js.map +1 -0
  88. package/dist/analysis/rust-literal-guard.d.ts.map +1 -1
  89. package/dist/analysis/rust-literal-guard.js +100 -1
  90. package/dist/analysis/rust-literal-guard.js.map +1 -1
  91. package/dist/analysis/sbom.d.ts +100 -0
  92. package/dist/analysis/sbom.d.ts.map +1 -1
  93. package/dist/analysis/sbom.js +514 -3
  94. package/dist/analysis/sbom.js.map +1 -1
  95. package/dist/analysis/semver-version.d.ts +30 -0
  96. package/dist/analysis/semver-version.d.ts.map +1 -0
  97. package/dist/analysis/semver-version.js +76 -0
  98. package/dist/analysis/semver-version.js.map +1 -0
  99. package/dist/analysis/taint-matcher.d.ts +5 -0
  100. package/dist/analysis/taint-matcher.d.ts.map +1 -1
  101. package/dist/analysis/taint-matcher.js +226 -4
  102. package/dist/analysis/taint-matcher.js.map +1 -1
  103. package/dist/analyzer.d.ts +57 -0
  104. package/dist/analyzer.d.ts.map +1 -1
  105. package/dist/analyzer.js +69 -6
  106. package/dist/analyzer.js.map +1 -1
  107. package/dist/browser/circle-ir.js +3360 -314
  108. package/dist/core/circle-ir-core.cjs +783 -220
  109. package/dist/core/circle-ir-core.js +783 -220
  110. package/dist/core/extractors/dfg.js +205 -2
  111. package/dist/core/extractors/dfg.js.map +1 -1
  112. package/dist/core/extractors/ts-declaration-detail.d.ts +39 -0
  113. package/dist/core/extractors/ts-declaration-detail.d.ts.map +1 -0
  114. package/dist/core/extractors/ts-declaration-detail.js +266 -0
  115. package/dist/core/extractors/ts-declaration-detail.js.map +1 -0
  116. package/dist/core/extractors/types.js +53 -9
  117. package/dist/core/extractors/types.js.map +1 -1
  118. package/dist/graph/analysis-pass.d.ts +4 -1
  119. package/dist/graph/analysis-pass.d.ts.map +1 -1
  120. package/dist/graph/analysis-pass.js +4 -1
  121. package/dist/graph/analysis-pass.js.map +1 -1
  122. package/dist/graph-facts/dead-code.d.ts +62 -0
  123. package/dist/graph-facts/dead-code.d.ts.map +1 -0
  124. package/dist/graph-facts/dead-code.js +90 -0
  125. package/dist/graph-facts/dead-code.js.map +1 -0
  126. package/dist/graph-facts/dependency-calls.d.ts +86 -0
  127. package/dist/graph-facts/dependency-calls.d.ts.map +1 -0
  128. package/dist/graph-facts/dependency-calls.js +93 -0
  129. package/dist/graph-facts/dependency-calls.js.map +1 -0
  130. package/dist/graph-facts/index.d.ts +10 -0
  131. package/dist/graph-facts/index.d.ts.map +1 -0
  132. package/dist/graph-facts/index.js +10 -0
  133. package/dist/graph-facts/index.js.map +1 -0
  134. package/dist/graph-facts/maven-packages.d.ts +42 -0
  135. package/dist/graph-facts/maven-packages.d.ts.map +1 -0
  136. package/dist/graph-facts/maven-packages.js +133 -0
  137. package/dist/graph-facts/maven-packages.js.map +1 -0
  138. package/dist/graph-facts/reach.d.ts +203 -0
  139. package/dist/graph-facts/reach.d.ts.map +1 -0
  140. package/dist/graph-facts/reach.js +219 -0
  141. package/dist/graph-facts/reach.js.map +1 -0
  142. package/dist/graph-facts/removal-reach.d.ts +73 -0
  143. package/dist/graph-facts/removal-reach.d.ts.map +1 -0
  144. package/dist/graph-facts/removal-reach.js +64 -0
  145. package/dist/graph-facts/removal-reach.js.map +1 -0
  146. package/dist/index.d.ts +21 -6
  147. package/dist/index.d.ts.map +1 -1
  148. package/dist/index.js +20 -3
  149. package/dist/index.js.map +1 -1
  150. package/dist/navigation/go-graph.d.ts +68 -0
  151. package/dist/navigation/go-graph.d.ts.map +1 -0
  152. package/dist/navigation/go-graph.js +753 -0
  153. package/dist/navigation/go-graph.js.map +1 -0
  154. package/dist/navigation/index.d.ts +7 -1
  155. package/dist/navigation/index.d.ts.map +1 -1
  156. package/dist/navigation/index.js +6 -0
  157. package/dist/navigation/index.js.map +1 -1
  158. package/dist/navigation/java-entry-tables.d.ts +43 -0
  159. package/dist/navigation/java-entry-tables.d.ts.map +1 -0
  160. package/dist/navigation/java-entry-tables.js +56 -0
  161. package/dist/navigation/java-entry-tables.js.map +1 -0
  162. package/dist/navigation/java-graph.d.ts +64 -0
  163. package/dist/navigation/java-graph.d.ts.map +1 -0
  164. package/dist/navigation/java-graph.js +466 -0
  165. package/dist/navigation/java-graph.js.map +1 -0
  166. package/dist/navigation/navigation-index.d.ts +91 -2
  167. package/dist/navigation/navigation-index.d.ts.map +1 -1
  168. package/dist/navigation/navigation-index.js +372 -45
  169. package/dist/navigation/navigation-index.js.map +1 -1
  170. package/dist/navigation/python-graph.d.ts +73 -0
  171. package/dist/navigation/python-graph.d.ts.map +1 -0
  172. package/dist/navigation/python-graph.js +867 -0
  173. package/dist/navigation/python-graph.js.map +1 -0
  174. package/dist/navigation/ts-call-edges.d.ts +193 -0
  175. package/dist/navigation/ts-call-edges.d.ts.map +1 -0
  176. package/dist/navigation/ts-call-edges.js +2089 -0
  177. package/dist/navigation/ts-call-edges.js.map +1 -0
  178. package/dist/navigation/ts-entry-tables.d.ts +88 -0
  179. package/dist/navigation/ts-entry-tables.d.ts.map +1 -0
  180. package/dist/navigation/ts-entry-tables.js +50 -0
  181. package/dist/navigation/ts-entry-tables.js.map +1 -0
  182. package/dist/path-set/branches.d.ts +52 -0
  183. package/dist/path-set/branches.d.ts.map +1 -0
  184. package/dist/path-set/branches.js +124 -0
  185. package/dist/path-set/branches.js.map +1 -0
  186. package/dist/path-set/finding-diff.d.ts +63 -0
  187. package/dist/path-set/finding-diff.d.ts.map +1 -0
  188. package/dist/path-set/finding-diff.js +150 -0
  189. package/dist/path-set/finding-diff.js.map +1 -0
  190. package/dist/path-set/index.d.ts +18 -0
  191. package/dist/path-set/index.d.ts.map +1 -0
  192. package/dist/path-set/index.js +12 -0
  193. package/dist/path-set/index.js.map +1 -0
  194. package/dist/path-set/line-diff.d.ts +19 -0
  195. package/dist/path-set/line-diff.d.ts.map +1 -0
  196. package/dist/path-set/line-diff.js +98 -0
  197. package/dist/path-set/line-diff.js.map +1 -0
  198. package/dist/path-set/path-set.d.ts +88 -0
  199. package/dist/path-set/path-set.d.ts.map +1 -0
  200. package/dist/path-set/path-set.js +716 -0
  201. package/dist/path-set/path-set.js.map +1 -0
  202. package/dist/path-set/templates.d.ts +42 -0
  203. package/dist/path-set/templates.d.ts.map +1 -0
  204. package/dist/path-set/templates.js +119 -0
  205. package/dist/path-set/templates.js.map +1 -0
  206. package/dist/path-set/types.d.ts +195 -0
  207. package/dist/path-set/types.d.ts.map +1 -0
  208. package/dist/path-set/types.js +8 -0
  209. package/dist/path-set/types.js.map +1 -0
  210. package/dist/remedy-class/index.d.ts +12 -0
  211. package/dist/remedy-class/index.d.ts.map +1 -0
  212. package/dist/remedy-class/index.js +9 -0
  213. package/dist/remedy-class/index.js.map +1 -0
  214. package/dist/remedy-class/match.d.ts +165 -0
  215. package/dist/remedy-class/match.d.ts.map +1 -0
  216. package/dist/remedy-class/match.js +705 -0
  217. package/dist/remedy-class/match.js.map +1 -0
  218. package/dist/remedy-class/output-context.d.ts +25 -0
  219. package/dist/remedy-class/output-context.d.ts.map +1 -0
  220. package/dist/remedy-class/output-context.js +150 -0
  221. package/dist/remedy-class/output-context.js.map +1 -0
  222. package/dist/remedy-class/sha256.d.ts +18 -0
  223. package/dist/remedy-class/sha256.d.ts.map +1 -0
  224. package/dist/remedy-class/sha256.js +89 -0
  225. package/dist/remedy-class/sha256.js.map +1 -0
  226. package/dist/remedy-class/table.d.ts +130 -0
  227. package/dist/remedy-class/table.d.ts.map +1 -0
  228. package/dist/remedy-class/table.js +457 -0
  229. package/dist/remedy-class/table.js.map +1 -0
  230. package/dist/resolution/cross-file.d.ts +18 -0
  231. package/dist/resolution/cross-file.d.ts.map +1 -1
  232. package/dist/resolution/cross-file.js +194 -5
  233. package/dist/resolution/cross-file.js.map +1 -1
  234. package/dist/stable-ids/builder.d.ts +20 -0
  235. package/dist/stable-ids/builder.d.ts.map +1 -0
  236. package/dist/stable-ids/builder.js +65 -0
  237. package/dist/stable-ids/builder.js.map +1 -0
  238. package/dist/stable-ids/diff.d.ts +18 -0
  239. package/dist/stable-ids/diff.d.ts.map +1 -0
  240. package/dist/stable-ids/diff.js +37 -0
  241. package/dist/stable-ids/diff.js.map +1 -0
  242. package/dist/stable-ids/go.d.ts +28 -0
  243. package/dist/stable-ids/go.d.ts.map +1 -0
  244. package/dist/stable-ids/go.js +160 -0
  245. package/dist/stable-ids/go.js.map +1 -0
  246. package/dist/stable-ids/hash.d.ts +22 -0
  247. package/dist/stable-ids/hash.d.ts.map +1 -0
  248. package/dist/stable-ids/hash.js +167 -0
  249. package/dist/stable-ids/hash.js.map +1 -0
  250. package/dist/stable-ids/index.d.ts +30 -0
  251. package/dist/stable-ids/index.d.ts.map +1 -0
  252. package/dist/stable-ids/index.js +59 -0
  253. package/dist/stable-ids/index.js.map +1 -0
  254. package/dist/stable-ids/java.d.ts +28 -0
  255. package/dist/stable-ids/java.d.ts.map +1 -0
  256. package/dist/stable-ids/java.js +224 -0
  257. package/dist/stable-ids/java.js.map +1 -0
  258. package/dist/stable-ids/moves.d.ts +87 -0
  259. package/dist/stable-ids/moves.d.ts.map +1 -0
  260. package/dist/stable-ids/moves.js +222 -0
  261. package/dist/stable-ids/moves.js.map +1 -0
  262. package/dist/stable-ids/nodes.d.ts +32 -0
  263. package/dist/stable-ids/nodes.d.ts.map +1 -0
  264. package/dist/stable-ids/nodes.js +92 -0
  265. package/dist/stable-ids/nodes.js.map +1 -0
  266. package/dist/stable-ids/normalize.d.ts +35 -0
  267. package/dist/stable-ids/normalize.d.ts.map +1 -0
  268. package/dist/stable-ids/normalize.js +174 -0
  269. package/dist/stable-ids/normalize.js.map +1 -0
  270. package/dist/stable-ids/parse-errors.d.ts +24 -0
  271. package/dist/stable-ids/parse-errors.d.ts.map +1 -0
  272. package/dist/stable-ids/parse-errors.js +46 -0
  273. package/dist/stable-ids/parse-errors.js.map +1 -0
  274. package/dist/stable-ids/python.d.ts +27 -0
  275. package/dist/stable-ids/python.d.ts.map +1 -0
  276. package/dist/stable-ids/python.js +212 -0
  277. package/dist/stable-ids/python.js.map +1 -0
  278. package/dist/stable-ids/ts-parse-errors.d.ts +27 -0
  279. package/dist/stable-ids/ts-parse-errors.d.ts.map +1 -0
  280. package/dist/stable-ids/ts-parse-errors.js +75 -0
  281. package/dist/stable-ids/ts-parse-errors.js.map +1 -0
  282. package/dist/stable-ids/types.d.ts +106 -0
  283. package/dist/stable-ids/types.d.ts.map +1 -0
  284. package/dist/stable-ids/types.js +16 -0
  285. package/dist/stable-ids/types.js.map +1 -0
  286. package/dist/stable-ids/typescript.d.ts +30 -0
  287. package/dist/stable-ids/typescript.d.ts.map +1 -0
  288. package/dist/stable-ids/typescript.js +362 -0
  289. package/dist/stable-ids/typescript.js.map +1 -0
  290. package/dist/types/config.d.ts +9 -0
  291. package/dist/types/config.d.ts.map +1 -1
  292. package/dist/types/index.d.ts +53 -0
  293. package/dist/types/index.d.ts.map +1 -1
  294. package/dist/types/index.js.map +1 -1
  295. package/package.json +3 -3
@@ -63,6 +63,11 @@ export class ConstantPropagator {
63
63
  taintedArrayElements = new Map();
64
64
  // Track current method name for scoping local variables
65
65
  currentMethod = null;
66
+ /** Start lines of nested `{ ... }` blocks — disambiguate same-named locals (#476 Collections10). */
67
+ blockScopeStack = [];
68
+ lineBlockScopes = new Map();
69
+ /** Skip block-scope push on the method/constructor body `{ ... }` (#476). */
70
+ skipMethodBodyBlockScope = false;
66
71
  // Track conditional taints: which variables were tainted under which conditions
67
72
  // Maps condition expression string → set of variables tainted under that condition
68
73
  conditionalTaints = new Map();
@@ -76,6 +81,8 @@ export class ConstantPropagator {
76
81
  inSynchronizedBlock = false;
77
82
  // Track iterator sources: iterator variable name → collection name it was created from
78
83
  iteratorSources = new Map();
84
+ /** Vars assigned from `iter.next()` — do not retro-clear on later iterator rebind (#476). */
85
+ taintedFromIteratorNext = new Set();
79
86
  // Track class field names (declared at class level, not local variables)
80
87
  classFields = new Set();
81
88
  // Track tainted method parameters for inter-procedural analysis
@@ -133,12 +140,16 @@ export class ConstantPropagator {
133
140
  this.sanitizedVars.clear();
134
141
  this.sanitizedVarUncovered.clear();
135
142
  this.currentMethod = null;
143
+ this.blockScopeStack = [];
144
+ this.lineBlockScopes.clear();
145
+ this.skipMethodBodyBlockScope = false;
136
146
  this.conditionalTaints.clear();
137
147
  this.conditionStack = [];
138
148
  this.lineConditions.clear();
139
149
  this.synchronizedLines.clear();
140
150
  this.inSynchronizedBlock = false;
141
151
  this.iteratorSources.clear();
152
+ this.taintedFromIteratorNext.clear();
142
153
  this.classFields.clear();
143
154
  this.instanceFieldTaint.clear();
144
155
  this.currentClassName = null;
@@ -180,6 +191,9 @@ export class ConstantPropagator {
180
191
  // Add unscoped versions of scoped names for backward compatibility
181
192
  for (const name of this.tainted) {
182
193
  if (name.includes(':')) {
194
+ if (/^[^:]+:b\d+:/.test(name)) {
195
+ continue;
196
+ }
183
197
  const unscoped = name.substring(name.indexOf(':') + 1);
184
198
  resultTainted.add(unscoped);
185
199
  }
@@ -204,6 +218,9 @@ export class ConstantPropagator {
204
218
  if (this.goScopedNames.has(name))
205
219
  continue;
206
220
  if (name.includes(':')) {
221
+ if (/^[^:]+:b\d+:/.test(name)) {
222
+ continue;
223
+ }
207
224
  const unscoped = name.substring(name.indexOf(':') + 1);
208
225
  // Only add if not already present (scoped version wins on conflict)
209
226
  if (!resultSymbols.has(unscoped)) {
@@ -214,6 +231,7 @@ export class ConstantPropagator {
214
231
  return {
215
232
  symbols: resultSymbols,
216
233
  tainted: resultTainted,
234
+ lineBlockScopes: new Map(this.lineBlockScopes),
217
235
  unreachableLines: new Set(this.unreachableLines),
218
236
  taintedCollections: new Map(this.taintedCollections),
219
237
  taintedArrayElements: new Map(this.taintedArrayElements),
@@ -840,6 +858,20 @@ export class ConstantPropagator {
840
858
  // ===========================================================================
841
859
  // Taint Refinement
842
860
  // ===========================================================================
861
+ /** True when `node` is `iter.next()` (possibly under a cast). */
862
+ isIteratorNextExpression(node) {
863
+ const text = getNodeText(node, this.source);
864
+ if (/\.\s*next\s*\(/.test(text))
865
+ return true;
866
+ let current = node;
867
+ if (current.type === 'cast_expression' || current.type === 'parenthesized_expression') {
868
+ current = current.childForFieldName('operand') ?? current.childForFieldName('expression');
869
+ }
870
+ if (!current || current.type !== 'method_invocation')
871
+ return false;
872
+ const nameNode = current.childForFieldName('name');
873
+ return nameNode ? getNodeText(nameNode, this.source) === 'next' : false;
874
+ }
843
875
  refineTaintFromConstants() {
844
876
  let changed = true;
845
877
  let iterations = 0;
@@ -849,6 +881,9 @@ export class ConstantPropagator {
849
881
  iterations++;
850
882
  const toRemove = [];
851
883
  for (const varName of this.tainted) {
884
+ if (this.taintedFromIteratorNext.has(varName)) {
885
+ continue;
886
+ }
852
887
  const symbol = this.symbols.get(varName);
853
888
  if (symbol && symbol.type !== 'unknown') {
854
889
  toRemove.push(varName);
@@ -909,6 +944,9 @@ export class ConstantPropagator {
909
944
  if (this.unreachableLines.has(line)) {
910
945
  return true; // skip subtree
911
946
  }
947
+ if (this.blockScopeStack.length > 0) {
948
+ this.lineBlockScopes.set(line, this.blockScopeStack[this.blockScopeStack.length - 1]);
949
+ }
912
950
  // Track which condition this line is under for correlated predicate analysis
913
951
  if (this.conditionStack.length > 0 && !this.lineConditions.has(line)) {
914
952
  // Use the innermost (most recent) condition
@@ -948,12 +986,24 @@ export class ConstantPropagator {
948
986
  case 'assignment_statement':
949
987
  this.handleGoAssignment(node);
950
988
  return false;
989
+ case 'var_declaration':
990
+ this.handleGoVarDeclaration(node);
991
+ return false;
951
992
  case 'update_expression':
952
993
  this.handleUpdateExpression(node);
953
994
  return false;
954
995
  case 'if_statement':
955
996
  this.handleIfStatement(node);
956
997
  return true;
998
+ case 'block': {
999
+ if (this.skipMethodBodyBlockScope) {
1000
+ this.skipMethodBodyBlockScope = false;
1001
+ }
1002
+ for (const child of node.children) {
1003
+ this.visit(child);
1004
+ }
1005
+ return true;
1006
+ }
957
1007
  case 'switch_expression':
958
1008
  case 'switch_statement':
959
1009
  this.handleSwitch(node);
@@ -1046,10 +1096,12 @@ export class ConstantPropagator {
1046
1096
  }
1047
1097
  }
1048
1098
  }
1049
- // Visit the method body
1099
+ // Visit the method body (do not treat the outer `{ ... }` as a nested block scope)
1050
1100
  const body = node.childForFieldName('body');
1051
1101
  if (body) {
1102
+ this.skipMethodBodyBlockScope = true;
1052
1103
  this.visit(body);
1104
+ this.skipMethodBodyBlockScope = false;
1053
1105
  }
1054
1106
  // Restore the previous method context
1055
1107
  this.currentMethod = prevMethod;
@@ -1065,11 +1117,36 @@ export class ConstantPropagator {
1065
1117
  if (varName.includes('.') || varName.includes(':')) {
1066
1118
  return varName;
1067
1119
  }
1068
- // Scope local variables by method name
1120
+ const blockId = this.blockScopeStack.length > 0
1121
+ ? this.blockScopeStack[this.blockScopeStack.length - 1]
1122
+ : undefined;
1069
1123
  if (this.currentMethod) {
1070
- return `${this.currentMethod}:${varName}`;
1124
+ return blockId !== undefined
1125
+ ? `${this.currentMethod}:b${blockId}:${varName}`
1126
+ : `${this.currentMethod}:${varName}`;
1071
1127
  }
1072
- return varName;
1128
+ return blockId !== undefined ? `b${blockId}:${varName}` : varName;
1129
+ }
1130
+ /** Innermost block → method → unscoped, for reads that may bind to an outer declaration. */
1131
+ getScopedNameCandidates(varName) {
1132
+ if (varName.includes('.') || varName.includes(':')) {
1133
+ return [varName];
1134
+ }
1135
+ const out = [];
1136
+ for (let i = this.blockScopeStack.length - 1; i >= 0; i--) {
1137
+ const blockId = this.blockScopeStack[i];
1138
+ if (this.currentMethod) {
1139
+ out.push(`${this.currentMethod}:b${blockId}:${varName}`);
1140
+ }
1141
+ else {
1142
+ out.push(`b${blockId}:${varName}`);
1143
+ }
1144
+ }
1145
+ if (this.currentMethod) {
1146
+ out.push(`${this.currentMethod}:${varName}`);
1147
+ }
1148
+ out.push(varName);
1149
+ return out;
1073
1150
  }
1074
1151
  /**
1075
1152
  * Look up a variable value, checking both scoped and unscoped names.
@@ -1077,12 +1154,12 @@ export class ConstantPropagator {
1077
1154
  * either local (scoped) or global (unscoped, like class fields).
1078
1155
  */
1079
1156
  lookupSymbol(varName) {
1080
- // First try the scoped name (local variable in current method)
1081
- if (this.currentMethod && !varName.includes('.') && !varName.includes(':')) {
1082
- const scopedName = `${this.currentMethod}:${varName}`;
1083
- const scopedValue = this.symbols.get(scopedName);
1084
- if (scopedValue) {
1085
- return scopedValue;
1157
+ if (!varName.includes('.') && !varName.includes(':')) {
1158
+ for (const scopedName of this.getScopedNameCandidates(varName)) {
1159
+ const scopedValue = this.symbols.get(scopedName);
1160
+ if (scopedValue) {
1161
+ return scopedValue;
1162
+ }
1086
1163
  }
1087
1164
  }
1088
1165
  // Fall back to unscoped name (class fields, etc.)
@@ -1152,9 +1229,26 @@ export class ConstantPropagator {
1152
1229
  this.trackIteratorsInNode(initNode);
1153
1230
  }
1154
1231
  }
1155
- // Visit all children (condition, body, etc.)
1156
- for (const child of node.children) {
1157
- this.visit(child);
1232
+ // #476: shadowing loop locals (`String str` per while) — scope the body block only.
1233
+ if (node.type === 'while_statement' || node.type === 'do_statement') {
1234
+ const body = node.childForFieldName('body');
1235
+ for (const child of node.children) {
1236
+ if (child !== body)
1237
+ this.visit(child);
1238
+ }
1239
+ if (body?.type === 'block') {
1240
+ this.blockScopeStack.push(getNodeLine(body));
1241
+ this.visit(body);
1242
+ this.blockScopeStack.pop();
1243
+ }
1244
+ else if (body) {
1245
+ this.visit(body);
1246
+ }
1247
+ }
1248
+ else {
1249
+ for (const child of node.children) {
1250
+ this.visit(child);
1251
+ }
1158
1252
  }
1159
1253
  // After visiting children, ensure loop variables stay unknown (they may have been overwritten)
1160
1254
  for (const varName of loopVarNames) {
@@ -1318,6 +1412,10 @@ export class ConstantPropagator {
1318
1412
  this.sanitizedVars.delete(scopedName); // No longer sanitized if receiving tainted value
1319
1413
  this.sanitizedVarUncovered.delete(scopedName);
1320
1414
  this.definitionNodes.set(scopedName, valueNode);
1415
+ if (this.isIteratorNextExpression(valueNode)) {
1416
+ this.taintedFromIteratorNext.add(scopedName);
1417
+ this.taintedFromIteratorNext.add(varName);
1418
+ }
1321
1419
  this.symbols.set(scopedName, createUnknown(line));
1322
1420
  }
1323
1421
  else {
@@ -1367,6 +1465,71 @@ export class ConstantPropagator {
1367
1465
  * helper that always returns a constant — clears the variable. Anything else stops being tracked here, which is what Go
1368
1466
  * assignments got before this handler existed: the DFG decides.
1369
1467
  */
1468
+ /** `var x = expr` / `var x T = expr` — same clean/taint rules as `:=` (#783). */
1469
+ handleGoVarDeclaration(node) {
1470
+ for (let i = 0; i < node.childCount; i++) {
1471
+ const spec = node.child(i);
1472
+ if (!spec || spec.type !== 'var_spec')
1473
+ continue;
1474
+ const nameNode = spec.childForFieldName('name');
1475
+ let valueNode = spec.childForFieldName('value');
1476
+ if (!nameNode || !valueNode)
1477
+ continue;
1478
+ const names = [];
1479
+ if (nameNode.type === 'identifier') {
1480
+ names.push(getNodeText(nameNode, this.source));
1481
+ }
1482
+ else if (nameNode.type === 'identifier_list') {
1483
+ for (const c of nameNode.children) {
1484
+ if (c.type === 'identifier')
1485
+ names.push(getNodeText(c, this.source));
1486
+ }
1487
+ }
1488
+ if (valueNode.type === 'expression_list') {
1489
+ valueNode =
1490
+ valueNode.children.find((c) => c.type !== ',' && c.type !== 'comment') ?? valueNode;
1491
+ }
1492
+ if (names.length === 0)
1493
+ continue;
1494
+ const line = getNodeLine(node);
1495
+ for (const varName of names) {
1496
+ if (varName === '_')
1497
+ continue;
1498
+ const scopedName = this.getScopedName(varName);
1499
+ this.goScopedNames.add(scopedName);
1500
+ if (this.isTaintedExpression(valueNode)) {
1501
+ this.goUnknownVars.add(scopedName);
1502
+ this.tainted.add(scopedName);
1503
+ this.sanitizedVars.delete(scopedName);
1504
+ this.sanitizedVarUncovered.delete(scopedName);
1505
+ this.definitionNodes.set(scopedName, valueNode);
1506
+ this.symbols.set(scopedName, createUnknown(line));
1507
+ continue;
1508
+ }
1509
+ if (!this.isGoKnownCleanRhs(valueNode) || this.goUnknownVars.has(scopedName)) {
1510
+ this.goUnknownVars.add(scopedName);
1511
+ this.symbols.delete(scopedName);
1512
+ this.sanitizedVars.delete(scopedName);
1513
+ this.sanitizedVarUncovered.delete(scopedName);
1514
+ if (!this.inConditionalBranch) {
1515
+ this.tainted.delete(scopedName);
1516
+ this.definitionNodes.delete(scopedName);
1517
+ }
1518
+ continue;
1519
+ }
1520
+ if (this.inConditionalBranch) {
1521
+ this.goUnknownVars.add(scopedName);
1522
+ this.symbols.delete(scopedName);
1523
+ continue;
1524
+ }
1525
+ this.tainted.delete(scopedName);
1526
+ this.definitionNodes.delete(scopedName);
1527
+ this.symbols.set(scopedName, this.evaluateExpression(valueNode));
1528
+ this.sanitizedVars.delete(scopedName);
1529
+ this.sanitizedVarUncovered.delete(scopedName);
1530
+ }
1531
+ }
1532
+ }
1370
1533
  handleGoAssignment(node) {
1371
1534
  const left = node.childForFieldName('left');
1372
1535
  const right = node.childForFieldName('right');
@@ -1477,6 +1640,12 @@ export class ConstantPropagator {
1477
1640
  this.sanitizedVars.delete(scopedName); // No longer sanitized if receiving tainted value
1478
1641
  this.sanitizedVarUncovered.delete(scopedName);
1479
1642
  this.definitionNodes.set(scopedName, right);
1643
+ if (this.isIteratorNextExpression(right)) {
1644
+ this.taintedFromIteratorNext.add(scopedName);
1645
+ if (!varName.includes('.')) {
1646
+ this.taintedFromIteratorNext.add(varName);
1647
+ }
1648
+ }
1480
1649
  this.symbols.set(scopedName, createUnknown(line));
1481
1650
  // Track constructor field assignments: this.field = taintedParam
1482
1651
  if (this.inConstructor && varName.startsWith('this.')) {
@@ -2319,6 +2488,31 @@ export class ConstantPropagator {
2319
2488
  cache.set(rootId, false);
2320
2489
  return false;
2321
2490
  }
2491
+ /** Is an argument of `call` an inline sanitizer call over tainted input (itself possibly nested)? */
2492
+ argIsSanitizedTaint(call) {
2493
+ const argsNode = call.childForFieldName('arguments');
2494
+ if (!argsNode)
2495
+ return false;
2496
+ for (const arg of argsNode.children) {
2497
+ if (arg.type !== 'method_invocation')
2498
+ continue;
2499
+ const name = arg.childForFieldName('name');
2500
+ if (!name || !SANITIZER_METHODS.has(getNodeText(name, this.source)))
2501
+ continue;
2502
+ const inner = arg.childForFieldName('arguments');
2503
+ if (!inner)
2504
+ continue;
2505
+ for (const a of inner.children) {
2506
+ if (a.type === '(' || a.type === ')' || a.type === ',')
2507
+ continue;
2508
+ if (this.isTaintedExpression(a))
2509
+ return true;
2510
+ }
2511
+ if (this.argIsSanitizedTaint(arg))
2512
+ return true;
2513
+ }
2514
+ return false;
2515
+ }
2322
2516
  isTaintedExpressionStep(node) {
2323
2517
  const text = getNodeText(node, this.source);
2324
2518
  if (node.type === 'method_invocation') {
@@ -2326,6 +2520,12 @@ export class ConstantPropagator {
2326
2520
  const objectNode = node.childForFieldName('object');
2327
2521
  if (nameNode) {
2328
2522
  const methodName = getNodeText(nameNode, this.source);
2523
+ // An anti-sanitizer undoing an inline sanitizer is a round trip
2524
+ // (`Base64.getDecoder().decode(Base64.getEncoder().encode(param.getBytes()))`):
2525
+ // the result carries the sanitizer's tainted input (#976).
2526
+ if (ANTI_SANITIZER_METHODS.has(methodName) && this.argIsSanitizedTaint(node)) {
2527
+ return true;
2528
+ }
2329
2529
  if (SANITIZER_METHODS.has(methodName) && !this.localNonSanitizing.has(methodName)) {
2330
2530
  return false;
2331
2531
  }
@@ -2620,16 +2820,20 @@ export class ConstantPropagator {
2620
2820
  }
2621
2821
  }
2622
2822
  if (node.type === 'identifier') {
2623
- // Check both scoped and unscoped taint
2624
- // Scoped: methodName:varName (for local variables)
2625
- // Unscoped: varName (for class fields or when not in a method)
2626
- const scopedName = this.currentMethod ? `${this.currentMethod}:${text}` : text;
2627
- const isTainted = this.tainted.has(scopedName) || this.tainted.has(text);
2628
- if (isTainted) {
2823
+ let isTainted = false;
2824
+ let boundName = null;
2825
+ for (const candidate of this.getScopedNameCandidates(text)) {
2826
+ if (this.tainted.has(candidate)) {
2827
+ isTainted = true;
2828
+ boundName = candidate;
2829
+ break;
2830
+ }
2831
+ }
2832
+ if (isTainted && boundName) {
2629
2833
  // Check if this taint is excluded by correlated predicate analysis
2630
2834
  // If the variable was tainted under condition C, and we're now under !C,
2631
2835
  // the taint doesn't apply in this context
2632
- if (this.isExcludedByCondition(scopedName) || this.isExcludedByCondition(text)) {
2836
+ if (this.isExcludedByCondition(boundName) || this.isExcludedByCondition(text)) {
2633
2837
  return false;
2634
2838
  }
2635
2839
  }