gitnexus 1.6.8-rc.8 → 1.6.8

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 (439) hide show
  1. package/README.md +3 -3
  2. package/dist/_shared/graph/types.d.ts +31 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/language-detection.d.ts.map +1 -1
  5. package/dist/_shared/language-detection.js +11 -1
  6. package/dist/_shared/language-detection.js.map +1 -1
  7. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  8. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  9. package/dist/_shared/lbug/schema-constants.js +6 -0
  10. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  11. package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
  12. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
  13. package/dist/_shared/scope-resolution/symbol-definition.d.ts +6 -0
  14. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
  15. package/dist/cli/ai-context.d.ts +31 -1
  16. package/dist/cli/ai-context.js +18 -16
  17. package/dist/cli/analyze-config.js +11 -0
  18. package/dist/cli/analyze.d.ts +14 -0
  19. package/dist/cli/analyze.js +98 -0
  20. package/dist/cli/embedding-dims.d.ts +33 -0
  21. package/dist/cli/embedding-dims.js +39 -0
  22. package/dist/cli/eval-server.d.ts +8 -0
  23. package/dist/cli/eval-server.js +242 -2
  24. package/dist/cli/help-i18n.js +19 -0
  25. package/dist/cli/i18n/en.d.ts +15 -1
  26. package/dist/cli/i18n/en.js +15 -1
  27. package/dist/cli/i18n/resources.d.ts +29 -1
  28. package/dist/cli/i18n/zh-CN.d.ts +14 -0
  29. package/dist/cli/i18n/zh-CN.js +15 -1
  30. package/dist/cli/index.js +89 -1
  31. package/dist/cli/mcp.d.ts +6 -1
  32. package/dist/cli/mcp.js +26 -1
  33. package/dist/cli/optional-grammars.d.ts +6 -8
  34. package/dist/cli/optional-grammars.js +8 -11
  35. package/dist/cli/setup.d.ts +3 -1
  36. package/dist/cli/setup.js +67 -17
  37. package/dist/cli/skill-gen.js +2 -1
  38. package/dist/cli/tool.d.ts +19 -1
  39. package/dist/cli/tool.js +96 -3
  40. package/dist/core/embeddings/hf-env.d.ts +0 -66
  41. package/dist/core/embeddings/http-client.d.ts +7 -0
  42. package/dist/core/embeddings/http-client.js +5 -3
  43. package/dist/core/embeddings/server-mapping.d.ts +1 -1
  44. package/dist/core/embeddings/server-mapping.js +7 -3
  45. package/dist/core/graph/import-cycles.d.ts +10 -0
  46. package/dist/core/graph/import-cycles.js +103 -0
  47. package/dist/core/group/cross-impact.d.ts +2 -1
  48. package/dist/core/group/cross-impact.js +5 -1
  49. package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
  50. package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
  51. package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
  52. package/dist/core/group/extractors/http-route-extractor.js +22 -4
  53. package/dist/core/group/extractors/include-extractor.js +22 -13
  54. package/dist/core/group/sync.d.ts +27 -1
  55. package/dist/core/group/sync.js +0 -0
  56. package/dist/core/incremental/subgraph-extract.js +18 -1
  57. package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
  58. package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
  59. package/dist/core/ingestion/cfg/collect.d.ts +51 -0
  60. package/dist/core/ingestion/cfg/collect.js +77 -0
  61. package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
  62. package/dist/core/ingestion/cfg/control-dependence.js +182 -0
  63. package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
  64. package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
  65. package/dist/core/ingestion/cfg/emit.d.ts +293 -0
  66. package/dist/core/ingestion/cfg/emit.js +646 -0
  67. package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
  68. package/dist/core/ingestion/cfg/post-dominators.js +176 -0
  69. package/dist/core/ingestion/cfg/reaching-def-reason-codec.d.ts +106 -0
  70. package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
  71. package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
  72. package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
  73. package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
  74. package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
  75. package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
  76. package/dist/core/ingestion/cfg/synthetic-escape.js +300 -0
  77. package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
  78. package/dist/core/ingestion/cfg/traversal-result.js +2 -0
  79. package/dist/core/ingestion/cfg/types.d.ts +274 -0
  80. package/dist/core/ingestion/cfg/types.js +13 -0
  81. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
  82. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
  83. package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
  84. package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
  85. package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
  86. package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
  87. package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
  88. package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
  89. package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
  90. package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
  91. package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
  92. package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
  93. package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
  94. package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
  95. package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
  96. package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
  97. package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
  98. package/dist/core/ingestion/cfg/visitors/go.js +638 -0
  99. package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
  100. package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
  101. package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
  102. package/dist/core/ingestion/cfg/visitors/java.js +812 -0
  103. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
  104. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
  105. package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
  106. package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
  107. package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
  108. package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
  109. package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
  110. package/dist/core/ingestion/cfg/visitors/php.js +721 -0
  111. package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
  112. package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
  113. package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
  114. package/dist/core/ingestion/cfg/visitors/python.js +558 -0
  115. package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
  116. package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
  117. package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
  118. package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
  119. package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
  120. package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
  121. package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
  122. package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
  123. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
  124. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
  125. package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
  126. package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
  127. package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
  128. package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
  129. package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
  130. package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
  131. package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
  132. package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
  133. package/dist/core/ingestion/import-resolvers/utils.js +2 -0
  134. package/dist/core/ingestion/language-provider.d.ts +23 -2
  135. package/dist/core/ingestion/languages/c-cpp.js +17 -5
  136. package/dist/core/ingestion/languages/cobol.js +4 -0
  137. package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
  138. package/dist/core/ingestion/languages/cpp/captures.js +50 -1
  139. package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
  140. package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
  141. package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
  142. package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
  143. package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
  144. package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
  145. package/dist/core/ingestion/languages/cpp/query.js +23 -0
  146. package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
  147. package/dist/core/ingestion/languages/csharp.js +2 -0
  148. package/dist/core/ingestion/languages/dart.js +2 -0
  149. package/dist/core/ingestion/languages/go.js +2 -0
  150. package/dist/core/ingestion/languages/java.js +3 -0
  151. package/dist/core/ingestion/languages/kotlin/query.js +3 -2
  152. package/dist/core/ingestion/languages/kotlin.js +8 -1
  153. package/dist/core/ingestion/languages/php.js +2 -0
  154. package/dist/core/ingestion/languages/python.js +2 -0
  155. package/dist/core/ingestion/languages/ruby.js +2 -0
  156. package/dist/core/ingestion/languages/rust.js +2 -0
  157. package/dist/core/ingestion/languages/swift.js +2 -0
  158. package/dist/core/ingestion/languages/typescript.js +5 -0
  159. package/dist/core/ingestion/languages/vue.js +6 -0
  160. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
  161. package/dist/core/ingestion/method-extractors/generic.js +1 -0
  162. package/dist/core/ingestion/method-types.d.ts +2 -0
  163. package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
  164. package/dist/core/ingestion/model/symbol-table.js +1 -0
  165. package/dist/core/ingestion/mro-processor.js +109 -0
  166. package/dist/core/ingestion/parsing-processor.js +44 -0
  167. package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
  168. package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
  169. package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
  170. package/dist/core/ingestion/pipeline-phases/index.js +2 -0
  171. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
  172. package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
  173. package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
  174. package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
  175. package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
  176. package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
  177. package/dist/core/ingestion/pipeline.d.ts +92 -10
  178. package/dist/core/ingestion/pipeline.js +11 -2
  179. package/dist/core/ingestion/scope-extractor.js +12 -2
  180. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
  181. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
  182. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
  183. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
  184. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
  185. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
  186. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
  187. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
  188. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
  189. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
  190. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
  191. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
  192. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
  193. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
  194. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
  195. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
  196. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
  197. package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
  198. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
  199. package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
  200. package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
  201. package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
  202. package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
  203. package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
  204. package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
  205. package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
  206. package/dist/core/ingestion/taint/call-summary-model.js +25 -0
  207. package/dist/core/ingestion/taint/emit.d.ts +125 -0
  208. package/dist/core/ingestion/taint/emit.js +208 -0
  209. package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
  210. package/dist/core/ingestion/taint/interproc-emit.js +88 -0
  211. package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
  212. package/dist/core/ingestion/taint/interproc-solver.js +312 -0
  213. package/dist/core/ingestion/taint/match.d.ts +153 -0
  214. package/dist/core/ingestion/taint/match.js +278 -0
  215. package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
  216. package/dist/core/ingestion/taint/path-codec.js +190 -0
  217. package/dist/core/ingestion/taint/propagate.d.ts +216 -0
  218. package/dist/core/ingestion/taint/propagate.js +659 -0
  219. package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
  220. package/dist/core/ingestion/taint/site-safety.js +98 -0
  221. package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
  222. package/dist/core/ingestion/taint/source-sink-config.js +35 -12
  223. package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
  224. package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
  225. package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +80 -0
  226. package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
  227. package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
  228. package/dist/core/ingestion/taint/summary-harvest.js +537 -0
  229. package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
  230. package/dist/core/ingestion/taint/summary-model.js +86 -0
  231. package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
  232. package/dist/core/ingestion/taint/typescript-model.js +102 -0
  233. package/dist/core/ingestion/type-extractors/c-cpp.js +21 -0
  234. package/dist/core/ingestion/utils/env.d.ts +7 -0
  235. package/dist/core/ingestion/utils/env.js +12 -0
  236. package/dist/core/ingestion/utils/method-props.js +1 -0
  237. package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
  238. package/dist/core/ingestion/workers/clone-safety.js +465 -0
  239. package/dist/core/ingestion/workers/parse-worker.d.ts +23 -0
  240. package/dist/core/ingestion/workers/parse-worker.js +81 -53
  241. package/dist/core/ingestion/workers/post-result.d.ts +22 -0
  242. package/dist/core/ingestion/workers/post-result.js +87 -0
  243. package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
  244. package/dist/core/ingestion/workers/result-merge.js +43 -0
  245. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -40
  246. package/dist/core/ingestion/workers/worker-pool.js +43 -16
  247. package/dist/core/lbug/csv-generator.d.ts +51 -3
  248. package/dist/core/lbug/csv-generator.js +369 -255
  249. package/dist/core/lbug/lbug-adapter.d.ts +58 -18
  250. package/dist/core/lbug/lbug-adapter.js +346 -84
  251. package/dist/core/lbug/lbug-config.d.ts +19 -0
  252. package/dist/core/lbug/lbug-config.js +29 -0
  253. package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
  254. package/dist/core/lbug/pdg-emit-sink.js +358 -0
  255. package/dist/core/lbug/pool-adapter.d.ts +35 -0
  256. package/dist/core/lbug/pool-adapter.js +90 -1
  257. package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
  258. package/dist/core/lbug/rel-pair-routing.js +140 -0
  259. package/dist/core/lbug/schema.d.ts +2 -2
  260. package/dist/core/lbug/schema.js +3 -0
  261. package/dist/core/logger.d.ts +0 -5
  262. package/dist/core/run-analyze.d.ts +96 -0
  263. package/dist/core/run-analyze.js +207 -3
  264. package/dist/core/tree-sitter/parser-loader.js +5 -4
  265. package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
  266. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  267. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  268. package/dist/core/wiki/generator.js +3 -1
  269. package/dist/core/wiki/graph-queries.d.ts +6 -0
  270. package/dist/core/wiki/graph-queries.js +9 -1
  271. package/dist/mcp/http-transport.d.ts +134 -0
  272. package/dist/mcp/http-transport.js +503 -0
  273. package/dist/mcp/local/local-backend.d.ts +110 -24
  274. package/dist/mcp/local/local-backend.js +1225 -19
  275. package/dist/mcp/local/pdg-impact.d.ts +384 -0
  276. package/dist/mcp/local/pdg-impact.js +1972 -0
  277. package/dist/mcp/resources.js +13 -1
  278. package/dist/mcp/tools.d.ts +12 -0
  279. package/dist/mcp/tools.js +214 -7
  280. package/dist/server/analyze-launch.d.ts +29 -0
  281. package/dist/server/analyze-launch.js +137 -0
  282. package/dist/server/analyze-upload.d.ts +33 -0
  283. package/dist/server/analyze-upload.js +123 -0
  284. package/dist/server/analyze-worker-ipc.d.ts +58 -0
  285. package/dist/server/analyze-worker-ipc.js +16 -0
  286. package/dist/server/analyze-worker.d.ts +17 -1
  287. package/dist/server/analyze-worker.js +7 -1
  288. package/dist/server/api.d.ts +17 -0
  289. package/dist/server/api.js +132 -168
  290. package/dist/server/git-clone.d.ts +43 -2
  291. package/dist/server/git-clone.js +211 -16
  292. package/dist/server/mcp-http.d.ts +5 -6
  293. package/dist/server/mcp-http.js +9 -82
  294. package/dist/server/middleware.d.ts +40 -0
  295. package/dist/server/middleware.js +92 -0
  296. package/dist/server/private-ip.d.ts +1 -0
  297. package/dist/server/private-ip.js +15 -0
  298. package/dist/server/upload-ingest.d.ts +56 -0
  299. package/dist/server/upload-ingest.js +276 -0
  300. package/dist/server/upload-paths.d.ts +37 -0
  301. package/dist/server/upload-paths.js +57 -0
  302. package/dist/server/upload-sweep.d.ts +20 -0
  303. package/dist/server/upload-sweep.js +57 -0
  304. package/dist/storage/git.js +5 -8
  305. package/dist/storage/parse-cache.d.ts +22 -1
  306. package/dist/storage/parse-cache.js +44 -10
  307. package/dist/storage/repo-manager.d.ts +121 -10
  308. package/dist/storage/repo-manager.js +43 -5
  309. package/dist/types/pipeline.d.ts +9 -0
  310. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
  311. package/hooks/claude/gitnexus-hook.cjs +110 -13
  312. package/hooks/claude/hook-db-lock-probe.cjs +513 -26
  313. package/package.json +8 -2
  314. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  315. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  316. package/scripts/cross-platform-tests.ts +1 -0
  317. package/skills/gitnexus-debugging.md +16 -4
  318. package/skills/gitnexus-exploring.md +3 -3
  319. package/skills/gitnexus-guide.md +33 -0
  320. package/skills/gitnexus-pdg-query.md +89 -0
  321. package/skills/gitnexus-refactoring.md +1 -1
  322. package/skills/gitnexus-taint-analysis.md +178 -0
  323. package/vendor/tree-sitter-c/package.json +1 -1
  324. package/vendor/tree-sitter-dart/package.json +1 -1
  325. package/vendor/tree-sitter-kotlin/package.json +1 -1
  326. package/vendor/tree-sitter-proto/package.json +1 -1
  327. package/vendor/tree-sitter-swift/package.json +1 -1
  328. package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
  329. package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
  330. package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
  331. package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
  332. package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
  333. package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
  334. package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
  335. package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
  336. package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
  337. package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
  338. package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
  339. package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
  340. package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
  341. package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
  342. package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
  343. package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
  344. package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
  345. package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
  346. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
  347. package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
  348. package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
  349. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
  350. package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
  351. package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
  352. package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
  353. package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
  354. package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
  355. package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
  356. package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
  357. package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
  358. package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
  359. package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
  360. package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
  361. package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
  362. package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
  363. package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
  364. package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
  365. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
  366. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
  367. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
  368. package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
  369. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
  370. package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
  371. package/web/assets/index-BvYev1UJ.js +635 -0
  372. package/web/assets/index-_lgn7hs5.css +2 -0
  373. package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
  374. package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
  375. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
  376. package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
  377. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
  378. package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
  379. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
  380. package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
  381. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
  382. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
  383. package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
  384. package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
  385. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
  386. package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
  387. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
  388. package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
  389. package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
  390. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
  391. package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
  392. package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
  393. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
  394. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
  395. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
  396. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
  397. package/web/index.html +13 -15
  398. package/scripts/materialize-vendor-grammars.cjs +0 -97
  399. package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
  400. package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
  401. package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
  402. package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
  403. package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
  404. package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
  405. package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
  406. package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
  407. package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
  408. package/web/assets/chunk-CilyBKbf.js +0 -1
  409. package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
  410. package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
  411. package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
  412. package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
  413. package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
  414. package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
  415. package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
  416. package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
  417. package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
  418. package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
  419. package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
  420. package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
  421. package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
  422. package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
  423. package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
  424. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
  425. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
  426. package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
  427. package/web/assets/index-BKWA-m7o.css +0 -2
  428. package/web/assets/index-CG6q8eTs.js +0 -626
  429. package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
  430. package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
  431. package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
  432. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
  433. package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
  434. package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
  435. package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
  436. package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
  437. package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
  438. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
  439. /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Per-function taint SUMMARY harvest (#2084 M4 U1).
3
+ *
4
+ * Pure, deterministic derivation of one function's {@link FunctionSummary}
5
+ * facts from the SAME substrate the M3 intra-procedural pass consumes — the M2
6
+ * reaching-definition facts (`computeReachingDefs`) and the matched taint sites
7
+ * (`matchFunctionSites`). No graph, no I/O, no logger; mirrors the
8
+ * `computeReachingDefs` / `computeTaintFlows` contract (insertion-ordered
9
+ * worklist, explicitly sorted outputs) so snapshot tests and the version stamp
10
+ * stay stable. Runs IN-PHASE inside the scope-resolution pdg window where the
11
+ * CFG side channel is live (plan KTD1); the cross-function fixpoint that
12
+ * COMPOSES these summaries runs afterward over the complete call graph.
13
+ *
14
+ * ## What a summary captures (whole-parameter granularity)
15
+ *
16
+ * Seeding each formal parameter as taint and running forward reachability over
17
+ * the def→use facts yields four edge categories:
18
+ *
19
+ * - **param→return** — a param's value reaches a `return <expr>`. Return
20
+ * statements are identified structurally: the SOURCE block of every CFG edge
21
+ * of kind `return` terminates in the return jump (the M2 edge-kind
22
+ * invariant), so its last statement's `uses` are the returned bindings.
23
+ * - **param→callee-arg** — a param occurrence lands in argument position
24
+ * `argIndex` of a call at `callLine`. The fixpoint resolves `callLine` to a
25
+ * callee via the caller's `CALLS` edges and applies the callee's summary
26
+ * (TITO composition).
27
+ * - **param→sink** — a param reaches a modelled sink position (the partial
28
+ * flow that a cross-function source completes).
29
+ * - **source→return** — a modelled source read (`req.body`) reaches the return
30
+ * (a generative summary: calling the function yields tainted data).
31
+ *
32
+ * ## Soundness model (context-insensitive first cut)
33
+ *
34
+ * Onward propagation uses the M3 STATEMENT-LEVEL precision floor: a statement
35
+ * that uses a tainted binding taints all of its defs (and `mayDefs`). This is
36
+ * the same sound over-approximation M3 documents — it may over-taint
37
+ * (multi-declarator conflation) but never drops a real flow. Sanitizer
38
+ * `resultDefs` narrow the EXCLUSION set (a def produced by a matched sanitizer
39
+ * carries that sanitizer's neutralised `SinkKind`s), so a sanitised value does
40
+ * not trigger a downstream sink of the neutralised kind — the kind-set
41
+ * exclusion model, simplified to the result-def channel (occurrence
42
+ * interposition, field paths, and callbacks are deferred — plan KTD).
43
+ *
44
+ * The summary edges themselves (return / call-arg / sink) are recorded from
45
+ * ACTUAL binding occurrences (a tainted binding present in a return's uses, a
46
+ * call's arg list, or a matched sink position), never the floor — the floor
47
+ * governs only onward def-tainting, keeping the recorded edges precise.
48
+ *
49
+ * ## Known limitation — destructured / rest params (documented FN)
50
+ *
51
+ * Param indices are assigned by ORDINAL over the flattened param-binding list,
52
+ * which equals the FORMAL parameter position only when every param is a simple
53
+ * identifier. A destructured or rest param contributes several bindings (or
54
+ * shifts the count), so a simple param positioned AFTER one
55
+ * (`function f([a, b], x) { sink(x) }`) gets a summary port index that does not
56
+ * match the formal argument position the interprocedural solver joins against
57
+ * — a cross-function false negative for that function. The precise fix needs a
58
+ * formal-param index threaded from the worker harvest (`BindingEntry`), a
59
+ * cache-namespace-affecting change deferred with the other documented FN
60
+ * classes (closures, fields — see the taint skill). Functions with all-simple
61
+ * params (the common case) are unaffected.
62
+ */
63
+ import type { FunctionCfg } from '../cfg/types.js';
64
+ import { type FunctionDefUse } from '../cfg/reaching-defs.js';
65
+ import type { FunctionSiteMatches } from './match.js';
66
+ import type { CallResult, ParamToCallArg, ParamToReturn, ParamToSink, SourceToCallArg, SourceToReturn } from './summary-model.js';
67
+ /** The own-facts portion of a summary (fnId/version are added by the caller). */
68
+ export interface HarvestedSummaryFacts {
69
+ readonly paramCount: number;
70
+ readonly paramToReturn: readonly ParamToReturn[];
71
+ readonly paramToCallArg: readonly ParamToCallArg[];
72
+ readonly paramToSink: readonly ParamToSink[];
73
+ readonly sourceToReturn: readonly SourceToReturn[];
74
+ readonly sourceToCallArg: readonly SourceToCallArg[];
75
+ readonly callResults: readonly CallResult[];
76
+ }
77
+ export interface HarvestResult {
78
+ /** `computed` — facts derived; `coverage-gap` — the RD solver was not
79
+ * `computed`, so no summary is produced (consistent with M3 R4). */
80
+ readonly status: 'computed' | 'coverage-gap';
81
+ readonly gapReason?: FunctionDefUse['status'];
82
+ readonly facts: HarvestedSummaryFacts;
83
+ }
84
+ /**
85
+ * Harvest the summary facts for one function. PRECONDITION: `cfg` is
86
+ * `isEmitSafeCfg`-filtered and `defUse` was computed from it; sites are assumed
87
+ * `hasTaintSafeSites`-valid (the caller gates exactly as the M3 emit path does).
88
+ */
89
+ export declare function harvestFunctionSummary(cfg: FunctionCfg, defUse: FunctionDefUse, matches: FunctionSiteMatches): HarvestResult;
@@ -0,0 +1,537 @@
1
+ /**
2
+ * Per-function taint SUMMARY harvest (#2084 M4 U1).
3
+ *
4
+ * Pure, deterministic derivation of one function's {@link FunctionSummary}
5
+ * facts from the SAME substrate the M3 intra-procedural pass consumes — the M2
6
+ * reaching-definition facts (`computeReachingDefs`) and the matched taint sites
7
+ * (`matchFunctionSites`). No graph, no I/O, no logger; mirrors the
8
+ * `computeReachingDefs` / `computeTaintFlows` contract (insertion-ordered
9
+ * worklist, explicitly sorted outputs) so snapshot tests and the version stamp
10
+ * stay stable. Runs IN-PHASE inside the scope-resolution pdg window where the
11
+ * CFG side channel is live (plan KTD1); the cross-function fixpoint that
12
+ * COMPOSES these summaries runs afterward over the complete call graph.
13
+ *
14
+ * ## What a summary captures (whole-parameter granularity)
15
+ *
16
+ * Seeding each formal parameter as taint and running forward reachability over
17
+ * the def→use facts yields four edge categories:
18
+ *
19
+ * - **param→return** — a param's value reaches a `return <expr>`. Return
20
+ * statements are identified structurally: the SOURCE block of every CFG edge
21
+ * of kind `return` terminates in the return jump (the M2 edge-kind
22
+ * invariant), so its last statement's `uses` are the returned bindings.
23
+ * - **param→callee-arg** — a param occurrence lands in argument position
24
+ * `argIndex` of a call at `callLine`. The fixpoint resolves `callLine` to a
25
+ * callee via the caller's `CALLS` edges and applies the callee's summary
26
+ * (TITO composition).
27
+ * - **param→sink** — a param reaches a modelled sink position (the partial
28
+ * flow that a cross-function source completes).
29
+ * - **source→return** — a modelled source read (`req.body`) reaches the return
30
+ * (a generative summary: calling the function yields tainted data).
31
+ *
32
+ * ## Soundness model (context-insensitive first cut)
33
+ *
34
+ * Onward propagation uses the M3 STATEMENT-LEVEL precision floor: a statement
35
+ * that uses a tainted binding taints all of its defs (and `mayDefs`). This is
36
+ * the same sound over-approximation M3 documents — it may over-taint
37
+ * (multi-declarator conflation) but never drops a real flow. Sanitizer
38
+ * `resultDefs` narrow the EXCLUSION set (a def produced by a matched sanitizer
39
+ * carries that sanitizer's neutralised `SinkKind`s), so a sanitised value does
40
+ * not trigger a downstream sink of the neutralised kind — the kind-set
41
+ * exclusion model, simplified to the result-def channel (occurrence
42
+ * interposition, field paths, and callbacks are deferred — plan KTD).
43
+ *
44
+ * The summary edges themselves (return / call-arg / sink) are recorded from
45
+ * ACTUAL binding occurrences (a tainted binding present in a return's uses, a
46
+ * call's arg list, or a matched sink position), never the floor — the floor
47
+ * governs only onward def-tainting, keeping the recorded edges precise.
48
+ *
49
+ * ## Known limitation — destructured / rest params (documented FN)
50
+ *
51
+ * Param indices are assigned by ORDINAL over the flattened param-binding list,
52
+ * which equals the FORMAL parameter position only when every param is a simple
53
+ * identifier. A destructured or rest param contributes several bindings (or
54
+ * shifts the count), so a simple param positioned AFTER one
55
+ * (`function f([a, b], x) { sink(x) }`) gets a summary port index that does not
56
+ * match the formal argument position the interprocedural solver joins against
57
+ * — a cross-function false negative for that function. The precise fix needs a
58
+ * formal-param index threaded from the worker harvest (`BindingEntry`), a
59
+ * cache-namespace-affecting change deferred with the other documented FN
60
+ * classes (closures, fields — see the taint skill). Functions with all-simple
61
+ * params (the common case) are unaffected.
62
+ */
63
+ import { pointKey } from '../cfg/reaching-defs.js';
64
+ import { sinkKindRank, sortSinkKinds } from './source-sink-config.js';
65
+ const EMPTY_FACTS = {
66
+ paramCount: 0,
67
+ paramToReturn: [],
68
+ paramToCallArg: [],
69
+ paramToSink: [],
70
+ sourceToReturn: [],
71
+ sourceToCallArg: [],
72
+ callResults: [],
73
+ };
74
+ /** Last segment of a dotted callee path (`child_process.exec` ⇒ `exec`). */
75
+ const calleeTail = (callee) => callee === undefined ? undefined : (callee.split('.').pop() ?? callee);
76
+ /**
77
+ * Harvest the summary facts for one function. PRECONDITION: `cfg` is
78
+ * `isEmitSafeCfg`-filtered and `defUse` was computed from it; sites are assumed
79
+ * `hasTaintSafeSites`-valid (the caller gates exactly as the M3 emit path does).
80
+ */
81
+ export function harvestFunctionSummary(cfg, defUse, matches) {
82
+ if (defUse.status !== 'computed') {
83
+ return { status: 'coverage-gap', gapReason: defUse.status, facts: EMPTY_FACTS };
84
+ }
85
+ const bindings = defUse.bindings;
86
+ // ── param bindings → param index (declaration order) ──────────────────────
87
+ // `kind:'param'` bindings, ordered by declaration site (declLine/declColumn).
88
+ const paramBindings = bindings
89
+ .map((b, idx) => ({ b, idx }))
90
+ .filter((e) => e.b.kind === 'param')
91
+ .sort((a, b) => a.b.declLine - b.b.declLine || a.b.declColumn - b.b.declColumn);
92
+ const paramIndexOf = new Map();
93
+ paramBindings.forEach((e, paramIdx) => paramIndexOf.set(e.idx, paramIdx));
94
+ const paramCount = paramBindings.length;
95
+ // ── return points: source block of every `return` CFG edge ────────────────
96
+ // The M2 edge-kind invariant: a `return` edge's SOURCE block terminates in
97
+ // the return jump, so its LAST statement is the `return <expr>` — its `uses`
98
+ // are the returned bindings. (`return;` with no value has empty uses.)
99
+ const returnUseStmtKeys = new Set();
100
+ for (const e of cfg.edges) {
101
+ if (e.kind !== 'return')
102
+ continue;
103
+ const block = cfg.blocks[e.from];
104
+ const stmts = block?.statements;
105
+ if (!stmts || stmts.length === 0)
106
+ continue;
107
+ returnUseStmtKeys.add(`${e.from}:${stmts.length - 1}`);
108
+ }
109
+ // ── per-statement match context (sink/source/sanitizer by site) ───────────
110
+ const sinkPosBySite = new Map(); // stmtKey → site → argPositions
111
+ const sinkKindByEntry = new Map(); // stmtKey → site → kinds at any pos
112
+ const sanitizerResultDefKinds = new Map(); // stmtKey → resultDef binding → kinds
113
+ // Matched sink/sanitizer sites (`stmtKey:siteIndex`) — EXCLUDED from the
114
+ // call-result seed (#2084 review P1-1): their result semantics are already
115
+ // modelled (a sanitizer's result rides U2 exclusions; a sink returns void).
116
+ const modeledSites = new Set();
117
+ for (const sm of matches.statements) {
118
+ const stmtKey = `${sm.blockIndex}:${sm.statementIndex}`;
119
+ for (const s of sm.sinks)
120
+ modeledSites.add(`${stmtKey}:${s.siteIndex}`);
121
+ for (const s of sm.sanitizers)
122
+ modeledSites.add(`${stmtKey}:${s.siteIndex}`);
123
+ if (sm.sinks.length > 0) {
124
+ const bySite = new Map();
125
+ const kindBySite = new Map();
126
+ for (const sink of sm.sinks) {
127
+ const pos = bySite.get(sink.siteIndex) ?? new Set();
128
+ for (const p of sink.argPositions)
129
+ pos.add(p);
130
+ bySite.set(sink.siteIndex, pos);
131
+ const ks = kindBySite.get(sink.siteIndex) ?? [];
132
+ ks.push(sink.entry.kind);
133
+ kindBySite.set(sink.siteIndex, ks);
134
+ }
135
+ sinkPosBySite.set(stmtKey, bySite);
136
+ sinkKindByEntry.set(stmtKey, kindBySite);
137
+ }
138
+ if (sm.sanitizers.length > 0) {
139
+ const byDef = new Map();
140
+ for (const san of sm.sanitizers) {
141
+ for (const d of san.resultDefs) {
142
+ const ks = byDef.get(d) ?? [];
143
+ ks.push(...san.entry.neutralizes);
144
+ byDef.set(d, ks);
145
+ }
146
+ }
147
+ sanitizerResultDefKinds.set(stmtKey, byDef);
148
+ }
149
+ }
150
+ const stmtAt = (p) => cfg.blocks[p.blockIndex]?.statements?.[p.stmtIndex];
151
+ // ── def→use index ─────────────────────────────────────────────────────────
152
+ const factsByDef = new Map();
153
+ for (const f of defUse.facts) {
154
+ const key = `${f.bindingIdx}:${pointKey(f.def)}`;
155
+ const list = factsByDef.get(key);
156
+ const entry = { bindingIdx: f.bindingIdx, use: f.use };
157
+ if (list)
158
+ list.push(entry);
159
+ else
160
+ factsByDef.set(key, [entry]);
161
+ }
162
+ // ── accumulators (deduped by string identity) ─────────────────────────────
163
+ const paramReturn = new Map(); // param → neutralized intersection
164
+ const paramReturnSeen = new Set();
165
+ const paramCallArg = new Map();
166
+ const sourceCallArg = new Map();
167
+ // Intersection-over-paths of the neutralized kinds reaching each call-arg
168
+ // edge (#2084 review P1-2, deepening correction a). MUST intersect, not
169
+ // first-write-wins: a second, un-sanitized occurrence path to the same edge
170
+ // (`relay(x){ exec(x); exec(escape(x)); }`) shrinks the set to ∅ — mirror
171
+ // `recordReturn`. `*Seen` tracks first-write so the initial set is a copy.
172
+ const paramCallArgKinds = new Map();
173
+ const sourceCallArgKinds = new Map();
174
+ const intersectKinds = (store, key, incoming) => {
175
+ const cur = store.get(key);
176
+ if (cur === undefined)
177
+ store.set(key, new Set(incoming));
178
+ else
179
+ for (const k of [...cur])
180
+ if (!incoming.has(k))
181
+ cur.delete(k);
182
+ };
183
+ const paramSink = new Set();
184
+ const paramSinkOut = [];
185
+ const sourceReturn = new Set();
186
+ // Caller-side call-result flows (#2084 review P1-1), deduped by a structural key.
187
+ const callResults = new Map();
188
+ const recordCallResult = (cr) => {
189
+ const d = cr.dest;
190
+ const destKey = d.to === 'sink'
191
+ ? `sink:${d.sinkKind}`
192
+ : d.to === 'return'
193
+ ? 'return'
194
+ : `arg:${d.toCallee ?? ''}:${d.argIndex}`;
195
+ const key = `${cr.calleeName}|${destKey}`;
196
+ if (!callResults.has(key))
197
+ callResults.set(key, cr);
198
+ };
199
+ /** Record param→return, intersecting neutralized kinds across paths. */
200
+ const recordReturn = (param, exclusions) => {
201
+ if (!paramReturnSeen.has(param)) {
202
+ paramReturnSeen.add(param);
203
+ paramReturn.set(param, new Set(exclusions));
204
+ }
205
+ else {
206
+ const cur = paramReturn.get(param);
207
+ for (const k of [...cur])
208
+ if (!exclusions.has(k))
209
+ cur.delete(k);
210
+ }
211
+ };
212
+ // ── seeds: each param at its entry def point + each source statement ───────
213
+ // seedId 0..paramCount-1 = params; -1 = source.
214
+ const queue = [];
215
+ const visited = new Set();
216
+ const enqueue = (t) => {
217
+ // originCallee discriminates call-result seeds (all share seedId -2) so two
218
+ // distinct callees' results on the same binding are not collapsed.
219
+ const key = `${t.seedId}:${t.originCallee ?? ''}:${t.bindingIdx}:${pointKey(t.point)}:${[...t.exclusions].sort().join(',')}`;
220
+ if (visited.has(key))
221
+ return;
222
+ visited.add(key);
223
+ queue.push(t);
224
+ };
225
+ // Param seeds: find each param's def point(s) in the def→use facts (params are
226
+ // defined at ENTRY; any fact whose def-binding is the param and whose def
227
+ // sits in the entry block is a param-origin edge).
228
+ for (const { idx } of paramBindings) {
229
+ const paramIdx = paramIndexOf.get(idx);
230
+ // Seed at every def point of this param binding in the entry block.
231
+ for (const f of defUse.facts) {
232
+ if (f.bindingIdx === idx && f.def.blockIndex === cfg.entryIndex) {
233
+ enqueue({ bindingIdx: idx, point: f.def, seedId: paramIdx, exclusions: new Set() });
234
+ }
235
+ }
236
+ }
237
+ // Source seeds: a statement with a matched source taints its own defs; a bare
238
+ // `return <source>` is a direct source→return. The source's value rides the
239
+ // statement's defs (resultDefs of the assignment) under the floor.
240
+ for (const sm of matches.statements) {
241
+ if (sm.sources.length === 0)
242
+ continue;
243
+ const stmtKey = `${sm.blockIndex}:${sm.statementIndex}`;
244
+ const facts = cfg.blocks[sm.blockIndex]?.statements?.[sm.statementIndex];
245
+ if (!facts)
246
+ continue;
247
+ const point = {
248
+ blockIndex: sm.blockIndex,
249
+ stmtIndex: sm.statementIndex,
250
+ line: facts.line,
251
+ };
252
+ if (returnUseStmtKeys.has(stmtKey)) {
253
+ for (const src of sm.sources)
254
+ sourceReturn.add(src.entry.kind);
255
+ }
256
+ for (const d of [...facts.defs, ...(facts.mayDefs ?? [])]) {
257
+ enqueue({ bindingIdx: d, point, seedId: -1, exclusions: new Set() });
258
+ }
259
+ // DIRECT source-in-call-arg (`runIt(req.body)`): no intermediate binding is
260
+ // defined, so the floor seed above records nothing. Climb the source
261
+ // member-read's `parent` chain — each enclosing call/new site is a
262
+ // `sourceToCallArg` (the cross-function fixpoint seed). A sink ancestor is
263
+ // M3's intra-procedural concern and harmless to also record here.
264
+ for (const src of sm.sources) {
265
+ let cur = facts.sites?.[src.siteIndex];
266
+ const guard = new Set([src.siteIndex]);
267
+ while (cur?.parent) {
268
+ const [siteIdx, argPos] = cur.parent;
269
+ if (guard.has(siteIdx))
270
+ break;
271
+ guard.add(siteIdx);
272
+ const ancestor = facts.sites?.[siteIdx];
273
+ if (!ancestor)
274
+ break;
275
+ if (ancestor.kind === 'call' || ancestor.kind === 'new') {
276
+ const tail = calleeTail(ancestor.callee);
277
+ const scKey = `${facts.line}:${argPos}:${tail ?? ''}`;
278
+ if (!sourceCallArg.has(scKey)) {
279
+ sourceCallArg.set(scKey, {
280
+ sourceKind: src.entry.kind,
281
+ callLine: facts.line,
282
+ argIndex: argPos,
283
+ ...(tail ? { calleeName: tail } : {}),
284
+ });
285
+ }
286
+ }
287
+ cur = ancestor;
288
+ }
289
+ }
290
+ }
291
+ // Call-result seeds (#2084 review P1-1): a call to a (potentially generative)
292
+ // USER function is a NEW taint origin — `matchFunctionSites` only sources
293
+ // member-reads, so the result of `getInput()` is invisible today. Seed every
294
+ // call/new site that is NOT a matched sink/sanitizer and carries a resolvable
295
+ // callee name; the worklist then records a CallResult edge when the result
296
+ // reaches a sink / return / another call arg. The fixpoint composes it with
297
+ // the callee's `sourceToReturn` (the floor cannot — the source is in the
298
+ // callee, so the caller passes no tainted input).
299
+ //
300
+ // Documented limitation: a result passed DIRECTLY into a modelled sink with
301
+ // no binding (`exec(getInput())`) is not recorded as `dest:sink` — the sink
302
+ // is occurrence-gated by `matchFunctionSites` and a bare call result is not a
303
+ // binding occurrence, so `exec` reads as a plain call (recorded `dest:callArg`
304
+ // to a callee with no summary → uncomposed). The binding form
305
+ // (`const t = getInput(); exec(t)`) is the supported path.
306
+ for (const block of cfg.blocks) {
307
+ block.statements?.forEach((facts, stmtIdx) => {
308
+ const stmtKey = `${block.index}:${stmtIdx}`;
309
+ const point = { blockIndex: block.index, stmtIndex: stmtIdx, line: facts.line };
310
+ facts.sites?.forEach((site, siteIndex) => {
311
+ if (site.kind !== 'call' && site.kind !== 'new')
312
+ return;
313
+ if (modeledSites.has(`${stmtKey}:${siteIndex}`))
314
+ return; // sink/sanitizer — modelled
315
+ const tail = calleeTail(site.callee);
316
+ if (tail === undefined)
317
+ return; // unresolvable callee — cannot compose
318
+ // Binding case (`const t = getInput(); …`): seed the result bindings.
319
+ for (const d of site.resultDefs ?? []) {
320
+ enqueue({ bindingIdx: d, point, seedId: -2, exclusions: new Set(), originCallee: tail });
321
+ }
322
+ // Direct case (`exec(getInput())` / `return getInput()`): no result
323
+ // binding — climb the call's parent chain (or detect a bare return).
324
+ if ((site.resultDefs?.length ?? 0) === 0) {
325
+ if (site.parent === undefined && returnUseStmtKeys.has(stmtKey)) {
326
+ recordCallResult({ calleeName: tail, dest: { to: 'return' } });
327
+ }
328
+ let cur = site;
329
+ const guard = new Set([siteIndex]);
330
+ while (cur?.parent) {
331
+ const [ancIdx, argPos] = cur.parent;
332
+ if (guard.has(ancIdx))
333
+ break;
334
+ guard.add(ancIdx);
335
+ const ancestor = facts.sites?.[ancIdx];
336
+ if (!ancestor)
337
+ break;
338
+ const ancKey = `${stmtKey}:${ancIdx}`;
339
+ const sinkPositions = sinkPosBySite.get(stmtKey)?.get(ancIdx);
340
+ if (sinkPositions?.has(argPos)) {
341
+ for (const kind of sinkKindByEntry.get(stmtKey)?.get(ancIdx) ?? []) {
342
+ recordCallResult({ calleeName: tail, dest: { to: 'sink', sinkKind: kind } });
343
+ }
344
+ }
345
+ else if (!modeledSites.has(ancKey) &&
346
+ (ancestor.kind === 'call' || ancestor.kind === 'new')) {
347
+ recordCallResult({
348
+ calleeName: tail,
349
+ dest: {
350
+ to: 'callArg',
351
+ ...(calleeTail(ancestor.callee) ? { toCallee: calleeTail(ancestor.callee) } : {}),
352
+ argIndex: argPos,
353
+ },
354
+ });
355
+ }
356
+ cur = ancestor;
357
+ }
358
+ }
359
+ });
360
+ });
361
+ }
362
+ // ── forward reachability ──────────────────────────────────────────────────
363
+ let head = 0;
364
+ while (head < queue.length) {
365
+ const t = queue[head++];
366
+ const b = t.bindingIdx;
367
+ for (const fact of factsByDef.get(`${b}:${pointKey(t.point)}`) ?? []) {
368
+ const useStmt = stmtAt(fact.use);
369
+ if (!useStmt)
370
+ continue;
371
+ const useKey = `${fact.use.blockIndex}:${fact.use.stmtIndex}`;
372
+ // (1) return reach
373
+ if (returnUseStmtKeys.has(useKey) && useStmt.uses.includes(b)) {
374
+ if (t.originCallee !== undefined) {
375
+ recordCallResult({ calleeName: t.originCallee, dest: { to: 'return' } });
376
+ }
377
+ else if (t.seedId >= 0)
378
+ recordReturn(t.seedId, t.exclusions);
379
+ else
380
+ sourceReturn.add('remote-input');
381
+ }
382
+ // (2) call-arg + sink reach: occurrences of b in this statement's sites.
383
+ const sinkBySite = sinkPosBySite.get(useKey);
384
+ const kindBySite = sinkKindByEntry.get(useKey);
385
+ useStmt.sites?.forEach((site, siteIndex) => {
386
+ const argHits = occurrencesInArgs(site, b);
387
+ for (const argPos of argHits) {
388
+ const callLine = useStmt.line;
389
+ const tail = calleeTail(site.callee);
390
+ if (t.originCallee !== undefined) {
391
+ // Call-result seed (#2084 review P1-1): the result of a call to
392
+ // `originCallee` flows into THIS call's arg `argPos`.
393
+ recordCallResult({
394
+ calleeName: t.originCallee,
395
+ dest: { to: 'callArg', ...(tail ? { toCallee: tail } : {}), argIndex: argPos },
396
+ });
397
+ }
398
+ else if (t.seedId >= 0) {
399
+ const caKey = `${t.seedId}:${callLine}:${argPos}:${tail ?? ''}`;
400
+ if (!paramCallArg.has(caKey)) {
401
+ paramCallArg.set(caKey, {
402
+ param: t.seedId,
403
+ callLine,
404
+ argIndex: argPos,
405
+ ...(tail ? { calleeName: tail } : {}),
406
+ });
407
+ }
408
+ // Carry the sanitizer exclusions on the path INTO this call arg,
409
+ // intersected over occurrence paths (P1-2).
410
+ intersectKinds(paramCallArgKinds, caKey, t.exclusions);
411
+ }
412
+ else {
413
+ // Source-seeded: a generated source flowing into a call argument is
414
+ // a fixpoint SEED (it taints the callee's param). One source kind
415
+ // today ('remote-input'); when more exist the seed must carry it.
416
+ const scKey = `${callLine}:${argPos}:${tail ?? ''}`;
417
+ if (!sourceCallArg.has(scKey)) {
418
+ sourceCallArg.set(scKey, {
419
+ sourceKind: 'remote-input',
420
+ callLine,
421
+ argIndex: argPos,
422
+ ...(tail ? { calleeName: tail } : {}),
423
+ });
424
+ }
425
+ intersectKinds(sourceCallArgKinds, scKey, t.exclusions);
426
+ }
427
+ // matched sink at this position?
428
+ const sinkPositions = sinkBySite?.get(siteIndex);
429
+ if (sinkPositions?.has(argPos)) {
430
+ for (const kind of kindBySite?.get(siteIndex) ?? []) {
431
+ if (t.exclusions.has(kind))
432
+ continue;
433
+ if (t.originCallee !== undefined) {
434
+ // A generated source returned by `originCallee` reaches a sink.
435
+ recordCallResult({
436
+ calleeName: t.originCallee,
437
+ dest: { to: 'sink', sinkKind: kind },
438
+ });
439
+ }
440
+ else if (t.seedId >= 0) {
441
+ const sKey = `${t.seedId}:${kind}`;
442
+ if (!paramSink.has(sKey)) {
443
+ paramSink.add(sKey);
444
+ paramSinkOut.push({ param: t.seedId, sinkKind: kind });
445
+ }
446
+ }
447
+ }
448
+ }
449
+ }
450
+ });
451
+ // (3) onward floor: this statement's defs become tainted, with sanitizer
452
+ // result-def exclusions accumulated.
453
+ const sanByDef = sanitizerResultDefKinds.get(useKey);
454
+ for (const d of [...useStmt.defs, ...(useStmt.mayDefs ?? [])]) {
455
+ const added = sanByDef?.get(d);
456
+ const exclusions = added && added.length > 0 ? new Set([...t.exclusions, ...added]) : t.exclusions;
457
+ enqueue({
458
+ bindingIdx: d,
459
+ point: {
460
+ blockIndex: fact.use.blockIndex,
461
+ stmtIndex: fact.use.stmtIndex,
462
+ line: useStmt.line,
463
+ },
464
+ seedId: t.seedId,
465
+ exclusions,
466
+ ...(t.originCallee !== undefined ? { originCallee: t.originCallee } : {}),
467
+ });
468
+ }
469
+ }
470
+ }
471
+ // ── deterministic assembly ────────────────────────────────────────────────
472
+ const paramToReturn = [...paramReturn.entries()]
473
+ .map(([param, kinds]) => ({
474
+ param,
475
+ ...(kinds.size > 0 ? { neutralized: sortSinkKinds(kinds) } : {}),
476
+ }))
477
+ .sort((a, b) => a.param - b.param);
478
+ const paramToCallArg = [...paramCallArg.entries()]
479
+ .map(([key, edge]) => {
480
+ const kinds = paramCallArgKinds.get(key);
481
+ return kinds && kinds.size > 0 ? { ...edge, neutralized: sortSinkKinds(kinds) } : edge;
482
+ })
483
+ .sort((a, b) => a.param - b.param ||
484
+ a.callLine - b.callLine ||
485
+ a.argIndex - b.argIndex ||
486
+ (a.calleeName ?? '').localeCompare(b.calleeName ?? ''));
487
+ const paramToSink = paramSinkOut.sort((a, b) => a.param - b.param || sinkKindRank(a.sinkKind) - sinkKindRank(b.sinkKind));
488
+ const sourceToReturn = sourceReturn.size > 0 ? [{ sourceKind: 'remote-input' }] : [];
489
+ const sourceToCallArg = [...sourceCallArg.entries()]
490
+ .map(([key, edge]) => {
491
+ const kinds = sourceCallArgKinds.get(key);
492
+ return kinds && kinds.size > 0 ? { ...edge, neutralized: sortSinkKinds(kinds) } : edge;
493
+ })
494
+ .sort((a, b) => a.callLine - b.callLine ||
495
+ a.argIndex - b.argIndex ||
496
+ (a.calleeName ?? '').localeCompare(b.calleeName ?? ''));
497
+ const callResultsOut = [...callResults.values()].sort((a, b) => {
498
+ const ord = (cr) => {
499
+ const d = cr.dest;
500
+ const dest = d.to === 'sink'
501
+ ? `1sink:${d.sinkKind}`
502
+ : d.to === 'return'
503
+ ? '2return'
504
+ : `0arg:${d.toCallee ?? ''}:${d.argIndex}`;
505
+ return `${cr.calleeName}|${dest}`;
506
+ };
507
+ return ord(a).localeCompare(ord(b));
508
+ });
509
+ return {
510
+ status: 'computed',
511
+ facts: {
512
+ paramCount,
513
+ paramToReturn,
514
+ paramToCallArg,
515
+ paramToSink,
516
+ sourceToReturn,
517
+ sourceToCallArg,
518
+ callResults: callResultsOut,
519
+ },
520
+ };
521
+ }
522
+ /** Argument positions where binding `b` occurs (direct or via a nested site). */
523
+ function occurrencesInArgs(site, b) {
524
+ const hits = [];
525
+ site.args?.forEach((entries, argPos) => {
526
+ for (const e of entries) {
527
+ if (typeof e === 'number') {
528
+ if (e === b)
529
+ hits.push(argPos);
530
+ }
531
+ else if (e[0] === b) {
532
+ hits.push(argPos);
533
+ }
534
+ }
535
+ });
536
+ return hits;
537
+ }