@oculum/scanner 1.0.14 → 1.0.15

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 (1323) hide show
  1. package/dist/detect/ai-code/index.d.ts +6 -11
  2. package/dist/detect/ai-code/index.d.ts.map +1 -1
  3. package/dist/detect/ai-code/index.js +6 -24
  4. package/dist/detect/ai-code/index.js.map +1 -1
  5. package/dist/detect/ast-rules/agent-tools-ast.d.ts +14 -0
  6. package/dist/detect/ast-rules/agent-tools-ast.d.ts.map +1 -0
  7. package/dist/detect/ast-rules/agent-tools-ast.js +809 -0
  8. package/dist/detect/ast-rules/agent-tools-ast.js.map +1 -0
  9. package/dist/detect/ast-rules/ai-fingerprinting-ast.d.ts +14 -0
  10. package/dist/detect/ast-rules/ai-fingerprinting-ast.d.ts.map +1 -0
  11. package/dist/detect/ast-rules/ai-fingerprinting-ast.js +344 -0
  12. package/dist/detect/ast-rules/ai-fingerprinting-ast.js.map +1 -0
  13. package/dist/detect/ast-rules/auth-patterns-ast.d.ts +14 -0
  14. package/dist/detect/ast-rules/auth-patterns-ast.d.ts.map +1 -0
  15. package/dist/detect/ast-rules/auth-patterns-ast.js +280 -0
  16. package/dist/detect/ast-rules/auth-patterns-ast.js.map +1 -0
  17. package/dist/detect/ast-rules/byok-ast.d.ts +13 -0
  18. package/dist/detect/ast-rules/byok-ast.d.ts.map +1 -0
  19. package/dist/detect/ast-rules/byok-ast.js +180 -0
  20. package/dist/detect/ast-rules/byok-ast.js.map +1 -0
  21. package/dist/detect/ast-rules/child-process-ast.d.ts +13 -0
  22. package/dist/detect/ast-rules/child-process-ast.d.ts.map +1 -0
  23. package/dist/detect/ast-rules/child-process-ast.js +252 -0
  24. package/dist/detect/ast-rules/child-process-ast.js.map +1 -0
  25. package/dist/detect/ast-rules/dangerous-eval-ast.d.ts +13 -0
  26. package/dist/detect/ast-rules/dangerous-eval-ast.d.ts.map +1 -0
  27. package/dist/detect/ast-rules/dangerous-eval-ast.js +218 -0
  28. package/dist/detect/ast-rules/dangerous-eval-ast.js.map +1 -0
  29. package/dist/detect/ast-rules/data-exposure-ast.d.ts +13 -0
  30. package/dist/detect/ast-rules/data-exposure-ast.d.ts.map +1 -0
  31. package/dist/detect/ast-rules/data-exposure-ast.js +158 -0
  32. package/dist/detect/ast-rules/data-exposure-ast.js.map +1 -0
  33. package/dist/detect/ast-rules/dom-xss-ast.d.ts +14 -0
  34. package/dist/detect/ast-rules/dom-xss-ast.d.ts.map +1 -0
  35. package/dist/detect/ast-rules/dom-xss-ast.js +217 -0
  36. package/dist/detect/ast-rules/dom-xss-ast.js.map +1 -0
  37. package/dist/detect/ast-rules/endpoint-protection-ast.d.ts +13 -0
  38. package/dist/detect/ast-rules/endpoint-protection-ast.d.ts.map +1 -0
  39. package/dist/detect/ast-rules/endpoint-protection-ast.js +228 -0
  40. package/dist/detect/ast-rules/endpoint-protection-ast.js.map +1 -0
  41. package/dist/detect/ast-rules/entropy-ast.d.ts +17 -0
  42. package/dist/detect/ast-rules/entropy-ast.d.ts.map +1 -0
  43. package/dist/detect/ast-rules/entropy-ast.js +265 -0
  44. package/dist/detect/ast-rules/entropy-ast.js.map +1 -0
  45. package/dist/detect/ast-rules/flask-debug-ast.d.ts +10 -0
  46. package/dist/detect/ast-rules/flask-debug-ast.d.ts.map +1 -0
  47. package/dist/detect/ast-rules/flask-debug-ast.js +125 -0
  48. package/dist/detect/ast-rules/flask-debug-ast.js.map +1 -0
  49. package/dist/detect/ast-rules/framework-checks-ast.d.ts +13 -0
  50. package/dist/detect/ast-rules/framework-checks-ast.d.ts.map +1 -0
  51. package/dist/detect/ast-rules/framework-checks-ast.js +185 -0
  52. package/dist/detect/ast-rules/framework-checks-ast.js.map +1 -0
  53. package/dist/detect/ast-rules/helpers/call-analysis.d.ts +62 -0
  54. package/dist/detect/ast-rules/helpers/call-analysis.d.ts.map +1 -0
  55. package/dist/detect/ast-rules/helpers/call-analysis.js +217 -0
  56. package/dist/detect/ast-rules/helpers/call-analysis.js.map +1 -0
  57. package/dist/detect/ast-rules/helpers/context-detection.d.ts +33 -0
  58. package/dist/detect/ast-rules/helpers/context-detection.d.ts.map +1 -0
  59. package/dist/detect/ast-rules/helpers/context-detection.js +256 -0
  60. package/dist/detect/ast-rules/helpers/context-detection.js.map +1 -0
  61. package/dist/detect/ast-rules/helpers/control-flow.d.ts +40 -0
  62. package/dist/detect/ast-rules/helpers/control-flow.d.ts.map +1 -0
  63. package/dist/detect/ast-rules/helpers/control-flow.js +174 -0
  64. package/dist/detect/ast-rules/helpers/control-flow.js.map +1 -0
  65. package/dist/detect/ast-rules/helpers/import-analysis.d.ts +43 -0
  66. package/dist/detect/ast-rules/helpers/import-analysis.d.ts.map +1 -0
  67. package/dist/detect/ast-rules/helpers/import-analysis.js +149 -0
  68. package/dist/detect/ast-rules/helpers/import-analysis.js.map +1 -0
  69. package/dist/detect/ast-rules/helpers/index.d.ts +16 -0
  70. package/dist/detect/ast-rules/helpers/index.d.ts.map +1 -0
  71. package/dist/detect/ast-rules/helpers/index.js +112 -0
  72. package/dist/detect/ast-rules/helpers/index.js.map +1 -0
  73. package/dist/detect/ast-rules/helpers/python-helpers.d.ts +215 -0
  74. package/dist/detect/ast-rules/helpers/python-helpers.d.ts.map +1 -0
  75. package/dist/detect/ast-rules/helpers/python-helpers.js +935 -0
  76. package/dist/detect/ast-rules/helpers/python-helpers.js.map +1 -0
  77. package/dist/detect/ast-rules/helpers/scope-analysis.d.ts +50 -0
  78. package/dist/detect/ast-rules/helpers/scope-analysis.d.ts.map +1 -0
  79. package/dist/detect/ast-rules/helpers/scope-analysis.js +194 -0
  80. package/dist/detect/ast-rules/helpers/scope-analysis.js.map +1 -0
  81. package/dist/detect/ast-rules/helpers/string-analysis.d.ts +57 -0
  82. package/dist/detect/ast-rules/helpers/string-analysis.d.ts.map +1 -0
  83. package/dist/detect/ast-rules/helpers/string-analysis.js +184 -0
  84. package/dist/detect/ast-rules/helpers/string-analysis.js.map +1 -0
  85. package/dist/detect/ast-rules/helpers/type-extraction.d.ts +44 -0
  86. package/dist/detect/ast-rules/helpers/type-extraction.d.ts.map +1 -0
  87. package/dist/detect/ast-rules/helpers/type-extraction.js +125 -0
  88. package/dist/detect/ast-rules/helpers/type-extraction.js.map +1 -0
  89. package/dist/detect/ast-rules/helpers/user-input.d.ts +35 -0
  90. package/dist/detect/ast-rules/helpers/user-input.d.ts.map +1 -0
  91. package/dist/detect/ast-rules/helpers/user-input.js +243 -0
  92. package/dist/detect/ast-rules/helpers/user-input.js.map +1 -0
  93. package/dist/detect/ast-rules/index.d.ts +112 -0
  94. package/dist/detect/ast-rules/index.d.ts.map +1 -0
  95. package/dist/detect/ast-rules/index.js +232 -0
  96. package/dist/detect/ast-rules/index.js.map +1 -0
  97. package/dist/detect/ast-rules/json-parse-ast.d.ts +13 -0
  98. package/dist/detect/ast-rules/json-parse-ast.d.ts.map +1 -0
  99. package/dist/detect/ast-rules/json-parse-ast.js +143 -0
  100. package/dist/detect/ast-rules/json-parse-ast.js.map +1 -0
  101. package/dist/detect/ast-rules/log-injection-ast.d.ts +14 -0
  102. package/dist/detect/ast-rules/log-injection-ast.d.ts.map +1 -0
  103. package/dist/detect/ast-rules/log-injection-ast.js +235 -0
  104. package/dist/detect/ast-rules/log-injection-ast.js.map +1 -0
  105. package/dist/detect/ast-rules/logic-gates-ast.d.ts +14 -0
  106. package/dist/detect/ast-rules/logic-gates-ast.d.ts.map +1 -0
  107. package/dist/detect/ast-rules/logic-gates-ast.js +312 -0
  108. package/dist/detect/ast-rules/logic-gates-ast.js.map +1 -0
  109. package/dist/detect/ast-rules/mcp-security-ast.d.ts +14 -0
  110. package/dist/detect/ast-rules/mcp-security-ast.d.ts.map +1 -0
  111. package/dist/detect/ast-rules/mcp-security-ast.js +755 -0
  112. package/dist/detect/ast-rules/mcp-security-ast.js.map +1 -0
  113. package/dist/detect/ast-rules/model-supply-chain-ast.d.ts +13 -0
  114. package/dist/detect/ast-rules/model-supply-chain-ast.d.ts.map +1 -0
  115. package/dist/detect/ast-rules/model-supply-chain-ast.js +188 -0
  116. package/dist/detect/ast-rules/model-supply-chain-ast.js.map +1 -0
  117. package/dist/detect/ast-rules/package-hallucination-ast.d.ts +13 -0
  118. package/dist/detect/ast-rules/package-hallucination-ast.d.ts.map +1 -0
  119. package/dist/detect/ast-rules/package-hallucination-ast.js +607 -0
  120. package/dist/detect/ast-rules/package-hallucination-ast.js.map +1 -0
  121. package/dist/detect/ast-rules/prompt-hygiene-ast.d.ts +15 -0
  122. package/dist/detect/ast-rules/prompt-hygiene-ast.d.ts.map +1 -0
  123. package/dist/detect/ast-rules/prompt-hygiene-ast.js +332 -0
  124. package/dist/detect/ast-rules/prompt-hygiene-ast.js.map +1 -0
  125. package/dist/detect/ast-rules/rag-safety-ast.d.ts +18 -0
  126. package/dist/detect/ast-rules/rag-safety-ast.d.ts.map +1 -0
  127. package/dist/detect/ast-rules/rag-safety-ast.js +640 -0
  128. package/dist/detect/ast-rules/rag-safety-ast.js.map +1 -0
  129. package/dist/detect/ast-rules/request-validation-ast.d.ts +13 -0
  130. package/dist/detect/ast-rules/request-validation-ast.d.ts.map +1 -0
  131. package/dist/detect/ast-rules/request-validation-ast.js +116 -0
  132. package/dist/detect/ast-rules/request-validation-ast.js.map +1 -0
  133. package/dist/detect/ast-rules/risky-imports-ast.d.ts +14 -0
  134. package/dist/detect/ast-rules/risky-imports-ast.d.ts.map +1 -0
  135. package/dist/detect/ast-rules/risky-imports-ast.js +114 -0
  136. package/dist/detect/ast-rules/risky-imports-ast.js.map +1 -0
  137. package/dist/detect/ast-rules/schema-validation-ast.d.ts +14 -0
  138. package/dist/detect/ast-rules/schema-validation-ast.d.ts.map +1 -0
  139. package/dist/detect/ast-rules/schema-validation-ast.js +233 -0
  140. package/dist/detect/ast-rules/schema-validation-ast.js.map +1 -0
  141. package/dist/detect/ast-rules/secret-patterns-ast.d.ts +17 -0
  142. package/dist/detect/ast-rules/secret-patterns-ast.d.ts.map +1 -0
  143. package/dist/detect/ast-rules/secret-patterns-ast.js +199 -0
  144. package/dist/detect/ast-rules/secret-patterns-ast.js.map +1 -0
  145. package/dist/detect/ast-rules/security-headers-ast.d.ts +14 -0
  146. package/dist/detect/ast-rules/security-headers-ast.d.ts.map +1 -0
  147. package/dist/detect/ast-rules/security-headers-ast.js +187 -0
  148. package/dist/detect/ast-rules/security-headers-ast.js.map +1 -0
  149. package/dist/detect/ast-rules/sql-injection-ast.d.ts +17 -0
  150. package/dist/detect/ast-rules/sql-injection-ast.d.ts.map +1 -0
  151. package/dist/detect/ast-rules/sql-injection-ast.js +497 -0
  152. package/dist/detect/ast-rules/sql-injection-ast.js.map +1 -0
  153. package/dist/detect/ast-rules/ssrf-ast.d.ts +14 -0
  154. package/dist/detect/ast-rules/ssrf-ast.d.ts.map +1 -0
  155. package/dist/detect/ast-rules/ssrf-ast.js +573 -0
  156. package/dist/detect/ast-rules/ssrf-ast.js.map +1 -0
  157. package/dist/detect/ast-rules/taint-fix-templates.d.ts +18 -0
  158. package/dist/detect/ast-rules/taint-fix-templates.d.ts.map +1 -0
  159. package/dist/detect/ast-rules/taint-fix-templates.js +92 -0
  160. package/dist/detect/ast-rules/taint-fix-templates.js.map +1 -0
  161. package/dist/detect/ast-rules/taint-flow-ast.d.ts +24 -0
  162. package/dist/detect/ast-rules/taint-flow-ast.d.ts.map +1 -0
  163. package/dist/detect/ast-rules/taint-flow-ast.js +340 -0
  164. package/dist/detect/ast-rules/taint-flow-ast.js.map +1 -0
  165. package/dist/detect/ast-rules/variables-ast.d.ts +24 -0
  166. package/dist/detect/ast-rules/variables-ast.d.ts.map +1 -0
  167. package/dist/detect/ast-rules/variables-ast.js +362 -0
  168. package/dist/detect/ast-rules/variables-ast.js.map +1 -0
  169. package/dist/detect/ast-rules/weak-crypto-ast.d.ts +15 -0
  170. package/dist/detect/ast-rules/weak-crypto-ast.d.ts.map +1 -0
  171. package/dist/detect/ast-rules/weak-crypto-ast.js +406 -0
  172. package/dist/detect/ast-rules/weak-crypto-ast.js.map +1 -0
  173. package/dist/detect/ast-rules/xxe-ast.d.ts +13 -0
  174. package/dist/detect/ast-rules/xxe-ast.d.ts.map +1 -0
  175. package/dist/detect/ast-rules/xxe-ast.js +157 -0
  176. package/dist/detect/ast-rules/xxe-ast.js.map +1 -0
  177. package/dist/detect/config/agent-skill-injection.d.ts.map +1 -1
  178. package/dist/detect/config/agent-skill-injection.js +2 -24
  179. package/dist/detect/config/agent-skill-injection.js.map +1 -1
  180. package/dist/detect/config/index.d.ts +1 -0
  181. package/dist/detect/config/index.d.ts.map +1 -1
  182. package/dist/detect/config/index.js +3 -1
  183. package/dist/detect/config/index.js.map +1 -1
  184. package/dist/detect/config/osv-check.d.ts.map +1 -1
  185. package/dist/detect/config/osv-check.js +6 -1
  186. package/dist/detect/config/osv-check.js.map +1 -1
  187. package/dist/detect/config/package-check.d.ts.map +1 -1
  188. package/dist/detect/config/package-check.js +6 -1
  189. package/dist/detect/config/package-check.js.map +1 -1
  190. package/dist/detect/config/rules-file-backdoor.d.ts +36 -0
  191. package/dist/detect/config/rules-file-backdoor.d.ts.map +1 -0
  192. package/dist/detect/config/rules-file-backdoor.js +379 -0
  193. package/dist/detect/config/rules-file-backdoor.js.map +1 -0
  194. package/dist/detect/index.d.ts +43 -6
  195. package/dist/detect/index.d.ts.map +1 -1
  196. package/dist/detect/index.js +70 -7
  197. package/dist/detect/index.js.map +1 -1
  198. package/dist/detect/secrets/config-audit.d.ts.map +1 -1
  199. package/dist/detect/secrets/config-audit.js +36 -3
  200. package/dist/detect/secrets/config-audit.js.map +1 -1
  201. package/dist/detect/secrets/entropy.d.ts.map +1 -1
  202. package/dist/detect/secrets/entropy.js +180 -0
  203. package/dist/detect/secrets/entropy.js.map +1 -1
  204. package/dist/detect/secrets/index.d.ts +0 -2
  205. package/dist/detect/secrets/index.d.ts.map +1 -1
  206. package/dist/detect/secrets/index.js +7 -17
  207. package/dist/detect/secrets/index.js.map +1 -1
  208. package/dist/detect/structural/index.d.ts +15 -28
  209. package/dist/detect/structural/index.d.ts.map +1 -1
  210. package/dist/detect/structural/index.js +20 -497
  211. package/dist/detect/structural/index.js.map +1 -1
  212. package/dist/index.d.ts +3 -0
  213. package/dist/index.d.ts.map +1 -1
  214. package/dist/index.js +9 -1
  215. package/dist/index.js.map +1 -1
  216. package/dist/model/auth-helper-detector.d.ts.map +1 -1
  217. package/dist/model/auth-helper-detector.js +2 -7
  218. package/dist/model/auth-helper-detector.js.map +1 -1
  219. package/dist/model/import-resolver.d.ts.map +1 -1
  220. package/dist/model/import-resolver.js +94 -0
  221. package/dist/model/import-resolver.js.map +1 -1
  222. package/dist/model/imported-auth-detector.js +8 -8
  223. package/dist/model/imported-auth-detector.js.map +1 -1
  224. package/dist/model/index.d.ts +8 -0
  225. package/dist/model/index.d.ts.map +1 -1
  226. package/dist/model/index.js +198 -73
  227. package/dist/model/index.js.map +1 -1
  228. package/dist/model/module-graph.d.ts.map +1 -1
  229. package/dist/model/module-graph.js +22 -9
  230. package/dist/model/module-graph.js.map +1 -1
  231. package/dist/model/project-context.d.ts +1 -1
  232. package/dist/model/project-context.d.ts.map +1 -1
  233. package/dist/model/project-context.js +34 -0
  234. package/dist/model/project-context.js.map +1 -1
  235. package/dist/model/route-auth-resolver.d.ts.map +1 -1
  236. package/dist/model/route-auth-resolver.js +17 -2
  237. package/dist/model/route-auth-resolver.js.map +1 -1
  238. package/dist/model/route-discovery/index.js +1 -1
  239. package/dist/model/route-discovery/index.js.map +1 -1
  240. package/dist/model/route-discovery/nextjs.js +1 -1
  241. package/dist/model/route-discovery/nextjs.js.map +1 -1
  242. package/dist/model/route-discovery/python.d.ts +6 -3
  243. package/dist/model/route-discovery/python.d.ts.map +1 -1
  244. package/dist/model/route-discovery/python.js +132 -9
  245. package/dist/model/route-discovery/python.js.map +1 -1
  246. package/dist/model/route-discovery/types.d.ts +1 -1
  247. package/dist/model/route-discovery/types.d.ts.map +1 -1
  248. package/dist/model/route-discovery/utils.d.ts +8 -0
  249. package/dist/model/route-discovery/utils.d.ts.map +1 -1
  250. package/dist/model/route-discovery/utils.js +70 -0
  251. package/dist/model/route-discovery/utils.js.map +1 -1
  252. package/dist/model/taint-types.d.ts +0 -4
  253. package/dist/model/taint-types.d.ts.map +1 -1
  254. package/dist/parse/ast.d.ts +58 -0
  255. package/dist/parse/ast.d.ts.map +1 -0
  256. package/dist/parse/ast.js +230 -0
  257. package/dist/parse/ast.js.map +1 -0
  258. package/dist/parse/call-graph.d.ts +41 -0
  259. package/dist/parse/call-graph.d.ts.map +1 -0
  260. package/dist/parse/call-graph.js +386 -0
  261. package/dist/parse/call-graph.js.map +1 -0
  262. package/dist/parse/file-classifier.d.ts +11 -0
  263. package/dist/parse/file-classifier.d.ts.map +1 -1
  264. package/dist/parse/file-classifier.js +63 -15
  265. package/dist/parse/file-classifier.js.map +1 -1
  266. package/dist/parse/node-index.d.ts +32 -0
  267. package/dist/parse/node-index.d.ts.map +1 -0
  268. package/dist/parse/node-index.js +103 -0
  269. package/dist/parse/node-index.js.map +1 -0
  270. package/dist/parse/type-extractor.d.ts +50 -0
  271. package/dist/parse/type-extractor.d.ts.map +1 -0
  272. package/dist/parse/type-extractor.js +243 -0
  273. package/dist/parse/type-extractor.js.map +1 -0
  274. package/dist/pipeline/config.d.ts +7 -1
  275. package/dist/pipeline/config.d.ts.map +1 -1
  276. package/dist/pipeline/config.js.map +1 -1
  277. package/dist/pipeline/index.d.ts +3 -3
  278. package/dist/pipeline/index.d.ts.map +1 -1
  279. package/dist/pipeline/index.js +192 -64
  280. package/dist/pipeline/index.js.map +1 -1
  281. package/dist/pipeline/modes/incremental.d.ts.map +1 -1
  282. package/dist/pipeline/modes/incremental.js +2 -7
  283. package/dist/pipeline/modes/incremental.js.map +1 -1
  284. package/dist/postprocess/dedup.d.ts +5 -2
  285. package/dist/postprocess/dedup.d.ts.map +1 -1
  286. package/dist/postprocess/dedup.js +47 -16
  287. package/dist/postprocess/dedup.js.map +1 -1
  288. package/dist/report/build-result.d.ts +9 -4
  289. package/dist/report/build-result.d.ts.map +1 -1
  290. package/dist/report/build-result.js +15 -4
  291. package/dist/report/build-result.js.map +1 -1
  292. package/dist/report/formatters/cli-terminal.d.ts +1 -1
  293. package/dist/report/formatters/cli-terminal.d.ts.map +1 -1
  294. package/dist/report/formatters/cli-terminal.js +434 -231
  295. package/dist/report/formatters/cli-terminal.js.map +1 -1
  296. package/dist/report/sanitize.d.ts +10 -0
  297. package/dist/report/sanitize.d.ts.map +1 -0
  298. package/dist/report/sanitize.js +19 -0
  299. package/dist/report/sanitize.js.map +1 -0
  300. package/dist/score/adjustments.d.ts +20 -2
  301. package/dist/score/adjustments.d.ts.map +1 -1
  302. package/dist/score/adjustments.js +108 -37
  303. package/dist/score/adjustments.js.map +1 -1
  304. package/dist/score/confidence.d.ts +6 -0
  305. package/dist/score/confidence.d.ts.map +1 -1
  306. package/dist/score/confidence.js +10 -4
  307. package/dist/score/confidence.js.map +1 -1
  308. package/dist/score/evidence.d.ts +25 -0
  309. package/dist/score/evidence.d.ts.map +1 -0
  310. package/dist/score/evidence.js +51 -0
  311. package/dist/score/evidence.js.map +1 -0
  312. package/dist/score/index.d.ts +3 -1
  313. package/dist/score/index.d.ts.map +1 -1
  314. package/dist/score/index.js +25 -50
  315. package/dist/score/index.js.map +1 -1
  316. package/dist/score/types.d.ts +5 -1
  317. package/dist/score/types.d.ts.map +1 -1
  318. package/dist/shared/category-filter.d.ts.map +1 -1
  319. package/dist/shared/category-filter.js +12 -0
  320. package/dist/shared/category-filter.js.map +1 -1
  321. package/dist/shared/regex-utils.d.ts +3 -0
  322. package/dist/shared/regex-utils.d.ts.map +1 -0
  323. package/dist/shared/regex-utils.js +8 -0
  324. package/dist/shared/regex-utils.js.map +1 -0
  325. package/dist/shared/registry-clients.d.ts +7 -0
  326. package/dist/shared/registry-clients.d.ts.map +1 -1
  327. package/dist/shared/registry-clients.js +94 -17
  328. package/dist/shared/registry-clients.js.map +1 -1
  329. package/dist/shared/rules/metadata.d.ts.map +1 -1
  330. package/dist/shared/rules/metadata.js +17 -0
  331. package/dist/shared/rules/metadata.js.map +1 -1
  332. package/dist/shared/types.d.ts +59 -15
  333. package/dist/shared/types.d.ts.map +1 -1
  334. package/dist/shared/types.js +38 -21
  335. package/dist/shared/types.js.map +1 -1
  336. package/dist/taint/async-flow.d.ts +44 -0
  337. package/dist/taint/async-flow.d.ts.map +1 -0
  338. package/dist/taint/async-flow.js +271 -0
  339. package/dist/taint/async-flow.js.map +1 -0
  340. package/dist/taint/cfg-builder.d.ts +35 -0
  341. package/dist/taint/cfg-builder.d.ts.map +1 -0
  342. package/dist/taint/cfg-builder.js +980 -0
  343. package/dist/taint/cfg-builder.js.map +1 -0
  344. package/dist/taint/cfg-types.d.ts +76 -0
  345. package/dist/taint/cfg-types.d.ts.map +1 -0
  346. package/dist/taint/cfg-types.js +13 -0
  347. package/dist/taint/cfg-types.js.map +1 -0
  348. package/dist/taint/constant-propagation.d.ts +34 -0
  349. package/dist/taint/constant-propagation.d.ts.map +1 -0
  350. package/dist/taint/constant-propagation.js +164 -0
  351. package/dist/taint/constant-propagation.js.map +1 -0
  352. package/dist/taint/cross-file-analyzer.d.ts +27 -0
  353. package/dist/taint/cross-file-analyzer.d.ts.map +1 -0
  354. package/dist/taint/cross-file-analyzer.js +99 -0
  355. package/dist/taint/cross-file-analyzer.js.map +1 -0
  356. package/dist/taint/cross-file-index.d.ts +59 -0
  357. package/dist/taint/cross-file-index.d.ts.map +1 -0
  358. package/dist/taint/cross-file-index.js +183 -0
  359. package/dist/taint/cross-file-index.js.map +1 -0
  360. package/dist/taint/def-use.d.ts +27 -0
  361. package/dist/taint/def-use.d.ts.map +1 -0
  362. package/dist/taint/def-use.js +519 -0
  363. package/dist/taint/def-use.js.map +1 -0
  364. package/dist/taint/file-analysis-cache.d.ts +47 -0
  365. package/dist/taint/file-analysis-cache.d.ts.map +1 -0
  366. package/dist/taint/file-analysis-cache.js +107 -0
  367. package/dist/taint/file-analysis-cache.js.map +1 -0
  368. package/dist/taint/framework-models.d.ts +77 -0
  369. package/dist/taint/framework-models.d.ts.map +1 -0
  370. package/dist/taint/framework-models.js +258 -0
  371. package/dist/taint/framework-models.js.map +1 -0
  372. package/dist/taint/helpers.d.ts +31 -0
  373. package/dist/taint/helpers.d.ts.map +1 -0
  374. package/dist/taint/helpers.js +130 -0
  375. package/dist/taint/helpers.js.map +1 -0
  376. package/dist/taint/index.d.ts +28 -0
  377. package/dist/taint/index.d.ts.map +1 -0
  378. package/dist/taint/index.js +77 -0
  379. package/dist/taint/index.js.map +1 -0
  380. package/dist/taint/llm-registry.d.ts +47 -0
  381. package/dist/taint/llm-registry.d.ts.map +1 -0
  382. package/dist/taint/llm-registry.js +152 -0
  383. package/dist/taint/llm-registry.js.map +1 -0
  384. package/dist/taint/llm-risk-scoring.d.ts +54 -0
  385. package/dist/taint/llm-risk-scoring.d.ts.map +1 -0
  386. package/dist/taint/llm-risk-scoring.js +376 -0
  387. package/dist/taint/llm-risk-scoring.js.map +1 -0
  388. package/dist/taint/propagation-types.d.ts +104 -0
  389. package/dist/taint/propagation-types.d.ts.map +1 -0
  390. package/dist/taint/propagation-types.js +98 -0
  391. package/dist/taint/propagation-types.js.map +1 -0
  392. package/dist/taint/propagation.d.ts +111 -0
  393. package/dist/taint/propagation.d.ts.map +1 -0
  394. package/dist/taint/propagation.js +1576 -0
  395. package/dist/taint/propagation.js.map +1 -0
  396. package/dist/taint/sanitizer-registry.d.ts +26 -0
  397. package/dist/taint/sanitizer-registry.d.ts.map +1 -0
  398. package/dist/taint/sanitizer-registry.js +422 -0
  399. package/dist/taint/sanitizer-registry.js.map +1 -0
  400. package/dist/taint/sink-classifier.d.ts +27 -0
  401. package/dist/taint/sink-classifier.d.ts.map +1 -0
  402. package/dist/taint/sink-classifier.js +1166 -0
  403. package/dist/taint/sink-classifier.js.map +1 -0
  404. package/dist/taint/source-classifier.d.ts +29 -0
  405. package/dist/taint/source-classifier.d.ts.map +1 -0
  406. package/dist/taint/source-classifier.js +814 -0
  407. package/dist/taint/source-classifier.js.map +1 -0
  408. package/dist/taint/taint-analyzer.d.ts +33 -0
  409. package/dist/taint/taint-analyzer.d.ts.map +1 -0
  410. package/dist/taint/taint-analyzer.js +88 -0
  411. package/dist/taint/taint-analyzer.js.map +1 -0
  412. package/dist/taint/taint-summary.d.ts +37 -0
  413. package/dist/taint/taint-summary.d.ts.map +1 -0
  414. package/dist/taint/taint-summary.js +293 -0
  415. package/dist/taint/taint-summary.js.map +1 -0
  416. package/dist/taint/types.d.ts +47 -0
  417. package/dist/taint/types.d.ts.map +1 -0
  418. package/dist/taint/types.js +19 -0
  419. package/dist/taint/types.js.map +1 -0
  420. package/dist/validate/clients.d.ts +2 -1
  421. package/dist/validate/clients.d.ts.map +1 -1
  422. package/dist/validate/clients.js +3 -2
  423. package/dist/validate/clients.js.map +1 -1
  424. package/dist/validate/index.d.ts +5 -6
  425. package/dist/validate/index.d.ts.map +1 -1
  426. package/dist/validate/index.js +22 -21
  427. package/dist/validate/index.js.map +1 -1
  428. package/dist/validate/prompts/modules/ai-patterns.d.ts +1 -1
  429. package/dist/validate/prompts/modules/ai-patterns.d.ts.map +1 -1
  430. package/dist/validate/prompts/modules/ai-patterns.js +16 -0
  431. package/dist/validate/prompts/modules/ai-patterns.js.map +1 -1
  432. package/dist/validate/prompts/modules/common.d.ts +1 -1
  433. package/dist/validate/prompts/modules/common.d.ts.map +1 -1
  434. package/dist/validate/prompts/modules/common.js +12 -3
  435. package/dist/validate/prompts/modules/common.js.map +1 -1
  436. package/dist/validate/providers/anthropic.d.ts +4 -4
  437. package/dist/validate/providers/anthropic.d.ts.map +1 -1
  438. package/dist/validate/providers/anthropic.js +85 -58
  439. package/dist/validate/providers/anthropic.js.map +1 -1
  440. package/dist/validate/providers/openai.d.ts +4 -4
  441. package/dist/validate/providers/openai.d.ts.map +1 -1
  442. package/dist/validate/providers/openai.js +149 -99
  443. package/dist/validate/providers/openai.js.map +1 -1
  444. package/dist/validate/request-builder.d.ts +2 -8
  445. package/dist/validate/request-builder.d.ts.map +1 -1
  446. package/dist/validate/request-builder.js +4 -34
  447. package/dist/validate/request-builder.js.map +1 -1
  448. package/dist/validate/types.d.ts +9 -0
  449. package/dist/validate/types.d.ts.map +1 -1
  450. package/dist/validate/types.js.map +1 -1
  451. package/dist/validate/utils/path-helpers.js +2 -2
  452. package/dist/validate/utils/path-helpers.js.map +1 -1
  453. package/dist/validate/utils/response-parser.d.ts +10 -0
  454. package/dist/validate/utils/response-parser.d.ts.map +1 -1
  455. package/dist/validate/utils/response-parser.js +21 -2
  456. package/dist/validate/utils/response-parser.js.map +1 -1
  457. package/dist/validate/utils/retry.d.ts.map +1 -1
  458. package/dist/validate/utils/retry.js +19 -4
  459. package/dist/validate/utils/retry.js.map +1 -1
  460. package/package.json +7 -4
  461. package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +1 -1
  462. package/src/__tests__/benchmark/planted-benchmark.test.ts +337 -0
  463. package/src/__tests__/benchmark/utils/test-runner.ts +38 -4
  464. package/src/__tests__/category-filter.test.ts +5 -1
  465. package/src/__tests__/context-engine/route-discovery/python.test.ts +726 -0
  466. package/src/__tests__/detect/ast-rules.test.ts +1043 -0
  467. package/src/__tests__/detect/offline-mode.test.ts +147 -0
  468. package/src/__tests__/detect/python-ast-rules.test.ts +569 -0
  469. package/src/__tests__/detect/python-helpers.test.ts +536 -0
  470. package/src/__tests__/detect/python-sast-rules.test.ts +453 -0
  471. package/src/__tests__/detect/rules-file-backdoor-decoders.test.ts +151 -0
  472. package/src/__tests__/detect/rules-file-backdoor.test.ts +284 -0
  473. package/src/__tests__/detect/taint-fix-templates.test.ts +150 -0
  474. package/src/__tests__/detect/taint-path-serialization.test.ts +170 -0
  475. package/src/__tests__/parse/call-graph.test.ts +300 -0
  476. package/src/__tests__/parse/python-parser.test.ts +274 -0
  477. package/src/__tests__/regression/known-false-positives.test.ts +491 -9
  478. package/src/__tests__/regression/rules-file-backdoor.test.ts +137 -0
  479. package/src/__tests__/score/adjustments.test.ts +34 -16
  480. package/src/__tests__/score/confidence.test.ts +84 -57
  481. package/src/__tests__/score/evidence-scoring.test.ts +249 -0
  482. package/src/__tests__/score/evidence.test.ts +144 -0
  483. package/src/__tests__/score/scoring-integration.test.ts +56 -34
  484. package/src/__tests__/score/taint-adjustments.test.ts +14 -228
  485. package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +65 -59
  486. package/src/__tests__/snapshots/scan-depth.test.ts +39 -7
  487. package/src/__tests__/taint/async-flow.test.ts +247 -0
  488. package/src/__tests__/taint/cfg-builder.test.ts +835 -0
  489. package/src/__tests__/taint/constant-propagation.test.ts +302 -0
  490. package/src/__tests__/taint/cross-file-index.test.ts +683 -0
  491. package/src/__tests__/taint/cross-file-integration.test.ts +275 -0
  492. package/src/__tests__/taint/cross-file-propagation.test.ts +910 -0
  493. package/src/__tests__/taint/def-use.test.ts +132 -0
  494. package/src/__tests__/taint/field-sensitive-sinks.test.ts +179 -0
  495. package/src/__tests__/taint/field-sensitivity.test.ts +342 -0
  496. package/src/__tests__/taint/file-analysis-cache.test.ts +290 -0
  497. package/src/__tests__/taint/framework-models.test.ts +227 -0
  498. package/src/__tests__/taint/llm-flow-graph.test.ts +850 -0
  499. package/src/__tests__/taint/llm-risk-scoring.test.ts +439 -0
  500. package/src/__tests__/taint/performance-parity.test.ts +315 -0
  501. package/src/__tests__/taint/propagation.test.ts +621 -0
  502. package/src/__tests__/taint/python-cross-file.test.ts +494 -0
  503. package/src/__tests__/taint/python-taint.test.ts +1344 -0
  504. package/src/__tests__/taint/sanitizer-registry.test.ts +304 -0
  505. package/src/__tests__/taint/sanitizer-regression.test.ts +111 -0
  506. package/src/__tests__/taint/sink-classifier.test.ts +537 -0
  507. package/src/__tests__/taint/source-classifier.test.ts +367 -0
  508. package/src/__tests__/taint/taint-pipeline.test.ts +418 -0
  509. package/src/__tests__/taint/taint-smoke.test.ts +400 -0
  510. package/src/__tests__/taint/taint-summary.test.ts +472 -0
  511. package/src/detect/ai-code/index.ts +6 -11
  512. package/src/detect/ast-rules/agent-tools-ast.ts +861 -0
  513. package/src/detect/ast-rules/ai-fingerprinting-ast.ts +451 -0
  514. package/src/detect/ast-rules/auth-patterns-ast.ts +304 -0
  515. package/src/detect/ast-rules/byok-ast.ts +195 -0
  516. package/src/detect/ast-rules/child-process-ast.ts +276 -0
  517. package/src/detect/ast-rules/dangerous-eval-ast.ts +227 -0
  518. package/src/detect/ast-rules/data-exposure-ast.ts +162 -0
  519. package/src/detect/ast-rules/dom-xss-ast.ts +260 -0
  520. package/src/detect/ast-rules/endpoint-protection-ast.ts +231 -0
  521. package/src/detect/ast-rules/entropy-ast.ts +268 -0
  522. package/src/detect/ast-rules/flask-debug-ast.ts +148 -0
  523. package/src/detect/ast-rules/framework-checks-ast.ts +200 -0
  524. package/src/detect/ast-rules/helpers/call-analysis.ts +256 -0
  525. package/src/detect/ast-rules/helpers/context-detection.ts +277 -0
  526. package/src/detect/ast-rules/helpers/control-flow.ts +179 -0
  527. package/src/detect/ast-rules/helpers/import-analysis.ts +185 -0
  528. package/src/detect/ast-rules/helpers/index.ts +133 -0
  529. package/src/detect/ast-rules/helpers/python-helpers.ts +1054 -0
  530. package/src/detect/ast-rules/helpers/scope-analysis.ts +224 -0
  531. package/src/detect/ast-rules/helpers/string-analysis.ts +215 -0
  532. package/src/detect/ast-rules/helpers/type-extraction.ts +138 -0
  533. package/src/detect/ast-rules/helpers/user-input.ts +256 -0
  534. package/src/detect/ast-rules/index.ts +311 -0
  535. package/src/detect/ast-rules/json-parse-ast.ts +162 -0
  536. package/src/detect/ast-rules/log-injection-ast.ts +243 -0
  537. package/src/detect/ast-rules/logic-gates-ast.ts +343 -0
  538. package/src/detect/ast-rules/mcp-security-ast.ts +808 -0
  539. package/src/detect/ast-rules/model-supply-chain-ast.ts +202 -0
  540. package/src/detect/ast-rules/package-hallucination-ast.ts +664 -0
  541. package/src/detect/ast-rules/prompt-hygiene-ast.ts +329 -0
  542. package/src/detect/ast-rules/rag-safety-ast.ts +689 -0
  543. package/src/detect/ast-rules/request-validation-ast.ts +122 -0
  544. package/src/detect/ast-rules/risky-imports-ast.ts +133 -0
  545. package/src/detect/ast-rules/schema-validation-ast.ts +244 -0
  546. package/src/detect/ast-rules/secret-patterns-ast.ts +223 -0
  547. package/src/detect/ast-rules/security-headers-ast.ts +206 -0
  548. package/src/detect/ast-rules/sql-injection-ast.ts +614 -0
  549. package/src/detect/ast-rules/ssrf-ast.ts +601 -0
  550. package/src/detect/ast-rules/taint-fix-templates.ts +108 -0
  551. package/src/detect/ast-rules/taint-flow-ast.ts +416 -0
  552. package/src/detect/ast-rules/variables-ast.ts +446 -0
  553. package/src/detect/ast-rules/weak-crypto-ast.ts +441 -0
  554. package/src/detect/ast-rules/xxe-ast.ts +184 -0
  555. package/src/detect/config/agent-skill-injection.ts +2 -24
  556. package/src/detect/config/index.ts +1 -0
  557. package/src/detect/config/osv-check.ts +6 -1
  558. package/src/detect/config/package-check.ts +6 -1
  559. package/src/detect/config/rules-file-backdoor.ts +438 -0
  560. package/src/detect/index.ts +146 -52
  561. package/src/detect/secrets/config-audit.ts +37 -3
  562. package/src/detect/secrets/entropy.ts +195 -0
  563. package/src/detect/secrets/index.ts +7 -16
  564. package/src/detect/structural/index.ts +23 -566
  565. package/src/index.ts +7 -0
  566. package/src/model/auth-helper-detector.ts +1 -7
  567. package/src/model/import-resolver.ts +104 -0
  568. package/src/model/imported-auth-detector.ts +1 -1
  569. package/src/model/index.ts +240 -80
  570. package/src/model/module-graph.ts +17 -5
  571. package/src/model/project-context.ts +28 -1
  572. package/src/model/route-auth-resolver.ts +18 -3
  573. package/src/model/route-discovery/index.ts +1 -1
  574. package/src/model/route-discovery/nextjs.ts +1 -1
  575. package/src/model/route-discovery/python.ts +156 -9
  576. package/src/model/route-discovery/types.ts +1 -1
  577. package/src/model/route-discovery/utils.ts +73 -0
  578. package/src/model/taint-types.ts +1 -6
  579. package/src/parse/ast.ts +271 -0
  580. package/src/parse/call-graph.ts +419 -0
  581. package/src/parse/file-classifier.ts +69 -15
  582. package/src/parse/node-index.ts +118 -0
  583. package/src/parse/type-extractor.ts +293 -0
  584. package/src/pipeline/config.ts +7 -0
  585. package/src/pipeline/index.ts +464 -199
  586. package/src/pipeline/modes/incremental.ts +1 -7
  587. package/src/postprocess/dedup.ts +48 -17
  588. package/src/report/build-result.ts +57 -29
  589. package/src/report/formatters/cli-terminal.ts +731 -415
  590. package/src/report/sanitize.ts +27 -0
  591. package/src/score/adjustments.ts +113 -40
  592. package/src/score/confidence.ts +10 -5
  593. package/src/score/evidence.ts +55 -0
  594. package/src/score/index.ts +27 -55
  595. package/src/score/types.ts +4 -0
  596. package/src/shared/category-filter.ts +12 -0
  597. package/src/shared/regex-utils.ts +4 -0
  598. package/src/shared/registry-clients.ts +106 -18
  599. package/src/shared/rules/__tests__/metadata.test.ts +5 -1
  600. package/src/shared/rules/metadata.ts +19 -0
  601. package/src/shared/types.ts +372 -253
  602. package/src/taint/async-flow.ts +301 -0
  603. package/src/taint/cfg-builder.ts +1127 -0
  604. package/src/taint/cfg-types.ts +110 -0
  605. package/src/taint/constant-propagation.ts +170 -0
  606. package/src/taint/cross-file-analyzer.ts +118 -0
  607. package/src/taint/cross-file-index.ts +275 -0
  608. package/src/taint/def-use.ts +556 -0
  609. package/src/taint/file-analysis-cache.ts +145 -0
  610. package/src/taint/framework-models.ts +313 -0
  611. package/src/taint/helpers.ts +138 -0
  612. package/src/taint/index.ts +71 -0
  613. package/src/taint/llm-registry.ts +174 -0
  614. package/src/taint/llm-risk-scoring.ts +412 -0
  615. package/src/taint/propagation-types.ts +188 -0
  616. package/src/taint/propagation.ts +1750 -0
  617. package/src/taint/sanitizer-registry.ts +490 -0
  618. package/src/taint/sink-classifier.ts +1402 -0
  619. package/src/taint/source-classifier.ts +859 -0
  620. package/src/taint/taint-analyzer.ts +112 -0
  621. package/src/taint/taint-summary.ts +341 -0
  622. package/src/taint/types.ts +86 -0
  623. package/src/validate/clients.ts +3 -2
  624. package/src/validate/index.ts +89 -53
  625. package/src/validate/prompts/modules/ai-patterns.ts +16 -0
  626. package/src/validate/prompts/modules/common.ts +12 -3
  627. package/src/validate/providers/anthropic.ts +254 -148
  628. package/src/validate/providers/openai.ts +363 -218
  629. package/src/validate/request-builder.ts +2 -45
  630. package/src/validate/types.ts +9 -0
  631. package/src/validate/utils/path-helpers.ts +2 -2
  632. package/src/validate/utils/response-parser.ts +32 -3
  633. package/src/validate/utils/retry.ts +19 -4
  634. package/dist/ai-context/index.d.ts +0 -6
  635. package/dist/ai-context/index.d.ts.map +0 -1
  636. package/dist/ai-context/index.js +0 -13
  637. package/dist/ai-context/index.js.map +0 -1
  638. package/dist/ai-context/manager.d.ts +0 -67
  639. package/dist/ai-context/manager.d.ts.map +0 -1
  640. package/dist/ai-context/manager.js +0 -104
  641. package/dist/ai-context/manager.js.map +0 -1
  642. package/dist/baseline/diff.d.ts +0 -32
  643. package/dist/baseline/diff.d.ts.map +0 -1
  644. package/dist/baseline/diff.js +0 -119
  645. package/dist/baseline/diff.js.map +0 -1
  646. package/dist/baseline/index.d.ts +0 -9
  647. package/dist/baseline/index.d.ts.map +0 -1
  648. package/dist/baseline/index.js +0 -19
  649. package/dist/baseline/index.js.map +0 -1
  650. package/dist/baseline/manager.d.ts +0 -67
  651. package/dist/baseline/manager.d.ts.map +0 -1
  652. package/dist/baseline/manager.js +0 -180
  653. package/dist/baseline/manager.js.map +0 -1
  654. package/dist/baseline/types.d.ts +0 -91
  655. package/dist/baseline/types.d.ts.map +0 -1
  656. package/dist/baseline/types.js +0 -12
  657. package/dist/baseline/types.js.map +0 -1
  658. package/dist/category-filter.d.ts +0 -125
  659. package/dist/category-filter.d.ts.map +0 -1
  660. package/dist/category-filter.js +0 -360
  661. package/dist/category-filter.js.map +0 -1
  662. package/dist/detect/ai-code/agent-tools.d.ts +0 -22
  663. package/dist/detect/ai-code/agent-tools.d.ts.map +0 -1
  664. package/dist/detect/ai-code/agent-tools.js +0 -1509
  665. package/dist/detect/ai-code/agent-tools.js.map +0 -1
  666. package/dist/detect/ai-code/byok-patterns.d.ts +0 -15
  667. package/dist/detect/ai-code/byok-patterns.d.ts.map +0 -1
  668. package/dist/detect/ai-code/byok-patterns.js +0 -313
  669. package/dist/detect/ai-code/byok-patterns.js.map +0 -1
  670. package/dist/detect/ai-code/endpoint-protection.d.ts +0 -38
  671. package/dist/detect/ai-code/endpoint-protection.d.ts.map +0 -1
  672. package/dist/detect/ai-code/endpoint-protection.js +0 -349
  673. package/dist/detect/ai-code/endpoint-protection.js.map +0 -1
  674. package/dist/detect/ai-code/execution-sinks.d.ts +0 -21
  675. package/dist/detect/ai-code/execution-sinks.d.ts.map +0 -1
  676. package/dist/detect/ai-code/execution-sinks.js +0 -1158
  677. package/dist/detect/ai-code/execution-sinks.js.map +0 -1
  678. package/dist/detect/ai-code/fingerprinting.d.ts +0 -10
  679. package/dist/detect/ai-code/fingerprinting.d.ts.map +0 -1
  680. package/dist/detect/ai-code/fingerprinting.js +0 -665
  681. package/dist/detect/ai-code/fingerprinting.js.map +0 -1
  682. package/dist/detect/ai-code/mcp-security.d.ts +0 -20
  683. package/dist/detect/ai-code/mcp-security.d.ts.map +0 -1
  684. package/dist/detect/ai-code/mcp-security.js +0 -880
  685. package/dist/detect/ai-code/mcp-security.js.map +0 -1
  686. package/dist/detect/ai-code/model-supply-chain.d.ts +0 -23
  687. package/dist/detect/ai-code/model-supply-chain.d.ts.map +0 -1
  688. package/dist/detect/ai-code/model-supply-chain.js +0 -447
  689. package/dist/detect/ai-code/model-supply-chain.js.map +0 -1
  690. package/dist/detect/ai-code/package-hallucination.d.ts +0 -22
  691. package/dist/detect/ai-code/package-hallucination.d.ts.map +0 -1
  692. package/dist/detect/ai-code/package-hallucination.js +0 -841
  693. package/dist/detect/ai-code/package-hallucination.js.map +0 -1
  694. package/dist/detect/ai-code/prompt-hygiene.d.ts +0 -22
  695. package/dist/detect/ai-code/prompt-hygiene.d.ts.map +0 -1
  696. package/dist/detect/ai-code/prompt-hygiene.js +0 -1177
  697. package/dist/detect/ai-code/prompt-hygiene.js.map +0 -1
  698. package/dist/detect/ai-code/rag-safety.d.ts +0 -24
  699. package/dist/detect/ai-code/rag-safety.d.ts.map +0 -1
  700. package/dist/detect/ai-code/rag-safety.js +0 -913
  701. package/dist/detect/ai-code/rag-safety.js.map +0 -1
  702. package/dist/detect/ai-code/schema-validation.d.ts +0 -28
  703. package/dist/detect/ai-code/schema-validation.d.ts.map +0 -1
  704. package/dist/detect/ai-code/schema-validation.js +0 -378
  705. package/dist/detect/ai-code/schema-validation.js.map +0 -1
  706. package/dist/detect/secrets/patterns.d.ts +0 -11
  707. package/dist/detect/secrets/patterns.d.ts.map +0 -1
  708. package/dist/detect/secrets/patterns.js +0 -518
  709. package/dist/detect/secrets/patterns.js.map +0 -1
  710. package/dist/detect/secrets/weak-crypto.d.ts +0 -10
  711. package/dist/detect/secrets/weak-crypto.d.ts.map +0 -1
  712. package/dist/detect/secrets/weak-crypto.js +0 -432
  713. package/dist/detect/secrets/weak-crypto.js.map +0 -1
  714. package/dist/detect/structural/auth-patterns.d.ts +0 -22
  715. package/dist/detect/structural/auth-patterns.d.ts.map +0 -1
  716. package/dist/detect/structural/auth-patterns.js +0 -533
  717. package/dist/detect/structural/auth-patterns.js.map +0 -1
  718. package/dist/detect/structural/dangerous-functions/child-process.d.ts +0 -16
  719. package/dist/detect/structural/dangerous-functions/child-process.d.ts.map +0 -1
  720. package/dist/detect/structural/dangerous-functions/child-process.js +0 -74
  721. package/dist/detect/structural/dangerous-functions/child-process.js.map +0 -1
  722. package/dist/detect/structural/dangerous-functions/dom-xss.d.ts +0 -34
  723. package/dist/detect/structural/dangerous-functions/dom-xss.d.ts.map +0 -1
  724. package/dist/detect/structural/dangerous-functions/dom-xss.js +0 -230
  725. package/dist/detect/structural/dangerous-functions/dom-xss.js.map +0 -1
  726. package/dist/detect/structural/dangerous-functions/index.d.ts +0 -16
  727. package/dist/detect/structural/dangerous-functions/index.d.ts.map +0 -1
  728. package/dist/detect/structural/dangerous-functions/index.js +0 -1193
  729. package/dist/detect/structural/dangerous-functions/index.js.map +0 -1
  730. package/dist/detect/structural/dangerous-functions/json-parse.d.ts +0 -31
  731. package/dist/detect/structural/dangerous-functions/json-parse.d.ts.map +0 -1
  732. package/dist/detect/structural/dangerous-functions/json-parse.js +0 -326
  733. package/dist/detect/structural/dangerous-functions/json-parse.js.map +0 -1
  734. package/dist/detect/structural/dangerous-functions/math-random.d.ts +0 -111
  735. package/dist/detect/structural/dangerous-functions/math-random.d.ts.map +0 -1
  736. package/dist/detect/structural/dangerous-functions/math-random.js +0 -684
  737. package/dist/detect/structural/dangerous-functions/math-random.js.map +0 -1
  738. package/dist/detect/structural/dangerous-functions/patterns.d.ts +0 -21
  739. package/dist/detect/structural/dangerous-functions/patterns.d.ts.map +0 -1
  740. package/dist/detect/structural/dangerous-functions/patterns.js +0 -163
  741. package/dist/detect/structural/dangerous-functions/patterns.js.map +0 -1
  742. package/dist/detect/structural/dangerous-functions/request-validation.d.ts +0 -13
  743. package/dist/detect/structural/dangerous-functions/request-validation.d.ts.map +0 -1
  744. package/dist/detect/structural/dangerous-functions/request-validation.js +0 -126
  745. package/dist/detect/structural/dangerous-functions/request-validation.js.map +0 -1
  746. package/dist/detect/structural/dangerous-functions/utils/control-flow.d.ts +0 -24
  747. package/dist/detect/structural/dangerous-functions/utils/control-flow.d.ts.map +0 -1
  748. package/dist/detect/structural/dangerous-functions/utils/control-flow.js +0 -70
  749. package/dist/detect/structural/dangerous-functions/utils/control-flow.js.map +0 -1
  750. package/dist/detect/structural/dangerous-functions/utils/helpers.d.ts +0 -31
  751. package/dist/detect/structural/dangerous-functions/utils/helpers.d.ts.map +0 -1
  752. package/dist/detect/structural/dangerous-functions/utils/helpers.js +0 -147
  753. package/dist/detect/structural/dangerous-functions/utils/helpers.js.map +0 -1
  754. package/dist/detect/structural/dangerous-functions/utils/index.d.ts +0 -9
  755. package/dist/detect/structural/dangerous-functions/utils/index.d.ts.map +0 -1
  756. package/dist/detect/structural/dangerous-functions/utils/index.js +0 -23
  757. package/dist/detect/structural/dangerous-functions/utils/index.js.map +0 -1
  758. package/dist/detect/structural/dangerous-functions/utils/schema-validation.d.ts +0 -22
  759. package/dist/detect/structural/dangerous-functions/utils/schema-validation.d.ts.map +0 -1
  760. package/dist/detect/structural/dangerous-functions/utils/schema-validation.js +0 -102
  761. package/dist/detect/structural/dangerous-functions/utils/schema-validation.js.map +0 -1
  762. package/dist/detect/structural/data-exposure.d.ts +0 -19
  763. package/dist/detect/structural/data-exposure.d.ts.map +0 -1
  764. package/dist/detect/structural/data-exposure.js +0 -262
  765. package/dist/detect/structural/data-exposure.js.map +0 -1
  766. package/dist/detect/structural/framework-checks.d.ts +0 -10
  767. package/dist/detect/structural/framework-checks.d.ts.map +0 -1
  768. package/dist/detect/structural/framework-checks.js +0 -389
  769. package/dist/detect/structural/framework-checks.js.map +0 -1
  770. package/dist/detect/structural/log-injection.d.ts +0 -18
  771. package/dist/detect/structural/log-injection.d.ts.map +0 -1
  772. package/dist/detect/structural/log-injection.js +0 -217
  773. package/dist/detect/structural/log-injection.js.map +0 -1
  774. package/dist/detect/structural/logic-gates.d.ts +0 -10
  775. package/dist/detect/structural/logic-gates.d.ts.map +0 -1
  776. package/dist/detect/structural/logic-gates.js +0 -227
  777. package/dist/detect/structural/logic-gates.js.map +0 -1
  778. package/dist/detect/structural/risky-imports.d.ts +0 -10
  779. package/dist/detect/structural/risky-imports.d.ts.map +0 -1
  780. package/dist/detect/structural/risky-imports.js +0 -168
  781. package/dist/detect/structural/risky-imports.js.map +0 -1
  782. package/dist/detect/structural/security-headers.d.ts +0 -18
  783. package/dist/detect/structural/security-headers.d.ts.map +0 -1
  784. package/dist/detect/structural/security-headers.js +0 -196
  785. package/dist/detect/structural/security-headers.js.map +0 -1
  786. package/dist/detect/structural/ssrf-detection.d.ts +0 -18
  787. package/dist/detect/structural/ssrf-detection.d.ts.map +0 -1
  788. package/dist/detect/structural/ssrf-detection.js +0 -263
  789. package/dist/detect/structural/ssrf-detection.js.map +0 -1
  790. package/dist/detect/structural/variables.d.ts +0 -11
  791. package/dist/detect/structural/variables.d.ts.map +0 -1
  792. package/dist/detect/structural/variables.js +0 -159
  793. package/dist/detect/structural/variables.js.map +0 -1
  794. package/dist/detect/structural/xxe-detection.d.ts +0 -18
  795. package/dist/detect/structural/xxe-detection.d.ts.map +0 -1
  796. package/dist/detect/structural/xxe-detection.js +0 -245
  797. package/dist/detect/structural/xxe-detection.js.map +0 -1
  798. package/dist/filtering/context-adjustments.d.ts +0 -23
  799. package/dist/filtering/context-adjustments.d.ts.map +0 -1
  800. package/dist/filtering/context-adjustments.js +0 -100
  801. package/dist/filtering/context-adjustments.js.map +0 -1
  802. package/dist/filtering/index.d.ts +0 -3
  803. package/dist/filtering/index.d.ts.map +0 -1
  804. package/dist/filtering/index.js +0 -8
  805. package/dist/filtering/index.js.map +0 -1
  806. package/dist/filtering/pipeline.d.ts +0 -48
  807. package/dist/filtering/pipeline.d.ts.map +0 -1
  808. package/dist/filtering/pipeline.js +0 -76
  809. package/dist/filtering/pipeline.js.map +0 -1
  810. package/dist/formatters/ai-context.d.ts +0 -23
  811. package/dist/formatters/ai-context.d.ts.map +0 -1
  812. package/dist/formatters/ai-context.js +0 -238
  813. package/dist/formatters/ai-context.js.map +0 -1
  814. package/dist/formatters/cli-terminal.d.ts +0 -65
  815. package/dist/formatters/cli-terminal.d.ts.map +0 -1
  816. package/dist/formatters/cli-terminal.js +0 -735
  817. package/dist/formatters/cli-terminal.js.map +0 -1
  818. package/dist/formatters/github-comment.d.ts +0 -41
  819. package/dist/formatters/github-comment.d.ts.map +0 -1
  820. package/dist/formatters/github-comment.js +0 -370
  821. package/dist/formatters/github-comment.js.map +0 -1
  822. package/dist/formatters/grouping.d.ts +0 -52
  823. package/dist/formatters/grouping.d.ts.map +0 -1
  824. package/dist/formatters/grouping.js +0 -152
  825. package/dist/formatters/grouping.js.map +0 -1
  826. package/dist/formatters/ide/claude-code.d.ts +0 -17
  827. package/dist/formatters/ide/claude-code.d.ts.map +0 -1
  828. package/dist/formatters/ide/claude-code.js +0 -94
  829. package/dist/formatters/ide/claude-code.js.map +0 -1
  830. package/dist/formatters/ide/cursor.d.ts +0 -13
  831. package/dist/formatters/ide/cursor.d.ts.map +0 -1
  832. package/dist/formatters/ide/cursor.js +0 -125
  833. package/dist/formatters/ide/cursor.js.map +0 -1
  834. package/dist/formatters/ide/index.d.ts +0 -62
  835. package/dist/formatters/ide/index.d.ts.map +0 -1
  836. package/dist/formatters/ide/index.js +0 -184
  837. package/dist/formatters/ide/index.js.map +0 -1
  838. package/dist/formatters/ide/windsurf.d.ts +0 -13
  839. package/dist/formatters/ide/windsurf.d.ts.map +0 -1
  840. package/dist/formatters/ide/windsurf.js +0 -117
  841. package/dist/formatters/ide/windsurf.js.map +0 -1
  842. package/dist/formatters/index.d.ts +0 -11
  843. package/dist/formatters/index.d.ts.map +0 -1
  844. package/dist/formatters/index.js +0 -54
  845. package/dist/formatters/index.js.map +0 -1
  846. package/dist/formatters/vscode-diagnostic.d.ts +0 -103
  847. package/dist/formatters/vscode-diagnostic.d.ts.map +0 -1
  848. package/dist/formatters/vscode-diagnostic.js +0 -151
  849. package/dist/formatters/vscode-diagnostic.js.map +0 -1
  850. package/dist/layer1/comments.d.ts +0 -11
  851. package/dist/layer1/comments.d.ts.map +0 -1
  852. package/dist/layer1/comments.js +0 -203
  853. package/dist/layer1/comments.js.map +0 -1
  854. package/dist/layer1/config-audit.d.ts +0 -11
  855. package/dist/layer1/config-audit.d.ts.map +0 -1
  856. package/dist/layer1/config-audit.js +0 -311
  857. package/dist/layer1/config-audit.js.map +0 -1
  858. package/dist/layer1/config-mcp-audit.d.ts +0 -23
  859. package/dist/layer1/config-mcp-audit.d.ts.map +0 -1
  860. package/dist/layer1/config-mcp-audit.js +0 -239
  861. package/dist/layer1/config-mcp-audit.js.map +0 -1
  862. package/dist/layer1/entropy.d.ts +0 -11
  863. package/dist/layer1/entropy.d.ts.map +0 -1
  864. package/dist/layer1/entropy.js +0 -741
  865. package/dist/layer1/entropy.js.map +0 -1
  866. package/dist/layer1/file-flags.d.ts +0 -10
  867. package/dist/layer1/file-flags.d.ts.map +0 -1
  868. package/dist/layer1/file-flags.js +0 -119
  869. package/dist/layer1/file-flags.js.map +0 -1
  870. package/dist/layer1/index.d.ts +0 -38
  871. package/dist/layer1/index.d.ts.map +0 -1
  872. package/dist/layer1/index.js +0 -170
  873. package/dist/layer1/index.js.map +0 -1
  874. package/dist/layer1/patterns.d.ts +0 -11
  875. package/dist/layer1/patterns.d.ts.map +0 -1
  876. package/dist/layer1/patterns.js +0 -512
  877. package/dist/layer1/patterns.js.map +0 -1
  878. package/dist/layer1/urls.d.ts +0 -11
  879. package/dist/layer1/urls.d.ts.map +0 -1
  880. package/dist/layer1/urls.js +0 -444
  881. package/dist/layer1/urls.js.map +0 -1
  882. package/dist/layer1/weak-crypto.d.ts +0 -10
  883. package/dist/layer1/weak-crypto.d.ts.map +0 -1
  884. package/dist/layer1/weak-crypto.js +0 -428
  885. package/dist/layer1/weak-crypto.js.map +0 -1
  886. package/dist/layer2/ai-agent-tools.d.ts +0 -22
  887. package/dist/layer2/ai-agent-tools.d.ts.map +0 -1
  888. package/dist/layer2/ai-agent-tools.js +0 -1490
  889. package/dist/layer2/ai-agent-tools.js.map +0 -1
  890. package/dist/layer2/ai-endpoint-protection.d.ts +0 -38
  891. package/dist/layer2/ai-endpoint-protection.d.ts.map +0 -1
  892. package/dist/layer2/ai-endpoint-protection.js +0 -346
  893. package/dist/layer2/ai-endpoint-protection.js.map +0 -1
  894. package/dist/layer2/ai-execution-sinks.d.ts +0 -21
  895. package/dist/layer2/ai-execution-sinks.d.ts.map +0 -1
  896. package/dist/layer2/ai-execution-sinks.js +0 -1155
  897. package/dist/layer2/ai-execution-sinks.js.map +0 -1
  898. package/dist/layer2/ai-fingerprinting.d.ts +0 -10
  899. package/dist/layer2/ai-fingerprinting.d.ts.map +0 -1
  900. package/dist/layer2/ai-fingerprinting.js +0 -650
  901. package/dist/layer2/ai-fingerprinting.js.map +0 -1
  902. package/dist/layer2/ai-mcp-security.d.ts +0 -20
  903. package/dist/layer2/ai-mcp-security.d.ts.map +0 -1
  904. package/dist/layer2/ai-mcp-security.js +0 -877
  905. package/dist/layer2/ai-mcp-security.js.map +0 -1
  906. package/dist/layer2/ai-package-hallucination.d.ts +0 -22
  907. package/dist/layer2/ai-package-hallucination.d.ts.map +0 -1
  908. package/dist/layer2/ai-package-hallucination.js +0 -828
  909. package/dist/layer2/ai-package-hallucination.js.map +0 -1
  910. package/dist/layer2/ai-prompt-hygiene.d.ts +0 -22
  911. package/dist/layer2/ai-prompt-hygiene.d.ts.map +0 -1
  912. package/dist/layer2/ai-prompt-hygiene.js +0 -1156
  913. package/dist/layer2/ai-prompt-hygiene.js.map +0 -1
  914. package/dist/layer2/ai-rag-safety.d.ts +0 -24
  915. package/dist/layer2/ai-rag-safety.d.ts.map +0 -1
  916. package/dist/layer2/ai-rag-safety.js +0 -910
  917. package/dist/layer2/ai-rag-safety.js.map +0 -1
  918. package/dist/layer2/ai-schema-validation.d.ts +0 -28
  919. package/dist/layer2/ai-schema-validation.d.ts.map +0 -1
  920. package/dist/layer2/ai-schema-validation.js +0 -375
  921. package/dist/layer2/ai-schema-validation.js.map +0 -1
  922. package/dist/layer2/auth-antipatterns.d.ts +0 -22
  923. package/dist/layer2/auth-antipatterns.d.ts.map +0 -1
  924. package/dist/layer2/auth-antipatterns.js +0 -522
  925. package/dist/layer2/auth-antipatterns.js.map +0 -1
  926. package/dist/layer2/byok-patterns.d.ts +0 -15
  927. package/dist/layer2/byok-patterns.d.ts.map +0 -1
  928. package/dist/layer2/byok-patterns.js +0 -302
  929. package/dist/layer2/byok-patterns.js.map +0 -1
  930. package/dist/layer2/dangerous-functions/child-process.d.ts +0 -16
  931. package/dist/layer2/dangerous-functions/child-process.d.ts.map +0 -1
  932. package/dist/layer2/dangerous-functions/child-process.js +0 -74
  933. package/dist/layer2/dangerous-functions/child-process.js.map +0 -1
  934. package/dist/layer2/dangerous-functions/dom-xss.d.ts +0 -34
  935. package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +0 -1
  936. package/dist/layer2/dangerous-functions/dom-xss.js +0 -230
  937. package/dist/layer2/dangerous-functions/dom-xss.js.map +0 -1
  938. package/dist/layer2/dangerous-functions/index.d.ts +0 -16
  939. package/dist/layer2/dangerous-functions/index.d.ts.map +0 -1
  940. package/dist/layer2/dangerous-functions/index.js +0 -1152
  941. package/dist/layer2/dangerous-functions/index.js.map +0 -1
  942. package/dist/layer2/dangerous-functions/json-parse.d.ts +0 -31
  943. package/dist/layer2/dangerous-functions/json-parse.d.ts.map +0 -1
  944. package/dist/layer2/dangerous-functions/json-parse.js +0 -319
  945. package/dist/layer2/dangerous-functions/json-parse.js.map +0 -1
  946. package/dist/layer2/dangerous-functions/math-random.d.ts +0 -111
  947. package/dist/layer2/dangerous-functions/math-random.d.ts.map +0 -1
  948. package/dist/layer2/dangerous-functions/math-random.js +0 -684
  949. package/dist/layer2/dangerous-functions/math-random.js.map +0 -1
  950. package/dist/layer2/dangerous-functions/patterns.d.ts +0 -21
  951. package/dist/layer2/dangerous-functions/patterns.d.ts.map +0 -1
  952. package/dist/layer2/dangerous-functions/patterns.js +0 -163
  953. package/dist/layer2/dangerous-functions/patterns.js.map +0 -1
  954. package/dist/layer2/dangerous-functions/request-validation.d.ts +0 -13
  955. package/dist/layer2/dangerous-functions/request-validation.d.ts.map +0 -1
  956. package/dist/layer2/dangerous-functions/request-validation.js +0 -119
  957. package/dist/layer2/dangerous-functions/request-validation.js.map +0 -1
  958. package/dist/layer2/dangerous-functions/utils/control-flow.d.ts +0 -24
  959. package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +0 -1
  960. package/dist/layer2/dangerous-functions/utils/control-flow.js +0 -70
  961. package/dist/layer2/dangerous-functions/utils/control-flow.js.map +0 -1
  962. package/dist/layer2/dangerous-functions/utils/helpers.d.ts +0 -31
  963. package/dist/layer2/dangerous-functions/utils/helpers.d.ts.map +0 -1
  964. package/dist/layer2/dangerous-functions/utils/helpers.js +0 -147
  965. package/dist/layer2/dangerous-functions/utils/helpers.js.map +0 -1
  966. package/dist/layer2/dangerous-functions/utils/index.d.ts +0 -9
  967. package/dist/layer2/dangerous-functions/utils/index.d.ts.map +0 -1
  968. package/dist/layer2/dangerous-functions/utils/index.js +0 -23
  969. package/dist/layer2/dangerous-functions/utils/index.js.map +0 -1
  970. package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts +0 -22
  971. package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts.map +0 -1
  972. package/dist/layer2/dangerous-functions/utils/schema-validation.js +0 -102
  973. package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +0 -1
  974. package/dist/layer2/data-exposure.d.ts +0 -19
  975. package/dist/layer2/data-exposure.d.ts.map +0 -1
  976. package/dist/layer2/data-exposure.js +0 -255
  977. package/dist/layer2/data-exposure.js.map +0 -1
  978. package/dist/layer2/framework-checks.d.ts +0 -10
  979. package/dist/layer2/framework-checks.d.ts.map +0 -1
  980. package/dist/layer2/framework-checks.js +0 -384
  981. package/dist/layer2/framework-checks.js.map +0 -1
  982. package/dist/layer2/index.d.ts +0 -74
  983. package/dist/layer2/index.d.ts.map +0 -1
  984. package/dist/layer2/index.js +0 -544
  985. package/dist/layer2/index.js.map +0 -1
  986. package/dist/layer2/log-injection.d.ts +0 -18
  987. package/dist/layer2/log-injection.d.ts.map +0 -1
  988. package/dist/layer2/log-injection.js +0 -214
  989. package/dist/layer2/log-injection.js.map +0 -1
  990. package/dist/layer2/logic-gates.d.ts +0 -10
  991. package/dist/layer2/logic-gates.d.ts.map +0 -1
  992. package/dist/layer2/logic-gates.js +0 -220
  993. package/dist/layer2/logic-gates.js.map +0 -1
  994. package/dist/layer2/model-supply-chain.d.ts +0 -23
  995. package/dist/layer2/model-supply-chain.d.ts.map +0 -1
  996. package/dist/layer2/model-supply-chain.js +0 -444
  997. package/dist/layer2/model-supply-chain.js.map +0 -1
  998. package/dist/layer2/risky-imports.d.ts +0 -10
  999. package/dist/layer2/risky-imports.d.ts.map +0 -1
  1000. package/dist/layer2/risky-imports.js +0 -165
  1001. package/dist/layer2/risky-imports.js.map +0 -1
  1002. package/dist/layer2/security-headers.d.ts +0 -18
  1003. package/dist/layer2/security-headers.d.ts.map +0 -1
  1004. package/dist/layer2/security-headers.js +0 -187
  1005. package/dist/layer2/security-headers.js.map +0 -1
  1006. package/dist/layer2/ssrf-detection.d.ts +0 -18
  1007. package/dist/layer2/ssrf-detection.d.ts.map +0 -1
  1008. package/dist/layer2/ssrf-detection.js +0 -252
  1009. package/dist/layer2/ssrf-detection.js.map +0 -1
  1010. package/dist/layer2/variables.d.ts +0 -11
  1011. package/dist/layer2/variables.d.ts.map +0 -1
  1012. package/dist/layer2/variables.js +0 -156
  1013. package/dist/layer2/variables.js.map +0 -1
  1014. package/dist/layer2/xxe-detection.d.ts +0 -18
  1015. package/dist/layer2/xxe-detection.d.ts.map +0 -1
  1016. package/dist/layer2/xxe-detection.js +0 -242
  1017. package/dist/layer2/xxe-detection.js.map +0 -1
  1018. package/dist/layer3/anthropic/auto-dismiss.d.ts +0 -24
  1019. package/dist/layer3/anthropic/auto-dismiss.d.ts.map +0 -1
  1020. package/dist/layer3/anthropic/auto-dismiss.js +0 -199
  1021. package/dist/layer3/anthropic/auto-dismiss.js.map +0 -1
  1022. package/dist/layer3/anthropic/clients.d.ts +0 -44
  1023. package/dist/layer3/anthropic/clients.d.ts.map +0 -1
  1024. package/dist/layer3/anthropic/clients.js +0 -81
  1025. package/dist/layer3/anthropic/clients.js.map +0 -1
  1026. package/dist/layer3/anthropic/index.d.ts +0 -41
  1027. package/dist/layer3/anthropic/index.d.ts.map +0 -1
  1028. package/dist/layer3/anthropic/index.js +0 -141
  1029. package/dist/layer3/anthropic/index.js.map +0 -1
  1030. package/dist/layer3/anthropic/prompts/index.d.ts +0 -8
  1031. package/dist/layer3/anthropic/prompts/index.d.ts.map +0 -1
  1032. package/dist/layer3/anthropic/prompts/index.js +0 -16
  1033. package/dist/layer3/anthropic/prompts/index.js.map +0 -1
  1034. package/dist/layer3/anthropic/prompts/modules/ai-patterns.d.ts +0 -19
  1035. package/dist/layer3/anthropic/prompts/modules/ai-patterns.d.ts.map +0 -1
  1036. package/dist/layer3/anthropic/prompts/modules/ai-patterns.js +0 -156
  1037. package/dist/layer3/anthropic/prompts/modules/ai-patterns.js.map +0 -1
  1038. package/dist/layer3/anthropic/prompts/modules/auth-access.d.ts +0 -9
  1039. package/dist/layer3/anthropic/prompts/modules/auth-access.d.ts.map +0 -1
  1040. package/dist/layer3/anthropic/prompts/modules/auth-access.js +0 -25
  1041. package/dist/layer3/anthropic/prompts/modules/auth-access.js.map +0 -1
  1042. package/dist/layer3/anthropic/prompts/modules/common.d.ts +0 -11
  1043. package/dist/layer3/anthropic/prompts/modules/common.d.ts.map +0 -1
  1044. package/dist/layer3/anthropic/prompts/modules/common.js +0 -152
  1045. package/dist/layer3/anthropic/prompts/modules/common.js.map +0 -1
  1046. package/dist/layer3/anthropic/prompts/modules/index.d.ts +0 -54
  1047. package/dist/layer3/anthropic/prompts/modules/index.d.ts.map +0 -1
  1048. package/dist/layer3/anthropic/prompts/modules/index.js +0 -185
  1049. package/dist/layer3/anthropic/prompts/modules/index.js.map +0 -1
  1050. package/dist/layer3/anthropic/prompts/modules/owasp-classic.d.ts +0 -8
  1051. package/dist/layer3/anthropic/prompts/modules/owasp-classic.d.ts.map +0 -1
  1052. package/dist/layer3/anthropic/prompts/modules/owasp-classic.js +0 -84
  1053. package/dist/layer3/anthropic/prompts/modules/owasp-classic.js.map +0 -1
  1054. package/dist/layer3/anthropic/prompts/modules/secrets-crypto.d.ts +0 -8
  1055. package/dist/layer3/anthropic/prompts/modules/secrets-crypto.d.ts.map +0 -1
  1056. package/dist/layer3/anthropic/prompts/modules/secrets-crypto.js +0 -68
  1057. package/dist/layer3/anthropic/prompts/modules/secrets-crypto.js.map +0 -1
  1058. package/dist/layer3/anthropic/prompts/modules/xss-prompt.d.ts +0 -8
  1059. package/dist/layer3/anthropic/prompts/modules/xss-prompt.d.ts.map +0 -1
  1060. package/dist/layer3/anthropic/prompts/modules/xss-prompt.js +0 -22
  1061. package/dist/layer3/anthropic/prompts/modules/xss-prompt.js.map +0 -1
  1062. package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts +0 -15
  1063. package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts.map +0 -1
  1064. package/dist/layer3/anthropic/prompts/semantic-analysis.js +0 -169
  1065. package/dist/layer3/anthropic/prompts/semantic-analysis.js.map +0 -1
  1066. package/dist/layer3/anthropic/prompts/validation.d.ts +0 -18
  1067. package/dist/layer3/anthropic/prompts/validation.d.ts.map +0 -1
  1068. package/dist/layer3/anthropic/prompts/validation.js +0 -25
  1069. package/dist/layer3/anthropic/prompts/validation.js.map +0 -1
  1070. package/dist/layer3/anthropic/providers/anthropic.d.ts +0 -21
  1071. package/dist/layer3/anthropic/providers/anthropic.d.ts.map +0 -1
  1072. package/dist/layer3/anthropic/providers/anthropic.js +0 -269
  1073. package/dist/layer3/anthropic/providers/anthropic.js.map +0 -1
  1074. package/dist/layer3/anthropic/providers/index.d.ts +0 -8
  1075. package/dist/layer3/anthropic/providers/index.d.ts.map +0 -1
  1076. package/dist/layer3/anthropic/providers/index.js +0 -15
  1077. package/dist/layer3/anthropic/providers/index.js.map +0 -1
  1078. package/dist/layer3/anthropic/providers/openai.d.ts +0 -18
  1079. package/dist/layer3/anthropic/providers/openai.d.ts.map +0 -1
  1080. package/dist/layer3/anthropic/providers/openai.js +0 -343
  1081. package/dist/layer3/anthropic/providers/openai.js.map +0 -1
  1082. package/dist/layer3/anthropic/request-builder.d.ts +0 -27
  1083. package/dist/layer3/anthropic/request-builder.d.ts.map +0 -1
  1084. package/dist/layer3/anthropic/request-builder.js +0 -150
  1085. package/dist/layer3/anthropic/request-builder.js.map +0 -1
  1086. package/dist/layer3/anthropic/types.d.ts +0 -88
  1087. package/dist/layer3/anthropic/types.d.ts.map +0 -1
  1088. package/dist/layer3/anthropic/types.js +0 -38
  1089. package/dist/layer3/anthropic/types.js.map +0 -1
  1090. package/dist/layer3/anthropic/utils/context-extractor.d.ts +0 -55
  1091. package/dist/layer3/anthropic/utils/context-extractor.d.ts.map +0 -1
  1092. package/dist/layer3/anthropic/utils/context-extractor.js +0 -161
  1093. package/dist/layer3/anthropic/utils/context-extractor.js.map +0 -1
  1094. package/dist/layer3/anthropic/utils/index.d.ts +0 -11
  1095. package/dist/layer3/anthropic/utils/index.d.ts.map +0 -1
  1096. package/dist/layer3/anthropic/utils/index.js +0 -27
  1097. package/dist/layer3/anthropic/utils/index.js.map +0 -1
  1098. package/dist/layer3/anthropic/utils/path-helpers.d.ts +0 -21
  1099. package/dist/layer3/anthropic/utils/path-helpers.d.ts.map +0 -1
  1100. package/dist/layer3/anthropic/utils/path-helpers.js +0 -69
  1101. package/dist/layer3/anthropic/utils/path-helpers.js.map +0 -1
  1102. package/dist/layer3/anthropic/utils/response-parser.d.ts +0 -40
  1103. package/dist/layer3/anthropic/utils/response-parser.d.ts.map +0 -1
  1104. package/dist/layer3/anthropic/utils/response-parser.js +0 -285
  1105. package/dist/layer3/anthropic/utils/response-parser.js.map +0 -1
  1106. package/dist/layer3/anthropic/utils/retry.d.ts +0 -15
  1107. package/dist/layer3/anthropic/utils/retry.d.ts.map +0 -1
  1108. package/dist/layer3/anthropic/utils/retry.js +0 -62
  1109. package/dist/layer3/anthropic/utils/retry.js.map +0 -1
  1110. package/dist/layer3/index.d.ts +0 -27
  1111. package/dist/layer3/index.d.ts.map +0 -1
  1112. package/dist/layer3/index.js +0 -150
  1113. package/dist/layer3/index.js.map +0 -1
  1114. package/dist/layer3/osv-check.d.ts +0 -75
  1115. package/dist/layer3/osv-check.d.ts.map +0 -1
  1116. package/dist/layer3/osv-check.js +0 -308
  1117. package/dist/layer3/osv-check.js.map +0 -1
  1118. package/dist/layer3/package-check.d.ts +0 -63
  1119. package/dist/layer3/package-check.d.ts.map +0 -1
  1120. package/dist/layer3/package-check.js +0 -508
  1121. package/dist/layer3/package-check.js.map +0 -1
  1122. package/dist/model/cross-file-taint.d.ts +0 -40
  1123. package/dist/model/cross-file-taint.d.ts.map +0 -1
  1124. package/dist/model/cross-file-taint.js +0 -290
  1125. package/dist/model/cross-file-taint.js.map +0 -1
  1126. package/dist/model/function-classifier.d.ts +0 -32
  1127. package/dist/model/function-classifier.d.ts.map +0 -1
  1128. package/dist/model/function-classifier.js +0 -143
  1129. package/dist/model/function-classifier.js.map +0 -1
  1130. package/dist/model/sanitiser-detection.d.ts +0 -27
  1131. package/dist/model/sanitiser-detection.d.ts.map +0 -1
  1132. package/dist/model/sanitiser-detection.js +0 -224
  1133. package/dist/model/sanitiser-detection.js.map +0 -1
  1134. package/dist/model/sink-matcher.d.ts +0 -17
  1135. package/dist/model/sink-matcher.d.ts.map +0 -1
  1136. package/dist/model/sink-matcher.js +0 -141
  1137. package/dist/model/sink-matcher.js.map +0 -1
  1138. package/dist/model/sink-patterns.d.ts +0 -19
  1139. package/dist/model/sink-patterns.d.ts.map +0 -1
  1140. package/dist/model/sink-patterns.js +0 -88
  1141. package/dist/model/sink-patterns.js.map +0 -1
  1142. package/dist/model/source-discovery.d.ts +0 -15
  1143. package/dist/model/source-discovery.d.ts.map +0 -1
  1144. package/dist/model/source-discovery.js +0 -170
  1145. package/dist/model/source-discovery.js.map +0 -1
  1146. package/dist/model/taint-tracker.d.ts +0 -21
  1147. package/dist/model/taint-tracker.d.ts.map +0 -1
  1148. package/dist/model/taint-tracker.js +0 -281
  1149. package/dist/model/taint-tracker.js.map +0 -1
  1150. package/dist/modes/incremental.d.ts +0 -66
  1151. package/dist/modes/incremental.d.ts.map +0 -1
  1152. package/dist/modes/incremental.js +0 -200
  1153. package/dist/modes/incremental.js.map +0 -1
  1154. package/dist/rules/framework-fixes.d.ts +0 -48
  1155. package/dist/rules/framework-fixes.d.ts.map +0 -1
  1156. package/dist/rules/framework-fixes.js +0 -439
  1157. package/dist/rules/framework-fixes.js.map +0 -1
  1158. package/dist/rules/index.d.ts +0 -8
  1159. package/dist/rules/index.d.ts.map +0 -1
  1160. package/dist/rules/index.js +0 -18
  1161. package/dist/rules/index.js.map +0 -1
  1162. package/dist/rules/metadata.d.ts +0 -43
  1163. package/dist/rules/metadata.d.ts.map +0 -1
  1164. package/dist/rules/metadata.js +0 -800
  1165. package/dist/rules/metadata.js.map +0 -1
  1166. package/dist/score/auto-dismiss.d.ts +0 -28
  1167. package/dist/score/auto-dismiss.d.ts.map +0 -1
  1168. package/dist/score/auto-dismiss.js +0 -200
  1169. package/dist/score/auto-dismiss.js.map +0 -1
  1170. package/dist/suppression/config-loader.d.ts +0 -74
  1171. package/dist/suppression/config-loader.d.ts.map +0 -1
  1172. package/dist/suppression/config-loader.js +0 -424
  1173. package/dist/suppression/config-loader.js.map +0 -1
  1174. package/dist/suppression/hash.d.ts +0 -48
  1175. package/dist/suppression/hash.d.ts.map +0 -1
  1176. package/dist/suppression/hash.js +0 -88
  1177. package/dist/suppression/hash.js.map +0 -1
  1178. package/dist/suppression/index.d.ts +0 -11
  1179. package/dist/suppression/index.d.ts.map +0 -1
  1180. package/dist/suppression/index.js +0 -39
  1181. package/dist/suppression/index.js.map +0 -1
  1182. package/dist/suppression/inline-parser.d.ts +0 -39
  1183. package/dist/suppression/inline-parser.d.ts.map +0 -1
  1184. package/dist/suppression/inline-parser.js +0 -218
  1185. package/dist/suppression/inline-parser.js.map +0 -1
  1186. package/dist/suppression/manager.d.ts +0 -94
  1187. package/dist/suppression/manager.d.ts.map +0 -1
  1188. package/dist/suppression/manager.js +0 -292
  1189. package/dist/suppression/manager.js.map +0 -1
  1190. package/dist/suppression/types.d.ts +0 -151
  1191. package/dist/suppression/types.d.ts.map +0 -1
  1192. package/dist/suppression/types.js +0 -28
  1193. package/dist/suppression/types.js.map +0 -1
  1194. package/dist/types.d.ts +0 -331
  1195. package/dist/types.d.ts.map +0 -1
  1196. package/dist/types.js +0 -124
  1197. package/dist/types.js.map +0 -1
  1198. package/dist/utils/auth-helper-detector.d.ts +0 -56
  1199. package/dist/utils/auth-helper-detector.d.ts.map +0 -1
  1200. package/dist/utils/auth-helper-detector.js +0 -360
  1201. package/dist/utils/auth-helper-detector.js.map +0 -1
  1202. package/dist/utils/code-analysis.d.ts +0 -39
  1203. package/dist/utils/code-analysis.d.ts.map +0 -1
  1204. package/dist/utils/code-analysis.js +0 -159
  1205. package/dist/utils/code-analysis.js.map +0 -1
  1206. package/dist/utils/comment-analyzer.d.ts +0 -38
  1207. package/dist/utils/comment-analyzer.d.ts.map +0 -1
  1208. package/dist/utils/comment-analyzer.js +0 -218
  1209. package/dist/utils/comment-analyzer.js.map +0 -1
  1210. package/dist/utils/context-helpers.d.ts +0 -219
  1211. package/dist/utils/context-helpers.d.ts.map +0 -1
  1212. package/dist/utils/context-helpers.js +0 -886
  1213. package/dist/utils/context-helpers.js.map +0 -1
  1214. package/dist/utils/diff-detector.d.ts +0 -53
  1215. package/dist/utils/diff-detector.d.ts.map +0 -1
  1216. package/dist/utils/diff-detector.js +0 -104
  1217. package/dist/utils/diff-detector.js.map +0 -1
  1218. package/dist/utils/diff-parser.d.ts +0 -80
  1219. package/dist/utils/diff-parser.d.ts.map +0 -1
  1220. package/dist/utils/diff-parser.js +0 -202
  1221. package/dist/utils/diff-parser.js.map +0 -1
  1222. package/dist/utils/environment-context.d.ts +0 -76
  1223. package/dist/utils/environment-context.d.ts.map +0 -1
  1224. package/dist/utils/environment-context.js +0 -271
  1225. package/dist/utils/environment-context.js.map +0 -1
  1226. package/dist/utils/imported-auth-detector.d.ts +0 -37
  1227. package/dist/utils/imported-auth-detector.d.ts.map +0 -1
  1228. package/dist/utils/imported-auth-detector.js +0 -251
  1229. package/dist/utils/imported-auth-detector.js.map +0 -1
  1230. package/dist/utils/intent-detector.d.ts +0 -66
  1231. package/dist/utils/intent-detector.d.ts.map +0 -1
  1232. package/dist/utils/intent-detector.js +0 -282
  1233. package/dist/utils/intent-detector.js.map +0 -1
  1234. package/dist/utils/middleware-detector.d.ts +0 -55
  1235. package/dist/utils/middleware-detector.d.ts.map +0 -1
  1236. package/dist/utils/middleware-detector.js +0 -260
  1237. package/dist/utils/middleware-detector.js.map +0 -1
  1238. package/dist/utils/oauth-flow-detector.d.ts +0 -41
  1239. package/dist/utils/oauth-flow-detector.d.ts.map +0 -1
  1240. package/dist/utils/oauth-flow-detector.js +0 -202
  1241. package/dist/utils/oauth-flow-detector.js.map +0 -1
  1242. package/dist/utils/parsed-file.d.ts +0 -51
  1243. package/dist/utils/parsed-file.d.ts.map +0 -1
  1244. package/dist/utils/parsed-file.js +0 -95
  1245. package/dist/utils/parsed-file.js.map +0 -1
  1246. package/dist/utils/path-exclusions.d.ts +0 -55
  1247. package/dist/utils/path-exclusions.d.ts.map +0 -1
  1248. package/dist/utils/path-exclusions.js +0 -224
  1249. package/dist/utils/path-exclusions.js.map +0 -1
  1250. package/dist/utils/project-context-builder.d.ts +0 -119
  1251. package/dist/utils/project-context-builder.d.ts.map +0 -1
  1252. package/dist/utils/project-context-builder.js +0 -534
  1253. package/dist/utils/project-context-builder.js.map +0 -1
  1254. package/dist/utils/registry-clients.d.ts +0 -93
  1255. package/dist/utils/registry-clients.d.ts.map +0 -1
  1256. package/dist/utils/registry-clients.js +0 -273
  1257. package/dist/utils/registry-clients.js.map +0 -1
  1258. package/dist/utils/route-hierarchy.d.ts +0 -50
  1259. package/dist/utils/route-hierarchy.d.ts.map +0 -1
  1260. package/dist/utils/route-hierarchy.js +0 -226
  1261. package/dist/utils/route-hierarchy.js.map +0 -1
  1262. package/dist/utils/schema-semantics.d.ts +0 -45
  1263. package/dist/utils/schema-semantics.d.ts.map +0 -1
  1264. package/dist/utils/schema-semantics.js +0 -193
  1265. package/dist/utils/schema-semantics.js.map +0 -1
  1266. package/dist/utils/trpc-analyzer.d.ts +0 -78
  1267. package/dist/utils/trpc-analyzer.d.ts.map +0 -1
  1268. package/dist/utils/trpc-analyzer.js +0 -297
  1269. package/dist/utils/trpc-analyzer.js.map +0 -1
  1270. package/src/__tests__/context-engine/cross-file-taint.test.ts +0 -284
  1271. package/src/__tests__/context-engine/function-classifier.test.ts +0 -146
  1272. package/src/__tests__/context-engine/integration.test.ts +0 -320
  1273. package/src/__tests__/context-engine/sanitiser-detection.test.ts +0 -187
  1274. package/src/__tests__/context-engine/sink-matcher.test.ts +0 -251
  1275. package/src/__tests__/context-engine/source-discovery.test.ts +0 -186
  1276. package/src/__tests__/context-engine/taint-tracker.test.ts +0 -182
  1277. package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +0 -750
  1278. package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +0 -555
  1279. package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +0 -321
  1280. package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +0 -439
  1281. package/src/detect/ai-code/agent-tools.ts +0 -1662
  1282. package/src/detect/ai-code/byok-patterns.ts +0 -354
  1283. package/src/detect/ai-code/endpoint-protection.ts +0 -406
  1284. package/src/detect/ai-code/execution-sinks.ts +0 -1310
  1285. package/src/detect/ai-code/fingerprinting.ts +0 -774
  1286. package/src/detect/ai-code/mcp-security.ts +0 -937
  1287. package/src/detect/ai-code/model-supply-chain.ts +0 -535
  1288. package/src/detect/ai-code/package-hallucination.ts +0 -955
  1289. package/src/detect/ai-code/prompt-hygiene.ts +0 -1314
  1290. package/src/detect/ai-code/rag-safety.ts +0 -977
  1291. package/src/detect/ai-code/schema-validation.ts +0 -427
  1292. package/src/detect/secrets/patterns.ts +0 -561
  1293. package/src/detect/secrets/weak-crypto.ts +0 -485
  1294. package/src/detect/structural/__tests__/math-random-enhanced.test.ts +0 -405
  1295. package/src/detect/structural/auth-patterns.ts +0 -621
  1296. package/src/detect/structural/dangerous-functions/child-process.ts +0 -98
  1297. package/src/detect/structural/dangerous-functions/dom-xss.ts +0 -292
  1298. package/src/detect/structural/dangerous-functions/index.ts +0 -1556
  1299. package/src/detect/structural/dangerous-functions/json-parse.ts +0 -393
  1300. package/src/detect/structural/dangerous-functions/math-random.ts +0 -789
  1301. package/src/detect/structural/dangerous-functions/patterns.ts +0 -176
  1302. package/src/detect/structural/dangerous-functions/request-validation.ts +0 -153
  1303. package/src/detect/structural/dangerous-functions/utils/control-flow.ts +0 -35
  1304. package/src/detect/structural/dangerous-functions/utils/helpers.ts +0 -170
  1305. package/src/detect/structural/dangerous-functions/utils/index.ts +0 -25
  1306. package/src/detect/structural/dangerous-functions/utils/schema-validation.ts +0 -106
  1307. package/src/detect/structural/data-exposure.ts +0 -302
  1308. package/src/detect/structural/framework-checks.ts +0 -439
  1309. package/src/detect/structural/log-injection.ts +0 -254
  1310. package/src/detect/structural/logic-gates.ts +0 -256
  1311. package/src/detect/structural/risky-imports.ts +0 -197
  1312. package/src/detect/structural/security-headers.ts +0 -231
  1313. package/src/detect/structural/ssrf-detection.ts +0 -300
  1314. package/src/detect/structural/variables.ts +0 -177
  1315. package/src/detect/structural/xxe-detection.ts +0 -295
  1316. package/src/model/cross-file-taint.ts +0 -374
  1317. package/src/model/function-classifier.ts +0 -184
  1318. package/src/model/sanitiser-detection.ts +0 -268
  1319. package/src/model/sink-matcher.ts +0 -178
  1320. package/src/model/sink-patterns.ts +0 -109
  1321. package/src/model/source-discovery.ts +0 -209
  1322. package/src/model/taint-tracker.ts +0 -333
  1323. package/src/score/auto-dismiss.ts +0 -224
@@ -0,0 +1,1750 @@
1
+ /**
2
+ * Taint Propagation Engine
3
+ *
4
+ * Forward worklist dataflow analysis. Seeds taint at sources, propagates
5
+ * through CFG to fixpoint, then checks if tainted data reaches sinks
6
+ * without proper sanitization.
7
+ *
8
+ * Algorithm:
9
+ * 1. Map sources/sinks/sanitizers to CFG nodes via AST containment
10
+ * 2. Seed source nodes with taint state
11
+ * 3. Worklist: propagate taint forward through CFG, applying transfer
12
+ * functions at each node (assignment, call, destructuring, etc.)
13
+ * 4. At fixpoint: check each sink node for matching taint kinds
14
+ * 5. Reconstruct source-to-sink paths via backward slice
15
+ */
16
+
17
+ import type Parser from 'tree-sitter'
18
+ import type { CFG, CFGNode } from './cfg-types'
19
+ import { getSuccessors, getPredecessors, buildFileCFGs } from './cfg-builder'
20
+ import type { TaintKind, TaintSource, TaintSink, TaintSanitizer } from './types'
21
+ import type { TaintState, TaintStep, TaintFinding, CallResolutionContext, CalleeResult } from './propagation-types'
22
+ import { getTaint, setTaint, deleteTaint, getFieldTaint, setFieldTaint, WHOLE_OBJECT } from './propagation-types'
23
+ import type { FieldKey } from './propagation-types'
24
+ import { walkAST } from '../parse/ast'
25
+ import { classifySources } from './source-classifier'
26
+ import { classifySinks } from './sink-classifier'
27
+ import { buildSanitizerRegistry } from './sanitizer-registry'
28
+ import { resolveCallTarget } from '../detect/ast-rules/helpers/call-analysis'
29
+ import { resolvePythonCallTarget } from '../detect/ast-rules/helpers/python-helpers'
30
+ import { collectImportBindings } from './helpers'
31
+ import { injectPromiseChainTaint, injectCallbackTaint } from './async-flow'
32
+ import { collectConstants, isConstantExpression } from './constant-propagation'
33
+ import type { ConstantMap } from './constant-propagation'
34
+ import { getOrBuildSummary, composeSummary } from './taint-summary'
35
+
36
+ // ============================================================================
37
+ // AST → CFGNode Matching
38
+ // ============================================================================
39
+
40
+ /**
41
+ * Check if `child` is a descendant of (or equal to) `ancestor` in the AST.
42
+ */
43
+ export function isDescendantOf(
44
+ child: Parser.SyntaxNode,
45
+ ancestor: Parser.SyntaxNode,
46
+ ): boolean {
47
+ let current: Parser.SyntaxNode | null = child
48
+ while (current) {
49
+ if (current.id === ancestor.id) return true
50
+ current = current.parent
51
+ }
52
+ return false
53
+ }
54
+
55
+ /**
56
+ * Find the CFG node that contains a given AST node.
57
+ * Returns the first CFG node whose astNode is an ancestor of (or equal to) the target.
58
+ */
59
+ export function findContainingCFGNode(
60
+ cfg: CFG,
61
+ astNode: Parser.SyntaxNode,
62
+ ): CFGNode | null {
63
+ for (const [, cfgNode] of cfg.nodes) {
64
+ if (!cfgNode.astNode) continue
65
+ if (isDescendantOf(astNode, cfgNode.astNode)) return cfgNode
66
+ }
67
+ return null
68
+ }
69
+
70
+ /**
71
+ * Phase 8 (Part 4): O(D) parent-chain walk using pre-built CFG node map.
72
+ * Walks up the AST parent chain until a mapped node is found.
73
+ * D = AST depth (5-15), vs O(N) where N = CFG nodes (50-200).
74
+ */
75
+ export function findContainingCFGNodeFast(
76
+ cfgNodeMap: Map<number, CFGNode>,
77
+ astNode: Parser.SyntaxNode,
78
+ ): CFGNode | null {
79
+ let current: Parser.SyntaxNode | null = astNode
80
+ while (current) {
81
+ const cfgNode = cfgNodeMap.get(current.id)
82
+ if (cfgNode) return cfgNode
83
+ current = current.parent
84
+ }
85
+ return null
86
+ }
87
+
88
+ /**
89
+ * Build a map from AST node ID → CFGNode for all nodes in a CFG.
90
+ */
91
+ export function buildCFGNodeMap(cfg: CFG): Map<number, CFGNode> {
92
+ const map = new Map<number, CFGNode>()
93
+ for (const [, cfgNode] of cfg.nodes) {
94
+ if (cfgNode.astNode) {
95
+ map.set(cfgNode.astNode.id, cfgNode)
96
+ }
97
+ }
98
+ return map
99
+ }
100
+
101
+ // ============================================================================
102
+ // Taint State Helpers
103
+ // ============================================================================
104
+
105
+ /** Clone a TaintState (deep copy of field maps and sets). */
106
+ function cloneState(state: TaintState): TaintState {
107
+ const clone: TaintState = new Map()
108
+ for (const [varName, fieldMap] of state) {
109
+ const clonedFieldMap = new Map<FieldKey, Set<TaintKind>>()
110
+ for (const [field, kinds] of fieldMap) {
111
+ clonedFieldMap.set(field, new Set(kinds))
112
+ }
113
+ clone.set(varName, clonedFieldMap)
114
+ }
115
+ return clone
116
+ }
117
+
118
+ /** Merge multiple predecessor out-states into one in-state (union). */
119
+ export function mergeStates(states: TaintState[]): TaintState {
120
+ const merged: TaintState = new Map()
121
+ for (const state of states) {
122
+ for (const [varName, fieldMap] of state) {
123
+ let existingFieldMap = merged.get(varName)
124
+ if (!existingFieldMap) {
125
+ existingFieldMap = new Map<FieldKey, Set<TaintKind>>()
126
+ merged.set(varName, existingFieldMap)
127
+ }
128
+ for (const [field, kinds] of fieldMap) {
129
+ const existingKinds = existingFieldMap.get(field)
130
+ if (existingKinds) {
131
+ for (const k of kinds) existingKinds.add(k)
132
+ } else {
133
+ existingFieldMap.set(field, new Set(kinds))
134
+ }
135
+ }
136
+ }
137
+ }
138
+ return merged
139
+ }
140
+
141
+ /** Check if two TaintStates are equal. */
142
+ function statesEqual(a: TaintState, b: TaintState): boolean {
143
+ // Phase 8 (3c): identity fast path — catches cases where applyTransfer returned same ref
144
+ if (a === b) return true
145
+ if (a.size !== b.size) return false
146
+ for (const [varName, aFieldMap] of a) {
147
+ const bFieldMap = b.get(varName)
148
+ if (!bFieldMap) return false
149
+ if (aFieldMap.size !== bFieldMap.size) return false
150
+ for (const [field, aKinds] of aFieldMap) {
151
+ const bKinds = bFieldMap.get(field)
152
+ if (!bKinds) return false
153
+ if (aKinds.size !== bKinds.size) return false
154
+ for (const k of aKinds) {
155
+ if (!bKinds.has(k)) return false
156
+ }
157
+ }
158
+ }
159
+ return true
160
+ }
161
+
162
+ /** Collect taint from a set of used variables in the current state. */
163
+ function collectUseTaint(
164
+ uses: Iterable<string>,
165
+ state: TaintState,
166
+ ): Set<TaintKind> {
167
+ const result = new Set<TaintKind>()
168
+ for (const u of uses) {
169
+ const kinds = getTaint(state, u)
170
+ if (kinds) {
171
+ for (const k of kinds) result.add(k)
172
+ }
173
+ }
174
+ return result
175
+ }
176
+
177
+ // ============================================================================
178
+ // Sanitizer Map
179
+ // ============================================================================
180
+
181
+ export interface SanitizerMapping {
182
+ cfgNodeId: number
183
+ sanitizer: TaintSanitizer
184
+ }
185
+
186
+ /**
187
+ * Build a map from CFG node ID → sanitizers active in that node.
188
+ * Phase 8: accepts optional cfgNodeMap for O(D) lookup instead of O(N).
189
+ */
190
+ export function buildSanitizerMap(
191
+ cfg: CFG,
192
+ sanitizers: TaintSanitizer[],
193
+ cfgNodeMap?: Map<number, CFGNode>,
194
+ ): Map<number, TaintSanitizer[]> {
195
+ const map = new Map<number, TaintSanitizer[]>()
196
+ for (const s of sanitizers) {
197
+ const cfgNode = cfgNodeMap
198
+ ? findContainingCFGNodeFast(cfgNodeMap, s.node)
199
+ : findContainingCFGNode(cfg, s.node)
200
+ if (!cfgNode) continue
201
+ const existing = map.get(cfgNode.id)
202
+ if (existing) {
203
+ existing.push(s)
204
+ } else {
205
+ map.set(cfgNode.id, [s])
206
+ }
207
+ }
208
+ return map
209
+ }
210
+
211
+ // ============================================================================
212
+ // Expression-level Transfer Function
213
+ // ============================================================================
214
+
215
+ /**
216
+ * Resolve taint for the RHS of an assignment by walking its AST.
217
+ *
218
+ * Returns the set of taint kinds that the RHS expression carries,
219
+ * considering sanitizer calls and optionally cross-file call resolution.
220
+ */
221
+ function resolveExpressionTaint(
222
+ exprNode: Parser.SyntaxNode,
223
+ inState: TaintState,
224
+ sanitizers: TaintSanitizer[],
225
+ callContext?: CallResolutionContext,
226
+ callerFile?: string,
227
+ ): Set<TaintKind> {
228
+ const result = new Set<TaintKind>()
229
+
230
+ // Literal values: no taint
231
+ if (isLiteral(exprNode)) return result
232
+
233
+ // Identifier: look up in state
234
+ if (exprNode.type === 'identifier') {
235
+ const kinds = getTaint(inState, exprNode.text)
236
+ if (kinds) for (const k of kinds) result.add(k)
237
+ return result
238
+ }
239
+
240
+ // Helper: shorthand for recursive calls with context forwarding
241
+ const resolve = (node: Parser.SyntaxNode) =>
242
+ resolveExpressionTaint(node, inState, sanitizers, callContext, callerFile)
243
+
244
+ // Await: taint propagates through (Python: 'await', JS: 'await_expression')
245
+ if (exprNode.type === 'await_expression' || exprNode.type === 'await') {
246
+ const inner = exprNode.namedChildren[0]
247
+ if (inner) {
248
+ const innerTaint = resolve(inner)
249
+ for (const k of innerTaint) result.add(k)
250
+ }
251
+ return result
252
+ }
253
+
254
+ // Parenthesized: pass through
255
+ if (exprNode.type === 'parenthesized_expression') {
256
+ const inner = exprNode.namedChildren[0]
257
+ if (inner) {
258
+ const innerTaint = resolve(inner)
259
+ for (const k of innerTaint) result.add(k)
260
+ }
261
+ return result
262
+ }
263
+
264
+ // Call expression: check sanitizers, then cross-file resolution, then conservative
265
+ // Python: 'call', JS: 'call_expression'
266
+ if (exprNode.type === 'call_expression' || exprNode.type === 'call') {
267
+ return resolveCallTaint(exprNode, inState, sanitizers, callContext, callerFile)
268
+ }
269
+
270
+ // New expression: union of arg taint
271
+ if (exprNode.type === 'new_expression') {
272
+ const args = exprNode.childForFieldName('arguments')
273
+ if (args) {
274
+ for (const arg of args.namedChildren) {
275
+ const argTaint = resolve(arg)
276
+ for (const k of argTaint) result.add(k)
277
+ }
278
+ }
279
+ return result
280
+ }
281
+
282
+ // Binary expression / template literal: union of both sides
283
+ // Python: 'binary_operator', JS: 'binary_expression'
284
+ if (exprNode.type === 'binary_expression' || exprNode.type === 'binary_operator') {
285
+ const left = exprNode.childForFieldName('left')
286
+ const right = exprNode.childForFieldName('right')
287
+ if (left) {
288
+ const lt = resolve(left)
289
+ for (const k of lt) result.add(k)
290
+ }
291
+ if (right) {
292
+ const rt = resolve(right)
293
+ for (const k of rt) result.add(k)
294
+ }
295
+ return result
296
+ }
297
+
298
+ // Python boolean/comparison operators: walk children for taint
299
+ if (exprNode.type === 'boolean_operator' || exprNode.type === 'comparison_operator' ||
300
+ exprNode.type === 'not_operator') {
301
+ for (const child of exprNode.namedChildren) {
302
+ const ct = resolve(child)
303
+ for (const k of ct) result.add(k)
304
+ }
305
+ return result
306
+ }
307
+
308
+ // Python f-string: string node with interpolation children
309
+ // Must check BEFORE the fallback walkAST to properly handle f-string taint
310
+ if (exprNode.type === 'string' && exprNode.descendantsOfType('interpolation').length > 0) {
311
+ for (const interp of exprNode.descendantsOfType('interpolation')) {
312
+ for (const child of interp.namedChildren) {
313
+ const childTaint = resolve(child)
314
+ for (const k of childTaint) result.add(k)
315
+ }
316
+ }
317
+ return result
318
+ }
319
+
320
+ // Python concatenated_string: "foo" + "bar" or "a" "b" style
321
+ if (exprNode.type === 'concatenated_string') {
322
+ for (const child of exprNode.namedChildren) {
323
+ const ct = resolve(child)
324
+ for (const k of ct) result.add(k)
325
+ }
326
+ return result
327
+ }
328
+
329
+ // Template string: union of all substitutions
330
+ if (exprNode.type === 'template_string') {
331
+ const subs = exprNode.descendantsOfType('template_substitution')
332
+ for (const sub of subs) {
333
+ for (const child of sub.namedChildren) {
334
+ const childTaint = resolve(child)
335
+ for (const k of childTaint) result.add(k)
336
+ }
337
+ }
338
+ return result
339
+ }
340
+
341
+ // Member expression: field-aware taint resolution
342
+ // Python: 'attribute' + 'subscript', JS: 'member_expression' + 'subscript_expression'
343
+ if (exprNode.type === 'member_expression' || exprNode.type === 'subscript_expression' ||
344
+ exprNode.type === 'attribute' || exprNode.type === 'subscript') {
345
+ const obj = exprNode.childForFieldName('object')
346
+ ?? exprNode.childForFieldName('value') // Python subscript uses 'value' field
347
+ if (obj) {
348
+ // Try field-specific taint: obj.field → getFieldTaint(state, "obj", "field")
349
+ if ((exprNode.type === 'member_expression' || exprNode.type === 'attribute') && obj.type === 'identifier') {
350
+ const prop = exprNode.type === 'attribute'
351
+ ? exprNode.childForFieldName('attribute')
352
+ : exprNode.childForFieldName('property')
353
+ if (prop && (prop.type === 'property_identifier' || prop.type === 'identifier')) {
354
+ const fieldTaint = getFieldTaint(inState, obj.text, prop.text)
355
+ if (fieldTaint) for (const k of fieldTaint) result.add(k)
356
+ return result
357
+ }
358
+ }
359
+ // Computed/dynamic access or deep chain: fall back to whole-object taint
360
+ const objTaint = resolve(obj)
361
+ for (const k of objTaint) result.add(k)
362
+ }
363
+ // Computed index may also carry taint
364
+ if (exprNode.type === 'subscript_expression' || exprNode.type === 'subscript') {
365
+ const index = exprNode.childForFieldName('index')
366
+ ?? exprNode.childForFieldName('subscript') // Python subscript field name
367
+ if (index) {
368
+ const idxTaint = resolve(index)
369
+ for (const k of idxTaint) result.add(k)
370
+ }
371
+ }
372
+ return result
373
+ }
374
+
375
+ // Spread element
376
+ if (exprNode.type === 'spread_element') {
377
+ const inner = exprNode.namedChildren[0]
378
+ if (inner) {
379
+ const innerTaint = resolve(inner)
380
+ for (const k of innerTaint) result.add(k)
381
+ }
382
+ return result
383
+ }
384
+
385
+ // Object literal: union of all value taint (whole-object view for backward compat)
386
+ if (exprNode.type === 'object') {
387
+ for (const child of exprNode.namedChildren) {
388
+ if (child.type === 'pair') {
389
+ const value = child.childForFieldName('value')
390
+ if (value) {
391
+ const vt = resolve(value)
392
+ for (const k of vt) result.add(k)
393
+ }
394
+ } else if (child.type === 'shorthand_property_identifier') {
395
+ const kinds = getTaint(inState, child.text)
396
+ if (kinds) for (const k of kinds) result.add(k)
397
+ } else if (child.type === 'spread_element') {
398
+ const inner = child.namedChildren[0]
399
+ if (inner) {
400
+ const innerTaint = resolve(inner)
401
+ for (const k of innerTaint) result.add(k)
402
+ }
403
+ }
404
+ }
405
+ return result
406
+ }
407
+
408
+ // Array literal / Python list/tuple: union of all element taint
409
+ if (exprNode.type === 'array' || exprNode.type === 'list' || exprNode.type === 'tuple') {
410
+ for (const child of exprNode.namedChildren) {
411
+ const ct = resolve(child)
412
+ for (const k of ct) result.add(k)
413
+ }
414
+ return result
415
+ }
416
+
417
+ // Python dictionary: union of all value taint
418
+ if (exprNode.type === 'dictionary') {
419
+ for (const child of exprNode.namedChildren) {
420
+ if (child.type === 'pair') {
421
+ const value = child.childForFieldName('value')
422
+ if (value) {
423
+ const vt = resolve(value)
424
+ for (const k of vt) result.add(k)
425
+ }
426
+ } else {
427
+ const ct = resolve(child)
428
+ for (const k of ct) result.add(k)
429
+ }
430
+ }
431
+ return result
432
+ }
433
+
434
+ // Ternary: union of both branches (conservative)
435
+ // Python: 'conditional_expression', JS: 'ternary_expression'
436
+ if (exprNode.type === 'ternary_expression' || exprNode.type === 'conditional_expression') {
437
+ for (const child of exprNode.namedChildren) {
438
+ const ct = resolve(child)
439
+ for (const k of ct) result.add(k)
440
+ }
441
+ return result
442
+ }
443
+
444
+ // Unary expression: propagate from operand
445
+ // Python: 'unary_operator', JS: 'unary_expression'
446
+ if (exprNode.type === 'unary_expression' || exprNode.type === 'unary_operator') {
447
+ const arg = exprNode.namedChildren[0]
448
+ if (arg) {
449
+ const at = resolve(arg)
450
+ for (const k of at) result.add(k)
451
+ }
452
+ return result
453
+ }
454
+
455
+ // Python keyword_argument: extract taint from value
456
+ if (exprNode.type === 'keyword_argument') {
457
+ const value = exprNode.childForFieldName('value')
458
+ if (value) {
459
+ const vt = resolve(value)
460
+ for (const k of vt) result.add(k)
461
+ }
462
+ return result
463
+ }
464
+
465
+ // Type assertion / as expression: propagate through
466
+ if (exprNode.type === 'as_expression' || exprNode.type === 'type_assertion' ||
467
+ exprNode.type === 'non_null_expression' || exprNode.type === 'satisfies_expression') {
468
+ const inner = exprNode.namedChildren[0]
469
+ if (inner) {
470
+ const innerTaint = resolve(inner)
471
+ for (const k of innerTaint) result.add(k)
472
+ }
473
+ return result
474
+ }
475
+
476
+ // Fallback: conservatively collect taint from all identifiers in the expression
477
+ walkAST(exprNode, (node) => {
478
+ if (node.type === 'identifier') {
479
+ const kinds = getTaint(inState, node.text)
480
+ if (kinds) for (const k of kinds) result.add(k)
481
+ }
482
+ // Don't descend into nested function expressions
483
+ if (node.type === 'arrow_function' || node.type === 'function_expression' ||
484
+ node.type === 'function_definition' || node.type === 'lambda') return true
485
+ return false
486
+ })
487
+
488
+ return result
489
+ }
490
+
491
+ /**
492
+ * Resolve taint for a call expression, checking sanitizers and cross-file callees.
493
+ */
494
+ function resolveCallTaint(
495
+ callNode: Parser.SyntaxNode,
496
+ inState: TaintState,
497
+ sanitizers: TaintSanitizer[],
498
+ callContext?: CallResolutionContext,
499
+ callerFile?: string,
500
+ ): Set<TaintKind> {
501
+ const result = new Set<TaintKind>()
502
+ const resolve = (node: Parser.SyntaxNode) =>
503
+ resolveExpressionTaint(node, inState, sanitizers, callContext, callerFile)
504
+
505
+ // First, collect taint from all arguments (and per-argument taint for cross-file)
506
+ const argsNode = callNode.childForFieldName('arguments')
507
+ const argTaint = new Set<TaintKind>()
508
+ const perArgTaint: Set<TaintKind>[] = []
509
+ if (argsNode) {
510
+ for (const arg of argsNode.namedChildren) {
511
+ const at = resolve(arg)
512
+ perArgTaint.push(at)
513
+ for (const k of at) argTaint.add(k)
514
+ }
515
+ }
516
+
517
+ // Also get taint from the callee (for method chains: tainted.toString())
518
+ // Python: 'attribute', JS: 'member_expression'
519
+ const fnNode = callNode.childForFieldName('function')
520
+ if (fnNode?.type === 'member_expression' || fnNode?.type === 'attribute') {
521
+ const obj = fnNode.childForFieldName('object')
522
+ if (obj) {
523
+ const objTaint = resolve(obj)
524
+ for (const k of objTaint) argTaint.add(k)
525
+ }
526
+ }
527
+
528
+ // Check if this call is a sanitizer
529
+ const matchingSanitizer = findMatchingSanitizer(callNode, sanitizers)
530
+ if (matchingSanitizer) {
531
+ // Sanitizer: result = argTaint minus clearsKinds
532
+ for (const k of argTaint) {
533
+ if (!matchingSanitizer.clearsKinds.has(k)) {
534
+ result.add(k)
535
+ }
536
+ }
537
+ return result
538
+ }
539
+
540
+ // Cross-file / inter-procedural resolution
541
+ if (callContext && callerFile && argTaint.size > 0) {
542
+ const calleeResult = resolveCalleeReturnTaint(
543
+ callNode, perArgTaint, callContext, callerFile,
544
+ )
545
+ if (calleeResult !== null) {
546
+ return calleeResult.returnTaint
547
+ }
548
+ }
549
+
550
+ // Not a sanitizer, not cross-file resolved: conservatively propagate all arg taint
551
+ return argTaint
552
+ }
553
+
554
+ // ============================================================================
555
+ // Cross-File Call Resolution
556
+ // ============================================================================
557
+
558
+ /**
559
+ * Resolve the return taint from following a call into another function/file.
560
+ *
561
+ * 1. Resolve call target name
562
+ * 2. Look up in CrossFileIndex (resolveImport or functionDefs)
563
+ * 3. If found and not in callStack (recursion guard):
564
+ * a. Parse callee file's AST, build CFG
565
+ * b. Create synthetic sources from argument taint → parameter names
566
+ * c. Run propagation on callee's CFG
567
+ * d. Collect return-value taint from return statements
568
+ * 4. Return the taint kinds flowing out + path steps
569
+ */
570
+ export function resolveCalleeReturnTaint(
571
+ callNode: Parser.SyntaxNode,
572
+ perArgTaint: Set<TaintKind>[],
573
+ ctx: CallResolutionContext,
574
+ callerFile: string,
575
+ ): CalleeResult | null {
576
+ // Resolve the call target (try JS first, then Python fallback)
577
+ let resolved: { type: string; name: string; object?: string } | null = resolveCallTarget(callNode)
578
+ if (!resolved) {
579
+ const pyResolved = resolvePythonCallTarget(callNode)
580
+ if (pyResolved) {
581
+ resolved = { type: pyResolved.type, name: pyResolved.name, object: pyResolved.object }
582
+ }
583
+ }
584
+ if (!resolved) return null
585
+
586
+ const { crossFileIndex, allASTs, callStack, maxDepth } = ctx
587
+
588
+ // Try to find the function definition
589
+ let funcDef = crossFileIndex.resolveImport.get(`${callerFile}:${resolved.name}`)
590
+
591
+ // Also try namespace.method pattern
592
+ if (!funcDef && resolved.type === 'member' && resolved.object) {
593
+ funcDef = crossFileIndex.resolveImport.get(
594
+ `${callerFile}:${resolved.object}.${resolved.name}`,
595
+ )
596
+ }
597
+
598
+ // Also try local function
599
+ if (!funcDef) {
600
+ funcDef = crossFileIndex.functionDefs.get(`${callerFile}:${resolved.name}`)
601
+ }
602
+
603
+ if (!funcDef) return null
604
+
605
+ // Recursion guard
606
+ const callKey = `${funcDef.filePath}:${funcDef.functionName}`
607
+ if (callStack.has(callKey)) return null
608
+ if (callStack.size >= maxDepth) return null
609
+
610
+ // Check cache
611
+ const taintSig = perArgTaint.map(s => [...s].sort().join(',')).join('|')
612
+ const cacheKey = `${callKey}:${taintSig}`
613
+ if (ctx.resultCache.has(cacheKey)) {
614
+ return ctx.resultCache.get(cacheKey)!
615
+ }
616
+
617
+ // Phase 8: Try summary-based composition first (much faster than full analysis)
618
+ if (ctx.summaryCache && ctx.analysisCache) {
619
+ const summary = getOrBuildSummary(funcDef, ctx)
620
+ if (summary) {
621
+ const result = composeSummary(summary, perArgTaint, callerFile, callNode)
622
+ ctx.resultCache.set(cacheKey, result.returnTaint.size > 0 || result.calleeFindings.length > 0 ? result : null)
623
+ return result.returnTaint.size > 0 || result.calleeFindings.length > 0 ? result : null
624
+ }
625
+ }
626
+
627
+ // Get callee AST
628
+ const calleeAST = allASTs.get(funcDef.filePath)
629
+ if (!calleeAST) {
630
+ ctx.resultCache.set(cacheKey, null)
631
+ return null
632
+ }
633
+
634
+ // Phase 8: Use analysis cache if available, otherwise recompute
635
+ const fileCache = ctx.analysisCache?.get(funcDef.filePath)
636
+ const calleeCFGs = fileCache?.cfgs ?? buildFileCFGs(calleeAST)
637
+
638
+ // Find the specific function's CFG
639
+ let calleeCFG: CFG | undefined
640
+ for (const [name, cfg] of calleeCFGs) {
641
+ if (name === funcDef.functionName) {
642
+ calleeCFG = cfg
643
+ break
644
+ }
645
+ }
646
+
647
+ if (!calleeCFG) {
648
+ ctx.resultCache.set(cacheKey, null)
649
+ return null
650
+ }
651
+
652
+ // Create synthetic sources from argument taint → parameter names
653
+ const syntheticSources: TaintSource[] = []
654
+ for (let i = 0; i < funcDef.params.length && i < perArgTaint.length; i++) {
655
+ const paramTaint = perArgTaint[i]
656
+ if (paramTaint.size === 0) continue
657
+
658
+ // Find the parameter's AST node in the callee function
659
+ const paramNode = findParamNode(funcDef.astNode, funcDef.params[i])
660
+ if (!paramNode) continue
661
+
662
+ syntheticSources.push({
663
+ node: paramNode,
664
+ line: paramNode.startPosition.row + 1,
665
+ variable: funcDef.params[i],
666
+ expression: `${funcDef.functionName}(${funcDef.params[i]})`,
667
+ sourceType: 'external_api', // synthetic source
668
+ taintKinds: new Set(paramTaint),
669
+ confidence: 'high',
670
+ })
671
+ }
672
+
673
+ if (syntheticSources.length === 0) {
674
+ ctx.resultCache.set(cacheKey, null)
675
+ return null
676
+ }
677
+
678
+ // Phase 8: Get sinks and sanitizers from cache or recompute
679
+ const calleeSinks = fileCache?.sinks ?? classifySinks(calleeAST)
680
+ const calleeSanitizers = fileCache?.sanitizers ?? buildSanitizerRegistry(calleeAST)
681
+
682
+ // Map synthetic parameter sources to the CFG entry node.
683
+ // Parameters aren't inside any statement node, so mapSourcesToCFG would fail.
684
+ // Instead, seed them at the entry node — parameters are defined at function entry.
685
+ let entryNodeId = -1
686
+ for (const [id, node] of calleeCFG.nodes) {
687
+ if (node.kind === 'entry') {
688
+ entryNodeId = id
689
+ break
690
+ }
691
+ }
692
+ if (entryNodeId === -1) {
693
+ ctx.resultCache.set(cacheKey, null)
694
+ return null
695
+ }
696
+
697
+ const sourceMappings: SourceMapping[] = syntheticSources.map(source => ({
698
+ source,
699
+ cfgNodeId: entryNodeId,
700
+ }))
701
+ // Phase 8 (Part 4): use cached CFG node map for O(D) lookup
702
+ const calleeCFGNodeMap = fileCache?.cfgNodeMaps.get(funcDef.functionName)
703
+ const sinkMappings = mapSinksToCFG(calleeCFG, calleeSinks, calleeCFGNodeMap)
704
+ const sanitizerMap = buildSanitizerMap(calleeCFG, calleeSanitizers, calleeCFGNodeMap)
705
+
706
+ // Push to call stack
707
+ callStack.add(callKey)
708
+
709
+ // Create child context for deeper calls
710
+ const childContext: CallResolutionContext = {
711
+ ...ctx,
712
+ callStack: new Set(callStack),
713
+ }
714
+
715
+ // Run fixpoint ONCE — used for both callee findings and return taint
716
+ const calleeConstants = calleeCFG.functionNode ? collectConstants(calleeCFG.functionNode) : new Map()
717
+ const outStates = runFixpointInternal(calleeCFG, sourceMappings, sanitizerMap, funcDef.filePath, childContext, calleeConstants)
718
+ const calleeFindings = deduplicateFindings(
719
+ checkSinksInternal(calleeCFG, outStates, sinkMappings, sourceMappings, funcDef.filePath),
720
+ )
721
+
722
+ // Pop from call stack
723
+ callStack.delete(callKey)
724
+
725
+ // Extract return-value taint from the already-computed out-states
726
+ const returnTaint = extractReturnTaint(calleeCFG, outStates, calleeSanitizers, childContext, funcDef.filePath)
727
+
728
+ // Build path steps
729
+ const steps: TaintStep[] = [
730
+ {
731
+ nodeId: 0,
732
+ line: callNode.startPosition.row + 1,
733
+ variable: funcDef.functionName,
734
+ taintKinds: new Set(returnTaint),
735
+ description: `CALL_ENTRY ${funcDef.functionName}() in ${funcDef.filePath}`,
736
+ stepType: 'call_entry',
737
+ filePath: funcDef.filePath,
738
+ functionName: funcDef.functionName,
739
+ },
740
+ {
741
+ nodeId: 0,
742
+ line: callNode.startPosition.row + 1,
743
+ variable: funcDef.functionName,
744
+ taintKinds: new Set(returnTaint),
745
+ description: `CALL_RETURN ${funcDef.functionName}() → ${[...returnTaint].join(',')}`,
746
+ stepType: 'call_return',
747
+ filePath: callerFile,
748
+ functionName: funcDef.functionName,
749
+ },
750
+ ]
751
+
752
+ const calleeResult: CalleeResult = {
753
+ returnTaint,
754
+ steps,
755
+ calleeFindings,
756
+ }
757
+
758
+ ctx.resultCache.set(cacheKey, calleeResult)
759
+ return calleeResult
760
+ }
761
+
762
+ /**
763
+ * Extract taint kinds from return statements using pre-computed out-states.
764
+ * Walks all return statements in the CFG and resolves expression taint.
765
+ */
766
+ function extractReturnTaint(
767
+ cfg: CFG,
768
+ outStates: Map<number, TaintState>,
769
+ sanitizers: TaintSanitizer[],
770
+ callContext?: CallResolutionContext,
771
+ filePath?: string,
772
+ ): Set<TaintKind> {
773
+ const returnTaint = new Set<TaintKind>()
774
+
775
+ for (const [, cfgNode] of cfg.nodes) {
776
+ if (!cfgNode.astNode) continue
777
+ const ast = cfgNode.astNode
778
+
779
+ if (ast.type !== 'return_statement') continue
780
+
781
+ const returnExpr = ast.namedChildren[0]
782
+ if (!returnExpr) continue
783
+
784
+ // Get the taint state at this node: merge predecessors + own out-state
785
+ const predIds = getPredecessors(cfg, cfgNode.id)
786
+ const predStates: TaintState[] = []
787
+ for (const pId of predIds) {
788
+ const ps = outStates.get(pId)
789
+ if (ps) predStates.push(ps)
790
+ }
791
+ const nodeInState = mergeStates(predStates)
792
+ const nodeState = mergeStates([nodeInState, outStates.get(cfgNode.id) ?? new Map()])
793
+
794
+ const exprTaint = resolveExpressionTaint(returnExpr, nodeState, sanitizers, callContext, filePath)
795
+ for (const k of exprTaint) returnTaint.add(k)
796
+ }
797
+
798
+ return returnTaint
799
+ }
800
+
801
+ /** Find a parameter's AST node in a function definition. */
802
+ function findParamNode(
803
+ fnNode: Parser.SyntaxNode,
804
+ paramName: string,
805
+ ): Parser.SyntaxNode | null {
806
+ const params = fnNode.childForFieldName('parameters')
807
+ if (!params) return null
808
+
809
+ for (const child of params.namedChildren) {
810
+ if (child.type === 'identifier' && child.text === paramName) return child
811
+ // JS/TS param types
812
+ if (child.type === 'required_parameter' || child.type === 'optional_parameter') {
813
+ const pattern = child.childForFieldName('pattern')
814
+ if (pattern?.type === 'identifier' && pattern.text === paramName) return pattern
815
+ }
816
+ if (child.type === 'assignment_pattern') {
817
+ const left = child.childForFieldName('left')
818
+ if (left?.type === 'identifier' && left.text === paramName) return left
819
+ }
820
+ // Python param types
821
+ if (child.type === 'typed_parameter' || child.type === 'default_parameter' ||
822
+ child.type === 'typed_default_parameter') {
823
+ const nameNode = child.namedChildren.find(c => c.type === 'identifier')
824
+ if (nameNode && nameNode.text === paramName) return nameNode
825
+ }
826
+ if (child.type === 'list_splat_pattern' || child.type === 'dictionary_splat_pattern') {
827
+ const ident = child.namedChildren.find(c => c.type === 'identifier')
828
+ if (ident && ident.text === paramName) return ident
829
+ }
830
+ }
831
+
832
+ // Fallback: search for identifier in params subtree
833
+ let found: Parser.SyntaxNode | null = null
834
+ walkAST(params, (node) => {
835
+ if (node.type === 'identifier' && node.text === paramName && !found) {
836
+ found = node
837
+ return true
838
+ }
839
+ return false
840
+ })
841
+ return found
842
+ }
843
+
844
+ /**
845
+ * Find a sanitizer whose AST node matches this call expression.
846
+ */
847
+ function findMatchingSanitizer(
848
+ callNode: Parser.SyntaxNode,
849
+ sanitizers: TaintSanitizer[],
850
+ ): TaintSanitizer | null {
851
+ for (const s of sanitizers) {
852
+ if (s.node.id === callNode.id) return s
853
+ // Also check if the sanitizer's node is a parent/ancestor of this call
854
+ if (isDescendantOf(callNode, s.node)) return s
855
+ }
856
+ return null
857
+ }
858
+
859
+ /** Check if a node represents a literal value (no taint). */
860
+ function isLiteral(node: Parser.SyntaxNode): boolean {
861
+ switch (node.type) {
862
+ case 'string':
863
+ // Python f-strings have type 'string' but contain interpolation children — NOT a literal
864
+ if (node.descendantsOfType('interpolation').length > 0) return false
865
+ return true
866
+ case 'number':
867
+ case 'true':
868
+ case 'false':
869
+ case 'null':
870
+ case 'undefined':
871
+ case 'regex':
872
+ // Python literals
873
+ case 'integer':
874
+ case 'float':
875
+ case 'none':
876
+ return true
877
+ default:
878
+ return false
879
+ }
880
+ }
881
+
882
+ // ============================================================================
883
+ // Statement-level Transfer Function
884
+ // ============================================================================
885
+
886
+ /**
887
+ * Apply the transfer function for a single CFG node.
888
+ * Given the incoming taint state, compute the outgoing taint state.
889
+ */
890
+ export function applyTransfer(
891
+ cfgNode: CFGNode,
892
+ inState: TaintState,
893
+ sanitizerMap: Map<number, TaintSanitizer[]>,
894
+ callContext?: CallResolutionContext,
895
+ callerFile?: string,
896
+ constantMap?: ConstantMap,
897
+ ): TaintState {
898
+ // Phase 8 (3b): skip clone for no-op nodes — entry, exit, and nodes without AST
899
+ if (!cfgNode.astNode) return inState
900
+ if (cfgNode.kind === 'entry' || cfgNode.kind === 'exit') return inState
901
+
902
+ // Phase 8 (3b): condition nodes with no defs and no sanitizers are read-only
903
+ if (cfgNode.kind === 'condition' && cfgNode.defs.size === 0) {
904
+ const hasSanitizers = sanitizerMap.has(cfgNode.id)
905
+ if (!hasSanitizers) return inState
906
+ }
907
+
908
+ const outState = cloneState(inState)
909
+ const sanitizers = sanitizerMap.get(cfgNode.id) ?? []
910
+ const ast = cfgNode.astNode
911
+
912
+ applyNodeTransfer(ast, outState, sanitizers, callContext, callerFile, constantMap)
913
+
914
+ return outState
915
+ }
916
+
917
+ /**
918
+ * Recursively apply transfer for an AST node within a CFG statement.
919
+ */
920
+ function applyNodeTransfer(
921
+ node: Parser.SyntaxNode,
922
+ state: TaintState,
923
+ sanitizers: TaintSanitizer[],
924
+ callContext?: CallResolutionContext,
925
+ callerFile?: string,
926
+ constantMap?: ConstantMap,
927
+ ): void {
928
+ // Helper for expression taint resolution with context forwarding
929
+ const resolve = (expr: Parser.SyntaxNode) =>
930
+ resolveExpressionTaint(expr, state, sanitizers, callContext, callerFile)
931
+
932
+ // Variable declaration: const x = <expr>
933
+ if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {
934
+ for (const child of node.namedChildren) {
935
+ applyNodeTransfer(child, state, sanitizers, callContext, callerFile, constantMap)
936
+ }
937
+ return
938
+ }
939
+
940
+ if (node.type === 'variable_declarator') {
941
+ const nameNode = node.childForFieldName('name')
942
+ const valueNode = node.childForFieldName('value')
943
+ if (!nameNode || !valueNode) return
944
+
945
+ if (nameNode.type === 'identifier') {
946
+ // Constant propagation: if RHS is entirely constant-derived, clear taint
947
+ if (constantMap && isConstantExpression(valueNode, constantMap)) {
948
+ deleteTaint(state, nameNode.text)
949
+ return
950
+ }
951
+ // Simple: const x = expr
952
+ const rhsTaint = resolve(valueNode)
953
+ if (rhsTaint.size > 0) {
954
+ setTaint(state, nameNode.text, rhsTaint)
955
+ } else {
956
+ deleteTaint(state, nameNode.text)
957
+ }
958
+ } else if (nameNode.type === 'object_pattern' || nameNode.type === 'array_pattern') {
959
+ // Destructuring: const { a, b } = expr
960
+ const rhsTaint = resolve(valueNode)
961
+ const rhsVarName = valueNode.type === 'identifier' ? valueNode.text
962
+ : (valueNode.type === 'member_expression' ? getMemberRoot(valueNode) : null)
963
+ applyDestructuringTaint(nameNode, rhsTaint, state, rhsVarName ?? undefined)
964
+ }
965
+ return
966
+ }
967
+
968
+ // Assignment: x = expr
969
+ // Python: 'assignment', JS: 'assignment_expression'
970
+ if (node.type === 'assignment_expression' || node.type === 'assignment') {
971
+ const left = node.childForFieldName('left')
972
+ const right = node.childForFieldName('right')
973
+ if (!left || !right) return
974
+
975
+ if (left.type === 'identifier') {
976
+ // Constant propagation: if RHS is entirely constant-derived, clear taint
977
+ if (constantMap && isConstantExpression(right, constantMap)) {
978
+ deleteTaint(state, left.text)
979
+ return
980
+ }
981
+ const rhsTaint = resolve(right)
982
+ if (rhsTaint.size > 0) {
983
+ setTaint(state, left.text, rhsTaint)
984
+ } else {
985
+ deleteTaint(state, left.text)
986
+ }
987
+ } else if (left.type === 'object_pattern' || left.type === 'array_pattern' ||
988
+ left.type === 'tuple_pattern' || left.type === 'list_pattern' ||
989
+ left.type === 'pattern_list' || left.type === 'tuple' || left.type === 'list') {
990
+ // Destructuring / Python unpacking: a, b = expr
991
+ const rhsTaint = resolve(right)
992
+ const rhsVarName = right.type === 'identifier' ? right.text
993
+ : (right.type === 'member_expression' || right.type === 'attribute' ? getMemberRoot(right) : null)
994
+ applyDestructuringTaint(left, rhsTaint, state, rhsVarName ?? undefined)
995
+ } else if (left.type === 'member_expression' || left.type === 'attribute') {
996
+ // obj.prop = val — field-level taint
997
+ // Python: 'attribute' with fields 'object'/'attribute', JS: 'member_expression' with 'object'/'property'
998
+ const obj = left.childForFieldName('object')
999
+ const prop = left.type === 'attribute'
1000
+ ? left.childForFieldName('attribute')
1001
+ : left.childForFieldName('property')
1002
+ if (obj?.type === 'identifier' && prop && (prop.type === 'property_identifier' || prop.type === 'identifier')) {
1003
+ // One-level field assignment: obj.field = val
1004
+ const rhsTaint = resolve(right)
1005
+ if (rhsTaint.size > 0) {
1006
+ setFieldTaint(state, obj.text, prop.text, rhsTaint)
1007
+ }
1008
+ } else {
1009
+ // Deep chain or computed: fall back to root object taint
1010
+ const root = getMemberRoot(left)
1011
+ if (root) {
1012
+ const rhsTaint = resolve(right)
1013
+ // Phase 8: clone before mutating — getTaint may return internal Set reference
1014
+ const existing = new Set(getTaint(state, root) ?? [])
1015
+ for (const k of rhsTaint) existing.add(k)
1016
+ if (existing.size > 0) setTaint(state, root, existing)
1017
+ }
1018
+ }
1019
+ }
1020
+ return
1021
+ }
1022
+
1023
+ // Augmented assignment: x += expr
1024
+ // Python: 'augmented_assignment', JS: 'augmented_assignment_expression'
1025
+ if (node.type === 'augmented_assignment_expression' || node.type === 'augmented_assignment') {
1026
+ const left = node.childForFieldName('left')
1027
+ const right = node.childForFieldName('right')
1028
+ if (!left || !right) return
1029
+
1030
+ if (left.type === 'identifier') {
1031
+ const rhsTaint = resolve(right)
1032
+ // Phase 8: clone before mutating — getTaint may return internal Set reference
1033
+ const existing = new Set(getTaint(state, left.text) ?? [])
1034
+ for (const k of rhsTaint) existing.add(k)
1035
+ if (existing.size > 0) setTaint(state, left.text, existing)
1036
+ }
1037
+ return
1038
+ }
1039
+
1040
+ // Expression statement: unwrap the inner expression
1041
+ if (node.type === 'expression_statement') {
1042
+ for (const child of node.namedChildren) {
1043
+ applyNodeTransfer(child, state, sanitizers, callContext, callerFile, constantMap)
1044
+ }
1045
+ return
1046
+ }
1047
+
1048
+ // For-in header: captures iteration variable taint from the right side
1049
+ if (node.type === 'for_in_statement') {
1050
+ const left = node.childForFieldName('left')
1051
+ const right = node.childForFieldName('right')
1052
+ if (left && right) {
1053
+ const rhsTaint = resolve(right)
1054
+ if (left.type === 'identifier') {
1055
+ if (rhsTaint.size > 0) setTaint(state, left.text, rhsTaint)
1056
+ } else {
1057
+ // Destructuring in for-of/for-in
1058
+ applyDestructuringTaint(left, rhsTaint, state)
1059
+ }
1060
+ }
1061
+ return
1062
+ }
1063
+
1064
+ // Python for loop with left/right fields: for x in iterable
1065
+ if (node.type === 'for_statement') {
1066
+ const left = node.childForFieldName('left')
1067
+ const right = node.childForFieldName('right')
1068
+ if (left && right) {
1069
+ const rhsTaint = resolve(right)
1070
+ if (left.type === 'identifier') {
1071
+ if (rhsTaint.size > 0) setTaint(state, left.text, rhsTaint)
1072
+ } else {
1073
+ applyDestructuringTaint(left, rhsTaint, state)
1074
+ }
1075
+ }
1076
+ return
1077
+ }
1078
+
1079
+ // Python with statement: with expr as var
1080
+ if (node.type === 'with_statement') {
1081
+ // The with_clause may contain 'as_pattern' nodes: expr as name
1082
+ for (const child of node.namedChildren) {
1083
+ if (child.type === 'with_clause') {
1084
+ for (const item of child.namedChildren) {
1085
+ if (item.type === 'with_item' || item.type === 'as_pattern') {
1086
+ const expr = item.namedChildren[0]
1087
+ const alias = item.namedChildren[1]
1088
+ if (expr && alias?.type === 'identifier') {
1089
+ const exprTaint = resolve(expr)
1090
+ if (exprTaint.size > 0) setTaint(state, alias.text, exprTaint)
1091
+ }
1092
+ }
1093
+ }
1094
+ }
1095
+ }
1096
+ return
1097
+ }
1098
+
1099
+ // Standalone call expression: trigger expression resolution so cross-file
1100
+ // analysis discovers callee findings (sinks reached inside the called function).
1101
+ // Without this, standalone calls like `query(input)` would be silently ignored.
1102
+ // Python: 'call', JS: 'call_expression'
1103
+ if (node.type === 'call_expression' || node.type === 'call') {
1104
+ // Inject taint into callback parameters for promise chains and callback APIs.
1105
+ // This must happen BEFORE resolve() so the taint state is available when
1106
+ // analyzing the callback body's expressions.
1107
+ injectPromiseChainTaint(node, state)
1108
+ injectCallbackTaint(node, state)
1109
+ resolve(node)
1110
+ return
1111
+ }
1112
+
1113
+ // Return / throw: no defs, but the expression is "used"
1114
+ // (no state changes needed — uses are tracked by def-use)
1115
+ }
1116
+
1117
+ /**
1118
+ * Apply taint from a destructuring pattern.
1119
+ * When rhsVarName is provided and object_pattern is used, field-specific
1120
+ * taint is extracted per destructured key. Otherwise falls back to whole-object taint.
1121
+ */
1122
+ function applyDestructuringTaint(
1123
+ pattern: Parser.SyntaxNode,
1124
+ rhsTaint: Set<TaintKind>,
1125
+ state: TaintState,
1126
+ rhsVarName?: string,
1127
+ ): void {
1128
+ if (rhsTaint.size === 0) return
1129
+
1130
+ if (pattern.type === 'identifier') {
1131
+ setTaint(state, pattern.text, new Set(rhsTaint))
1132
+ return
1133
+ }
1134
+
1135
+ if (pattern.type === 'object_pattern') {
1136
+ for (const prop of pattern.namedChildren) {
1137
+ if (prop.type === 'shorthand_property_identifier_pattern') {
1138
+ // { email } = obj → email gets field-specific taint from obj.email
1139
+ if (rhsVarName) {
1140
+ const fieldTaint = getFieldTaint(state, rhsVarName, prop.text)
1141
+ if (fieldTaint && fieldTaint.size > 0) {
1142
+ setTaint(state, prop.text, new Set(fieldTaint))
1143
+ } else {
1144
+ // Field has no taint — don't assign whole-object taint
1145
+ deleteTaint(state, prop.text)
1146
+ }
1147
+ } else {
1148
+ setTaint(state, prop.text, new Set(rhsTaint))
1149
+ }
1150
+ } else if (prop.type === 'pair_pattern') {
1151
+ // { email: e } = obj → e gets taint from obj.email
1152
+ const key = prop.childForFieldName('key')
1153
+ const value = prop.childForFieldName('value')
1154
+ if (value && rhsVarName && key) {
1155
+ const fieldName = key.text
1156
+ const fieldTaint = getFieldTaint(state, rhsVarName, fieldName)
1157
+ if (fieldTaint && fieldTaint.size > 0) {
1158
+ applyDestructuringTaint(value, fieldTaint, state)
1159
+ }
1160
+ } else if (value) {
1161
+ applyDestructuringTaint(value, rhsTaint, state)
1162
+ }
1163
+ } else if (prop.type === 'rest_pattern') {
1164
+ // { ...rest } = obj → rest gets whole-object taint (conservative)
1165
+ const inner = prop.namedChildren[0]
1166
+ if (inner) applyDestructuringTaint(inner, rhsTaint, state)
1167
+ }
1168
+ }
1169
+ return
1170
+ }
1171
+
1172
+ if (pattern.type === 'array_pattern') {
1173
+ for (const elem of pattern.namedChildren) {
1174
+ applyDestructuringTaint(elem, rhsTaint, state)
1175
+ }
1176
+ return
1177
+ }
1178
+
1179
+ // Python unpacking: a, b = expr / (a, b) = expr / [a, b] = expr
1180
+ if (pattern.type === 'tuple_pattern' || pattern.type === 'list_pattern' ||
1181
+ pattern.type === 'pattern_list' || pattern.type === 'expression_list' ||
1182
+ pattern.type === 'tuple' || pattern.type === 'list') {
1183
+ for (const elem of pattern.namedChildren) {
1184
+ applyDestructuringTaint(elem, rhsTaint, state)
1185
+ }
1186
+ return
1187
+ }
1188
+
1189
+ // Python list_splat_pattern: *rest
1190
+ if (pattern.type === 'list_splat_pattern') {
1191
+ const inner = pattern.namedChildren[0]
1192
+ if (inner) applyDestructuringTaint(inner, rhsTaint, state)
1193
+ return
1194
+ }
1195
+
1196
+ if (pattern.type === 'assignment_pattern') {
1197
+ const left = pattern.childForFieldName('left')
1198
+ if (left) applyDestructuringTaint(left, rhsTaint, state, rhsVarName)
1199
+ return
1200
+ }
1201
+
1202
+ if (pattern.type === 'rest_pattern') {
1203
+ const inner = pattern.namedChildren[0]
1204
+ if (inner) applyDestructuringTaint(inner, rhsTaint, state)
1205
+ }
1206
+ }
1207
+
1208
+ /** Get the root identifier of a member expression chain: `a.b.c` → 'a'. */
1209
+ function getMemberRoot(node: Parser.SyntaxNode): string | null {
1210
+ let current = node
1211
+ while (current.type === 'member_expression' || current.type === 'subscript_expression' ||
1212
+ current.type === 'attribute' || current.type === 'subscript') {
1213
+ const obj = current.childForFieldName('object')
1214
+ ?? current.childForFieldName('value') // Python subscript uses 'value' field
1215
+ if (!obj) return null
1216
+ current = obj
1217
+ }
1218
+ return current.type === 'identifier' ? current.text : null
1219
+ }
1220
+
1221
+ // ============================================================================
1222
+ // Source / Sink Mapping
1223
+ // ============================================================================
1224
+
1225
+ export interface SourceMapping {
1226
+ source: TaintSource
1227
+ cfgNodeId: number
1228
+ }
1229
+
1230
+ export interface SinkMapping {
1231
+ sink: TaintSink
1232
+ cfgNodeId: number
1233
+ }
1234
+
1235
+ /**
1236
+ * Map sources to their containing CFG nodes.
1237
+ * Phase 8: accepts optional cfgNodeMap for O(D) lookup instead of O(N).
1238
+ */
1239
+ export function mapSourcesToCFG(
1240
+ cfg: CFG,
1241
+ sources: TaintSource[],
1242
+ cfgNodeMap?: Map<number, CFGNode>,
1243
+ ): SourceMapping[] {
1244
+ const mappings: SourceMapping[] = []
1245
+ for (const source of sources) {
1246
+ const cfgNode = cfgNodeMap
1247
+ ? findContainingCFGNodeFast(cfgNodeMap, source.node)
1248
+ : findContainingCFGNode(cfg, source.node)
1249
+ if (cfgNode) {
1250
+ mappings.push({ source, cfgNodeId: cfgNode.id })
1251
+ }
1252
+ }
1253
+ return mappings
1254
+ }
1255
+
1256
+ /**
1257
+ * Map sinks to their containing CFG nodes.
1258
+ * Phase 8: accepts optional cfgNodeMap for O(D) lookup instead of O(N).
1259
+ */
1260
+ export function mapSinksToCFG(
1261
+ cfg: CFG,
1262
+ sinks: TaintSink[],
1263
+ cfgNodeMap?: Map<number, CFGNode>,
1264
+ ): SinkMapping[] {
1265
+ const mappings: SinkMapping[] = []
1266
+ for (const sink of sinks) {
1267
+ const cfgNode = cfgNodeMap
1268
+ ? findContainingCFGNodeFast(cfgNodeMap, sink.node)
1269
+ : findContainingCFGNode(cfg, sink.node)
1270
+ if (cfgNode) {
1271
+ mappings.push({ sink, cfgNodeId: cfgNode.id })
1272
+ }
1273
+ }
1274
+ return mappings
1275
+ }
1276
+
1277
+ // ============================================================================
1278
+ // Worklist Algorithm
1279
+ // ============================================================================
1280
+
1281
+ const MAX_ITERATIONS = 1000
1282
+
1283
+ /**
1284
+ * Run the worklist fixpoint algorithm on a CFG.
1285
+ *
1286
+ * Seeds taint at source nodes, propagates forward through the CFG until
1287
+ * no more state changes occur (fixpoint). Returns the per-node out-states.
1288
+ *
1289
+ * Exported as runFixpointInternal for use by taint-summary.ts (Phase 8).
1290
+ */
1291
+ export function runFixpointInternal(
1292
+ cfg: CFG,
1293
+ sourceMappings: SourceMapping[],
1294
+ sanitizerMap: Map<number, TaintSanitizer[]>,
1295
+ filePath: string,
1296
+ callContext?: CallResolutionContext,
1297
+ constantMap?: ConstantMap,
1298
+ ): Map<number, TaintState> {
1299
+ // Per-node out-states
1300
+ const outStates = new Map<number, TaintState>()
1301
+ for (const [id] of cfg.nodes) {
1302
+ outStates.set(id, new Map())
1303
+ }
1304
+
1305
+ // Seed sources: set source variable taint in the source node's out-state
1306
+ const worklist = new Set<number>()
1307
+
1308
+ // Build a per-node source seed map so seeds survive worklist overwrites.
1309
+ // Without this, when a predecessor's propagation causes node N to be
1310
+ // re-processed, applyTransfer overwrites the seeded out-state with just
1311
+ // the propagated in-state — losing any source taint that was seeded here.
1312
+ const sourceSeeds = new Map<number, Array<{ variable: string; kinds: Set<TaintKind>; isDestructuring: boolean; defs: Set<string> }>>()
1313
+
1314
+ for (const { source, cfgNodeId } of sourceMappings) {
1315
+ const state = outStates.get(cfgNodeId)!
1316
+ const cfgNode = cfg.nodes.get(cfgNodeId)
1317
+ const varName = source.variable
1318
+
1319
+ const isDestructuring = varName.startsWith('{') || varName.startsWith('[')
1320
+ if (isDestructuring && cfgNode && cfgNode.defs.size > 0) {
1321
+ for (const defVar of cfgNode.defs) {
1322
+ // Phase 8: clone before mutating — getTaint may return internal Set reference
1323
+ const existing = new Set(getTaint(state, defVar) ?? [])
1324
+ for (const k of source.taintKinds) existing.add(k)
1325
+ setTaint(state, defVar, existing)
1326
+ }
1327
+ } else {
1328
+ // Phase 8: clone before mutating — getTaint may return internal Set reference
1329
+ const existing = new Set(getTaint(state, varName) ?? [])
1330
+ for (const k of source.taintKinds) existing.add(k)
1331
+ setTaint(state, varName, existing)
1332
+
1333
+ // For compound source variables (e.g. "req.body", "req.query"), also seed
1334
+ // the root identifier. The expression-level resolver walks member chains
1335
+ // from the root, so `req.body.email` resolves `req` → tainted → propagates.
1336
+ if (varName.includes('.')) {
1337
+ const root = varName.split('.')[0]
1338
+ const rootExisting = new Set(getTaint(state, root) ?? [])
1339
+ for (const k of source.taintKinds) rootExisting.add(k)
1340
+ setTaint(state, root, rootExisting)
1341
+ }
1342
+ }
1343
+
1344
+ // Record seed for re-injection during worklist
1345
+ if (!sourceSeeds.has(cfgNodeId)) sourceSeeds.set(cfgNodeId, [])
1346
+ sourceSeeds.get(cfgNodeId)!.push({
1347
+ variable: varName,
1348
+ kinds: new Set(source.taintKinds),
1349
+ isDestructuring,
1350
+ defs: cfgNode?.defs ?? new Set(),
1351
+ })
1352
+
1353
+ // Push successors to worklist (the source node's out-state is now seeded)
1354
+ for (const succ of getSuccessors(cfg, cfgNodeId)) {
1355
+ worklist.add(succ)
1356
+ }
1357
+ }
1358
+
1359
+ // Iterate to fixpoint
1360
+ let iterations = 0
1361
+
1362
+ while (worklist.size > 0 && iterations < MAX_ITERATIONS) {
1363
+ iterations++
1364
+
1365
+ // Pop first element from worklist
1366
+ const nodeId = worklist.values().next().value!
1367
+ worklist.delete(nodeId)
1368
+
1369
+ const cfgNode = cfg.nodes.get(nodeId)
1370
+ if (!cfgNode) continue
1371
+
1372
+ // Compute in-state = merge of all predecessor out-states
1373
+ // Phase 8 (3a): single-predecessor fast path — skip mergeStates allocation for ~70% of nodes
1374
+ const predIds = getPredecessors(cfg, nodeId)
1375
+ let inState: TaintState
1376
+ if (predIds.length === 1) {
1377
+ const ps = outStates.get(predIds[0])
1378
+ inState = ps ?? new Map()
1379
+ } else {
1380
+ const predStates: TaintState[] = []
1381
+ for (const pId of predIds) {
1382
+ const ps = outStates.get(pId)
1383
+ if (ps) predStates.push(ps)
1384
+ }
1385
+ inState = mergeStates(predStates)
1386
+ }
1387
+
1388
+ // Apply transfer function
1389
+ const newOutState = applyTransfer(cfgNode, inState, sanitizerMap, callContext, filePath, constantMap)
1390
+
1391
+ // Re-inject source seeds: if this node has sources, ensure their taint
1392
+ // survives the transfer function (which only sees predecessor in-state).
1393
+ // Phase 8: clone before mutating — getTaint may return internal Set reference
1394
+ const seeds = sourceSeeds.get(nodeId)
1395
+ if (seeds) {
1396
+ for (const seed of seeds) {
1397
+ if (seed.isDestructuring && seed.defs.size > 0) {
1398
+ for (const defVar of seed.defs) {
1399
+ const existing = new Set(getTaint(newOutState, defVar) ?? [])
1400
+ for (const k of seed.kinds) existing.add(k)
1401
+ setTaint(newOutState, defVar, existing)
1402
+ }
1403
+ } else {
1404
+ const existing = new Set(getTaint(newOutState, seed.variable) ?? [])
1405
+ for (const k of seed.kinds) existing.add(k)
1406
+ setTaint(newOutState, seed.variable, existing)
1407
+
1408
+ if (seed.variable.includes('.')) {
1409
+ const root = seed.variable.split('.')[0]
1410
+ const rootExisting = new Set(getTaint(newOutState, root) ?? [])
1411
+ for (const k of seed.kinds) rootExisting.add(k)
1412
+ setTaint(newOutState, root, rootExisting)
1413
+ }
1414
+ }
1415
+ }
1416
+ }
1417
+
1418
+ // Check if state changed
1419
+ const oldOutState = outStates.get(nodeId)!
1420
+ // Phase 8 (3d): identity check — if applyTransfer returned same ref, no change possible
1421
+ if (newOutState !== oldOutState && !statesEqual(newOutState, oldOutState)) {
1422
+ outStates.set(nodeId, newOutState)
1423
+ // Push all successors to worklist
1424
+ for (const succ of getSuccessors(cfg, nodeId)) {
1425
+ worklist.add(succ)
1426
+ }
1427
+ }
1428
+ }
1429
+
1430
+ if (iterations >= MAX_ITERATIONS) {
1431
+ console.warn(`[taint] Worklist hit iteration limit (${MAX_ITERATIONS}) for ${cfg.functionName} — results may be incomplete`)
1432
+ }
1433
+
1434
+ return outStates
1435
+ }
1436
+
1437
+ /**
1438
+ * Check sinks for tainted data given computed out-states.
1439
+ * Returns findings for each source→sink pair with matching taint kinds.
1440
+ *
1441
+ * Exported as checkSinksInternal for use by taint-summary.ts (Phase 8).
1442
+ */
1443
+ export function checkSinksInternal(
1444
+ cfg: CFG,
1445
+ outStates: Map<number, TaintState>,
1446
+ sinkMappings: SinkMapping[],
1447
+ sourceMappings: SourceMapping[],
1448
+ filePath: string,
1449
+ ): TaintFinding[] {
1450
+ const findings: TaintFinding[] = []
1451
+
1452
+ for (const { sink, cfgNodeId } of sinkMappings) {
1453
+ // The taint state at the sink is the in-state (merge of predecessor outs)
1454
+ const predIds = getPredecessors(cfg, cfgNodeId)
1455
+ const predStates: TaintState[] = []
1456
+ for (const pId of predIds) {
1457
+ const ps = outStates.get(pId)
1458
+ if (ps) predStates.push(ps)
1459
+ }
1460
+ // Also include the sink node's own out-state predecessor contributions
1461
+ // But more importantly, check the in-state to the sink node
1462
+ const sinkInState = mergeStates(predStates)
1463
+
1464
+ // Also check the out-state of the sink's own node (for cases where
1465
+ // source and sink are in the same statement or sink uses vars from its own node)
1466
+ const sinkOutState = outStates.get(cfgNodeId) ?? new Map()
1467
+
1468
+ // Merge both for maximum coverage
1469
+ const combined = mergeStates([sinkInState, sinkOutState])
1470
+
1471
+ // Find which variables at this sink are tainted with matching kinds
1472
+ const sinkNode = cfg.nodes.get(cfgNodeId)
1473
+ if (!sinkNode) continue
1474
+
1475
+ // Get all variables used by the sink expression
1476
+ const sinkUses = extractSinkUses(sink, sinkNode)
1477
+
1478
+ // Extract field-level accesses from the sink for precise taint checking
1479
+ const sinkFieldAccesses = extractSinkFieldAccesses(sink)
1480
+
1481
+ for (const varName of sinkUses) {
1482
+ // Check if this variable has a field-level access in the sink
1483
+ const fieldAccess = sinkFieldAccesses.get(varName)
1484
+ const varTaint = fieldAccess
1485
+ ? getFieldTaint(combined, varName, fieldAccess)
1486
+ : getTaint(combined, varName)
1487
+ if (!varTaint || varTaint.size === 0) continue
1488
+
1489
+ // Find matching kinds: intersection of variable's taint and sink's vulnerable kinds
1490
+ const matchingKinds = new Set<TaintKind>()
1491
+ for (const k of varTaint) {
1492
+ if (sink.vulnerableToKinds.has(k)) {
1493
+ matchingKinds.add(k)
1494
+ }
1495
+ }
1496
+
1497
+ if (matchingKinds.size === 0) continue
1498
+
1499
+ // Find which source(s) contributed this taint
1500
+ for (const { source } of sourceMappings) {
1501
+ // Check if source's taint kinds overlap with matching kinds
1502
+ const sourceOverlap = new Set<TaintKind>()
1503
+ for (const k of matchingKinds) {
1504
+ if (source.taintKinds.has(k)) sourceOverlap.add(k)
1505
+ }
1506
+ if (sourceOverlap.size === 0) continue
1507
+
1508
+ // Reconstruct path
1509
+ const path = reconstructPath(
1510
+ cfg, outStates, source, sink, varName,
1511
+ sourceMappings, cfgNodeId,
1512
+ )
1513
+
1514
+ findings.push({
1515
+ source,
1516
+ sink,
1517
+ path,
1518
+ matchingKinds: sourceOverlap,
1519
+ functionName: cfg.functionName,
1520
+ filePath,
1521
+ })
1522
+ }
1523
+ }
1524
+ }
1525
+
1526
+ return findings
1527
+ }
1528
+
1529
+ /**
1530
+ * Propagate taint through a single function's CFG.
1531
+ *
1532
+ * Returns all taint findings (source→sink pairs with matching kinds).
1533
+ * When callContext is provided, calls to imported/local functions are
1534
+ * resolved inter-procedurally.
1535
+ */
1536
+ export function propagateFunction(
1537
+ cfg: CFG,
1538
+ sourceMappings: SourceMapping[],
1539
+ sinkMappings: SinkMapping[],
1540
+ sanitizerMap: Map<number, TaintSanitizer[]>,
1541
+ filePath: string,
1542
+ callContext?: CallResolutionContext,
1543
+ ): TaintFinding[] {
1544
+ // Collect constants for this function scope
1545
+ const constantMap = cfg.functionNode ? collectConstants(cfg.functionNode) : new Map()
1546
+ const outStates = runFixpointInternal(cfg, sourceMappings, sanitizerMap, filePath, callContext, constantMap)
1547
+ const findings = checkSinksInternal(cfg, outStates, sinkMappings, sourceMappings, filePath)
1548
+ return deduplicateFindings(findings)
1549
+ }
1550
+
1551
+ /**
1552
+ * Extract variables used by a sink expression.
1553
+ * Uses the CFG node's `uses` set plus any identifiers in the sink's AST.
1554
+ *
1555
+ * When `sink.relevantArgNodes` is set (e.g., SSRF sinks), only variables
1556
+ * within those specific argument nodes are collected — taint in other
1557
+ * arguments (like request body) won't trigger the finding.
1558
+ */
1559
+ function extractSinkUses(sink: TaintSink, cfgNode: CFGNode): Set<string> {
1560
+ // When the sink specifies relevant arg nodes, ONLY collect vars from those nodes.
1561
+ // This prevents e.g. tainted body data from triggering SSRF (which requires tainted URL).
1562
+ if (sink.relevantArgNodes && sink.relevantArgNodes.length > 0) {
1563
+ const uses = new Set<string>()
1564
+ for (const argNode of sink.relevantArgNodes) {
1565
+ walkAST(argNode, (node) => {
1566
+ if (node.type === 'identifier') {
1567
+ uses.add(node.text)
1568
+ }
1569
+ if (node.type === 'arrow_function' || node.type === 'function_expression') return true
1570
+ return false
1571
+ })
1572
+ }
1573
+ return uses
1574
+ }
1575
+
1576
+ // Default: collect from entire sink expression + CFG node uses
1577
+ const uses = new Set<string>(cfgNode.uses)
1578
+
1579
+ walkAST(sink.node, (node) => {
1580
+ if (node.type === 'identifier') {
1581
+ uses.add(node.text)
1582
+ }
1583
+ if (node.type === 'arrow_function' || node.type === 'function_expression') return true
1584
+ return false
1585
+ })
1586
+
1587
+ return uses
1588
+ }
1589
+
1590
+ /**
1591
+ * Extract one-level field accesses from a sink expression.
1592
+ * Returns a map of root variable → field name for `obj.field` patterns.
1593
+ * Used for field-sensitive sink checking.
1594
+ */
1595
+ function extractSinkFieldAccesses(sink: TaintSink): Map<string, string> {
1596
+ const accesses = new Map<string, string>()
1597
+
1598
+ // Walk the sink's arguments to find member expression patterns
1599
+ walkAST(sink.node, (node) => {
1600
+ if (node.type === 'member_expression') {
1601
+ const obj = node.childForFieldName('object')
1602
+ const prop = node.childForFieldName('property')
1603
+ if (obj?.type === 'identifier' && prop?.type === 'property_identifier') {
1604
+ accesses.set(obj.text, prop.text)
1605
+ }
1606
+ }
1607
+ if (node.type === 'arrow_function' || node.type === 'function_expression') return true
1608
+ return false
1609
+ })
1610
+
1611
+ return accesses
1612
+ }
1613
+
1614
+ // ============================================================================
1615
+ // Path Reconstruction
1616
+ // ============================================================================
1617
+
1618
+ /**
1619
+ * Reconstruct the path from source to sink via backward slice.
1620
+ */
1621
+ function reconstructPath(
1622
+ cfg: CFG,
1623
+ outStates: Map<number, TaintState>,
1624
+ source: TaintSource,
1625
+ sink: TaintSink,
1626
+ sinkVar: string,
1627
+ sourceMappings: SourceMapping[],
1628
+ sinkNodeId: number,
1629
+ ): TaintStep[] {
1630
+ const steps: TaintStep[] = []
1631
+
1632
+ // Step 1: Sink step
1633
+ const sinkNode = cfg.nodes.get(sinkNodeId)
1634
+ if (sinkNode) {
1635
+ steps.unshift({
1636
+ nodeId: sinkNodeId,
1637
+ line: sink.line,
1638
+ variable: sinkVar,
1639
+ taintKinds: new Set(sink.vulnerableToKinds),
1640
+ description: `SINK ${sink.expression.slice(0, 60)}`,
1641
+ stepType: 'sink',
1642
+ })
1643
+ }
1644
+
1645
+ // Step 2: Walk backward from sink to source
1646
+ const visited = new Set<number>()
1647
+ let currentVar = sinkVar
1648
+ let currentNodeId = sinkNodeId
1649
+
1650
+ for (let depth = 0; depth < 50; depth++) {
1651
+ visited.add(currentNodeId)
1652
+
1653
+ // Check if we've reached a source node
1654
+ const sourceMapping = sourceMappings.find(
1655
+ sm => sm.source === source && sm.cfgNodeId === currentNodeId
1656
+ )
1657
+ if (sourceMapping) break
1658
+
1659
+ // Find a predecessor that has taint for the current variable
1660
+ const predIds = getPredecessors(cfg, currentNodeId)
1661
+ let foundPred = false
1662
+
1663
+ for (const predId of predIds) {
1664
+ if (visited.has(predId)) continue
1665
+
1666
+ const predState = outStates.get(predId)
1667
+ if (!predState) continue
1668
+
1669
+ // Check if this predecessor contributes taint for our variable
1670
+ const predTaint = getTaint(predState, currentVar)
1671
+ if (!predTaint || predTaint.size === 0) continue
1672
+
1673
+ const predNode = cfg.nodes.get(predId)
1674
+ if (!predNode) continue
1675
+
1676
+ // Check if this node defines the variable (it's a propagation step)
1677
+ if (predNode.defs.has(currentVar)) {
1678
+ steps.unshift({
1679
+ nodeId: predId,
1680
+ line: predNode.line,
1681
+ variable: currentVar,
1682
+ taintKinds: new Set(predTaint),
1683
+ description: `ASSIGN ${predNode.label}`,
1684
+ stepType: 'propagation',
1685
+ })
1686
+
1687
+ // Check what variable(s) contributed to this def
1688
+ // Look at the node's uses that are tainted
1689
+ for (const use of predNode.uses) {
1690
+ const useTaint = getTaint(predState, use) // check predecessor's state
1691
+ if (!useTaint || useTaint.size === 0) {
1692
+ // Also check in-state (merge of pred's preds)
1693
+ const predPredIds = getPredecessors(cfg, predId)
1694
+ for (const ppId of predPredIds) {
1695
+ const ppState = outStates.get(ppId)
1696
+ const ppTaint = ppState ? getTaint(ppState, use) : undefined
1697
+ if (ppTaint && ppTaint.size > 0) {
1698
+ currentVar = use
1699
+ break
1700
+ }
1701
+ }
1702
+ if (currentVar !== use) continue
1703
+ } else {
1704
+ currentVar = use
1705
+ }
1706
+ break
1707
+ }
1708
+ }
1709
+
1710
+ currentNodeId = predId
1711
+ foundPred = true
1712
+ break
1713
+ }
1714
+
1715
+ if (!foundPred) break
1716
+ }
1717
+
1718
+ // Step 3: Source step
1719
+ steps.unshift({
1720
+ nodeId: sourceMappings.find(sm => sm.source === source)?.cfgNodeId ?? 0,
1721
+ line: source.line,
1722
+ variable: source.variable,
1723
+ taintKinds: new Set(source.taintKinds),
1724
+ description: `SOURCE ${source.expression.slice(0, 60)}`,
1725
+ stepType: 'source',
1726
+ })
1727
+
1728
+ return steps
1729
+ }
1730
+
1731
+ // ============================================================================
1732
+ // Deduplication
1733
+ // ============================================================================
1734
+
1735
+ /**
1736
+ * Deduplicate findings: same source line + same sink line + same kinds = one finding.
1737
+ */
1738
+ function deduplicateFindings(findings: TaintFinding[]): TaintFinding[] {
1739
+ const seen = new Set<string>()
1740
+ const result: TaintFinding[] = []
1741
+
1742
+ for (const f of findings) {
1743
+ const key = `${f.source.line}:${f.sink.line}:${[...f.matchingKinds].sort().join(',')}`
1744
+ if (seen.has(key)) continue
1745
+ seen.add(key)
1746
+ result.push(f)
1747
+ }
1748
+
1749
+ return result
1750
+ }