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,278 @@
1
+ /**
2
+ * Import-aware taint-site matcher (#2083 M3 U2, plan KTD7).
3
+ *
4
+ * Classifies a function's harvested {@link SiteRecord}s against a registered
5
+ * {@link SourceSinkSanitizerSpec}: which member reads are SOURCES, which
6
+ * call/new sites are SINKS (and at which argument positions), and which are
7
+ * SANITIZERS. Pure main-thread data work — sites + bindings come from the U1
8
+ * worker harvest, imports from `ParsedFile.parsedImports`; no AST, no I/O.
9
+ *
10
+ * PRECONDITION: the caller must gate the CFG through `hasTaintSafeSites`
11
+ * (taint/site-safety.ts) first — this module dereferences binding/site
12
+ * indices without re-validating them.
13
+ *
14
+ * ## Callee resolution precedence (bare and member-rooted calls)
15
+ *
16
+ * 1. ESM import join — the callee root's local name is resolved through the
17
+ * {@link TaintImportIndex} built from `parsedImports` (`named`/`alias`
18
+ * members, `namespace`/default-import module handles); `import { exec as
19
+ * run } from 'child_process'` makes `run(c)` resolve to
20
+ * `child_process.exec`, and `import * as cp …` makes `cp.exec(c)` resolve
21
+ * the same way.
22
+ * 2. require-literal join — a binding whose in-function defining site carries
23
+ * `requireArg` resolves like a namespace handle (`const cp =
24
+ * require('child_process'); cp.exec(c)`). A BARE call of a require-joined
25
+ * binding is matched under BOTH interpretations, `<module>.default` (the
26
+ * module/default export invoked directly) and `<module>.<localName>`
27
+ * (non-renamed destructured require — the harvest attaches `resultDefs`
28
+ * to destructured bindings without recording the property path, and the
29
+ * binding name IS the member name in the non-renamed case).
30
+ * 3. Bare-name fallback — TRUE GLOBALS only (`global: true` entries: `eval`,
31
+ * `new Function`, `encodeURIComponent`), and only when the name is neither
32
+ * import-bound nor shadowed. Conventional receiver names (`req`/`request`
33
+ * member-read sources, `res.send`, `.query`/`.execute`) are matched
34
+ * name-based by their own mechanisms, never via the global fallback.
35
+ *
36
+ * ## Shadowing rule (exact)
37
+ *
38
+ * A name is treated as function-local — blocking import/global resolution —
39
+ * iff the function's binding table contains a NON-`synthetic` entry with that
40
+ * name (an in-function `function exec(){}` / `const exec = …`). Synthetic
41
+ * bindings (kind `module`, `synthetic: true`) are imports, true globals, or
42
+ * enclosing-scope captures and do not shadow. Member-call roots use the
43
+ * harvested `receiver` binding index directly (no name scan).
44
+ *
45
+ * ## Documented resolution gaps (direction stated, per plan KTD10)
46
+ *
47
+ * - MODULE-LEVEL `const cp = require('child_process')`: the binding is
48
+ * synthetic inside the function, produces no `ParsedImport`, and its
49
+ * defining site lives outside the function's harvested sites — the
50
+ * require join cannot see it. Module-mechanism sinks miss (FN) and
51
+ * sanitizers don't kill (FP noise — never a false kill, the safe
52
+ * direction). Only in-function requires resolve.
53
+ * - RENAMED destructured require (`const { exec: run } = require(…)`):
54
+ * the dual interpretation resolves `run` to `child_process.run` — no
55
+ * match (FN). Non-renamed destructures resolve exactly.
56
+ * - CONSERVATIVE shadow scan for bare calls: ANY non-synthetic binding of
57
+ * the callee name anywhere in the function blocks import/global
58
+ * resolution, even when the shadow is block-scoped elsewhere and the call
59
+ * site actually sees the import (FN; rare; safe for sanitizers).
60
+ * - MODULE-LEVEL user declarations are indistinguishable from imports in
61
+ * the binding table (both synthetic). ESM forbids a module-level
62
+ * declaration colliding with an import name, so the import join is
63
+ * authoritative when an import exists; a module-level user function
64
+ * shadowing a TRUE GLOBAL (e.g. a local `encodeURIComponent`) is not
65
+ * detectable and would still match (pathological; accepted).
66
+ * - Handle COPIES (`const c2 = cp; c2.exec(…)`) are not followed — joins
67
+ * are one level deep (binding → import/require), never through
68
+ * assignments (FN).
69
+ * - `this.`/`super.`-rooted and call-rooted callee chains have no
70
+ * resolvable root: only the syntactic `anyReceiver`/`receivers`
71
+ * mechanisms can match them.
72
+ * - `reexport`/`wildcard`/`dynamic-*`/`side-effect` imports introduce no
73
+ * matcher-visible local binding and are skipped by the index.
74
+ */
75
+ const stripNodeScheme = (specifier) => specifier.startsWith('node:') ? specifier.slice('node:'.length) : specifier;
76
+ /**
77
+ * Build the local-name → module/member index from a file's `parsedImports`.
78
+ * Only `named`/`alias`/`namespace` kinds bind matcher-visible local names;
79
+ * `importedName === 'default'` collapses to a module handle.
80
+ */
81
+ export function buildTaintImportIndex(imports) {
82
+ const index = new Map();
83
+ for (const imp of imports) {
84
+ if (imp.kind === 'named' || imp.kind === 'alias') {
85
+ const module = stripNodeScheme(imp.targetRaw);
86
+ index.set(imp.localName, imp.importedName === 'default' ? { module } : { module, member: imp.importedName });
87
+ }
88
+ else if (imp.kind === 'namespace') {
89
+ index.set(imp.localName, { module: stripNodeScheme(imp.targetRaw) });
90
+ }
91
+ }
92
+ return index;
93
+ }
94
+ /**
95
+ * Classify a function's harvested sites against a language spec. See the
96
+ * module doc for resolution precedence, the shadowing rule, and gaps.
97
+ */
98
+ export function matchFunctionSites(cfg, spec, imports) {
99
+ const bindings = cfg.bindings ?? [];
100
+ // Non-synthetic (in-function-declared) binding indices by name — the
101
+ // shadow scan + bare-call require-join lookup.
102
+ const nonSyntheticByName = new Map();
103
+ bindings.forEach((b, i) => {
104
+ if (b.synthetic === true)
105
+ return;
106
+ const list = nonSyntheticByName.get(b.name);
107
+ if (list)
108
+ list.push(i);
109
+ else
110
+ nonSyntheticByName.set(b.name, [i]);
111
+ });
112
+ // require-literal join: binding index → module specifier. A binding def'd
113
+ // by two DIFFERENT require literals is conflicted → dropped (resolving it
114
+ // either way could fabricate a sanitizer kill).
115
+ const requireByBinding = new Map();
116
+ const conflicted = new Set();
117
+ for (const block of cfg.blocks) {
118
+ for (const stmt of block.statements ?? []) {
119
+ for (const site of stmt.sites ?? []) {
120
+ if (site.requireArg === undefined || site.resultDefs === undefined)
121
+ continue;
122
+ const module = stripNodeScheme(site.requireArg);
123
+ for (const def of site.resultDefs) {
124
+ if (conflicted.has(def))
125
+ continue;
126
+ const prior = requireByBinding.get(def);
127
+ if (prior === undefined)
128
+ requireByBinding.set(def, module);
129
+ else if (prior !== module) {
130
+ requireByBinding.delete(def);
131
+ conflicted.add(def);
132
+ }
133
+ }
134
+ }
135
+ }
136
+ }
137
+ const resolveCallee = (site) => {
138
+ if (site.callee === undefined)
139
+ return undefined;
140
+ const path = site.callee.split('.');
141
+ const root = path[0];
142
+ const rest = path.slice(1);
143
+ const canonical = [];
144
+ let globalRoot = false;
145
+ if (site.receiver !== undefined) {
146
+ // Member chain with an identifier root — origin known by binding index.
147
+ const rb = bindings[site.receiver];
148
+ if (rb.synthetic === true) {
149
+ const imp = imports.get(rb.name);
150
+ if (imp !== undefined) {
151
+ const base = imp.member === undefined ? [imp.module] : [imp.module, imp.member];
152
+ canonical.push([...base, ...rest].join('.'));
153
+ }
154
+ }
155
+ else {
156
+ const module = requireByBinding.get(site.receiver);
157
+ if (module !== undefined)
158
+ canonical.push([module, ...rest].join('.'));
159
+ }
160
+ }
161
+ else if (path.length === 1) {
162
+ // Bare call. `this`/`super`/call-rooted chains never get here (those
163
+ // are dotted-without-receiver or callee-less).
164
+ const locals = nonSyntheticByName.get(root);
165
+ if (locals !== undefined) {
166
+ // Shadowed by an in-function declaration — only the require join
167
+ // applies, under the dual interpretation (module doc).
168
+ for (const idx of locals) {
169
+ const module = requireByBinding.get(idx);
170
+ if (module !== undefined)
171
+ canonical.push(`${module}.default`, `${module}.${root}`);
172
+ }
173
+ }
174
+ else {
175
+ const imp = imports.get(root);
176
+ if (imp !== undefined) {
177
+ canonical.push(imp.member === undefined ? `${imp.module}.default` : `${imp.module}.${imp.member}`);
178
+ }
179
+ else {
180
+ globalRoot = true;
181
+ }
182
+ }
183
+ }
184
+ return { path, canonical, globalRoot };
185
+ };
186
+ /** The spread/template/registered-position rule (see MatchedSinkCall doc). */
187
+ const positionMatches = (entry, site, p) => {
188
+ if (site.template === true)
189
+ return true;
190
+ if (entry.args === undefined)
191
+ return true;
192
+ if (site.spread !== undefined && p >= site.spread) {
193
+ const spread = site.spread;
194
+ return entry.args.some((q) => q >= spread);
195
+ }
196
+ return entry.args.includes(p);
197
+ };
198
+ const sinkMechanismHit = (entry, site, r) => {
199
+ if (entry.module !== undefined)
200
+ return r.canonical.includes(`${entry.module}.${entry.name}`);
201
+ if (entry.global === true) {
202
+ return (r.globalRoot &&
203
+ r.path.length === 1 &&
204
+ r.path[0] === entry.name &&
205
+ (entry.newOnly !== true || site.kind === 'new'));
206
+ }
207
+ if (entry.anyReceiver === true) {
208
+ return r.path.length >= 2 && r.path[r.path.length - 1] === entry.name;
209
+ }
210
+ if (entry.receivers !== undefined) {
211
+ return r.path.length === 2 && entry.receivers.includes(r.path[0]) && r.path[1] === entry.name;
212
+ }
213
+ return false;
214
+ };
215
+ // Sanitizers: module + global mechanisms ONLY — never receiver-conventional,
216
+ // never bare-name for non-globals (a false kill is the forbidden direction).
217
+ const sanitizerMechanismHit = (entry, r) => {
218
+ if (entry.module !== undefined)
219
+ return r.canonical.includes(`${entry.module}.${entry.name}`);
220
+ if (entry.global === true) {
221
+ return r.globalRoot && r.path.length === 1 && r.path[0] === entry.name;
222
+ }
223
+ return false;
224
+ };
225
+ const statements = [];
226
+ let hasSource = false;
227
+ let hasSink = false;
228
+ cfg.blocks.forEach((block, blockIndex) => {
229
+ block.statements?.forEach((stmt, statementIndex) => {
230
+ const sites = stmt.sites;
231
+ if (sites === undefined || sites.length === 0)
232
+ return;
233
+ const sources = [];
234
+ const sinks = [];
235
+ const sanitizers = [];
236
+ sites.forEach((site, siteIndex) => {
237
+ if (site.kind === 'member-read') {
238
+ if (site.object === undefined || site.property === undefined)
239
+ return;
240
+ const objectName = bindings[site.object].name;
241
+ const property = site.property;
242
+ for (const entry of spec.sources) {
243
+ if (entry.objects.includes(objectName) && entry.properties.includes(property)) {
244
+ sources.push({ siteIndex, entry });
245
+ }
246
+ }
247
+ return;
248
+ }
249
+ // call / new
250
+ const resolved = resolveCallee(site);
251
+ if (resolved === undefined)
252
+ return;
253
+ for (const entry of spec.sinks) {
254
+ if (!sinkMechanismHit(entry, site, resolved))
255
+ continue;
256
+ const argPositions = [];
257
+ site.args?.forEach((occurrences, p) => {
258
+ if (occurrences.length > 0 && positionMatches(entry, site, p))
259
+ argPositions.push(p);
260
+ });
261
+ if (argPositions.length > 0)
262
+ sinks.push({ siteIndex, entry, argPositions });
263
+ }
264
+ for (const entry of spec.sanitizers) {
265
+ if (!sanitizerMechanismHit(entry, resolved))
266
+ continue;
267
+ sanitizers.push({ siteIndex, entry, resultDefs: site.resultDefs ?? [] });
268
+ }
269
+ });
270
+ if (sources.length === 0 && sinks.length === 0 && sanitizers.length === 0)
271
+ return;
272
+ hasSource ||= sources.length > 0;
273
+ hasSink ||= sinks.length > 0;
274
+ statements.push({ blockIndex, statementIndex, line: stmt.line, sources, sinks, sanitizers });
275
+ });
276
+ });
277
+ return { statements, hasSource, hasSink };
278
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Taint-path reason codec (#2083 M3 U4/U6, plan KTD6).
3
+ *
4
+ * THE one shared encoder/decoder for the hop-encoded `reason` carried on
5
+ * persisted `TAINTED` edges: the U4 emit path writes it, the U6 MCP `explain`
6
+ * tool reads it. Two hand-rolled copies of a wire format drift — both sides
7
+ * MUST import from here.
8
+ *
9
+ * ## Wire format (version `1`)
10
+ *
11
+ * ```
12
+ * 1[;<kind>]|<name>:<line>[:<flags>]|<name>:<line>[:<flags>]|…[|~]
13
+ * ```
14
+ *
15
+ * - One-character version prefix (`TAINT_PATH_CODEC_VERSION`), then an
16
+ * OPTIONAL `;<kind>` header segment, then ordered source→sink hops, each
17
+ * `variable:line[:flags]`.
18
+ * - `kind` is the finding's sink category (`SinkKind`, e.g.
19
+ * `command-injection`). It rides the reason because it is the ONLY
20
+ * persisted channel: the CodeRelation columns are
21
+ * `type/confidence/reason/step` — `step` is INT32 and the emit-time edge id
22
+ * (which embeds the kind) is not a stored column. The U6 `explain` tool
23
+ * reads it for finding classification. Charset `[a-z0-9-]` (printable
24
+ * ASCII, disjoint from every structural delimiter); `;` itself is printable
25
+ * ASCII and never appears in hop names (identifier charset) or flags.
26
+ * U6 deviation note: this header was added by U6 WITHIN version `1` —
27
+ * U4 and U6 ship in the same release, so no reason string without the
28
+ * header was ever persisted by a released build; the decoder still accepts
29
+ * header-less strings (`kind` simply decodes as `undefined`).
30
+ * - `flags` is a lowercase-letter set; only `c` (= the hop passed through an
31
+ * unmodeled call, KTD5 `viaCall`) is defined today — the rest of the
32
+ * alphabet is RESERVED, and the decoder accepts unknown flag letters so a
33
+ * future writer's output stays decodable.
34
+ * - A trailing `|~` segment is the TRUNCATION MARKER: the encoded path is a
35
+ * source-side PREFIX of the real one (hop cap, byte cap, or an unencodable
36
+ * hop name). Decoders MUST report it as "path incomplete" — never an error.
37
+ *
38
+ * ## Delimiter / round-trip discipline (KTD6)
39
+ *
40
+ * Every structural character (`|`, `:`, `~`, digits, flag letters) is
41
+ * printable ASCII: `sanitizeUTF8` (csv-generator.ts) strips control
42
+ * characters, lone surrogates, and U+FFFE/FFFF — printable ASCII passes
43
+ * through byte-exact, so the encoding survives `escapeCSVField ∘
44
+ * sanitizeUTF8` and the DB load unchanged (pinned by the round-trip test).
45
+ * None of the delimiters can appear in a JS identifier.
46
+ *
47
+ * Hop names are identifier-charset by U1 construction (the harvest records
48
+ * binding names), but the encoder DEFENDS anyway: a hop whose name falls
49
+ * outside the safe charset (or whose line is not a non-negative integer) is
50
+ * never emitted — encoding stops at the offending hop and sets the truncation
51
+ * marker, preserving the prefix-of-the-true-path invariant rather than
52
+ * corrupting the format. (`#` is in the charset: JS private names are
53
+ * `#field`, and the propagation engine's fallback hop names are `#<idx>`.)
54
+ *
55
+ * The byte cap (`TAINT_REASON_MAX_BYTES`, KTD6's "absolute reason-byte cap")
56
+ * bounds the persisted reason column regardless of hop caps: overflow drops
57
+ * TRAILING hops (keeps the source side) and sets the marker. All structural
58
+ * chars and valid names are single-byte ASCII, so `string.length` IS the
59
+ * byte length.
60
+ */
61
+ /** One-character format version prefix. Bump on any wire-format change. */
62
+ export declare const TAINT_PATH_CODEC_VERSION = "1";
63
+ /**
64
+ * Absolute cap on the encoded reason's byte length (KTD6). 4096 comfortably
65
+ * holds ~100 hops of realistic identifiers — far beyond the default hop cap
66
+ * (32) — while bounding the persisted column even at `maxHops: 0` (unlimited).
67
+ */
68
+ export declare const TAINT_REASON_MAX_BYTES = 4096;
69
+ /** The truncation-marker segment content (rides as a trailing `|~`). */
70
+ export declare const TAINT_PATH_TRUNCATION_MARKER = "~";
71
+ /** Encoder input hop — shape-compatible with `TaintHop` (propagate.ts). */
72
+ export interface TaintPathHopInput {
73
+ readonly name: string;
74
+ readonly line: number;
75
+ readonly viaCall?: boolean;
76
+ }
77
+ export interface EncodeTaintPathOptions {
78
+ /**
79
+ * The hop list is already a truncated prefix (e.g. the propagation engine's
80
+ * `hopsTruncated` from its hop cap) — emit the marker even when every hop
81
+ * fits.
82
+ */
83
+ readonly truncated?: boolean;
84
+ /** Byte cap override (tests). Default {@link TAINT_REASON_MAX_BYTES}. */
85
+ readonly maxBytes?: number;
86
+ /**
87
+ * Finding sink category (`SinkKind`) carried in the `;<kind>` header — the
88
+ * only persisted channel for it (see the module doc). A value outside the
89
+ * `[a-z0-9-]` charset is DROPPED (header omitted), never corrupted into the
90
+ * wire string; `SinkKind` is a closed lowercase-hyphen union so this is
91
+ * purely defensive.
92
+ */
93
+ readonly kind?: string;
94
+ }
95
+ export interface EncodedTaintPath {
96
+ /** The wire string for the TAINTED edge's `reason` column. */
97
+ readonly reason: string;
98
+ /** True when the marker was emitted (caller-flagged, byte cap, or bad hop). */
99
+ readonly truncated: boolean;
100
+ }
101
+ export interface DecodedTaintHop {
102
+ readonly variable: string;
103
+ readonly line: number;
104
+ /** The hop passed through an unmodeled call (flag `c`, KTD5). */
105
+ readonly viaCall: boolean;
106
+ }
107
+ export interface DecodedTaintPath {
108
+ readonly ok: true;
109
+ readonly version: string;
110
+ /** Finding sink category from the `;<kind>` header; absent when not encoded. */
111
+ readonly kind?: string;
112
+ /** Ordered source→sink hops (a PREFIX when `truncated`). */
113
+ readonly hops: readonly DecodedTaintHop[];
114
+ /** Path incomplete (trailing `|~`) — informational, NOT an error. */
115
+ readonly truncated: boolean;
116
+ }
117
+ /** Typed parse failure — the decoder never throws. */
118
+ export interface TaintPathDecodeFailure {
119
+ readonly ok: false;
120
+ readonly error: string;
121
+ }
122
+ export type TaintPathDecodeResult = DecodedTaintPath | TaintPathDecodeFailure;
123
+ /**
124
+ * Encode an ordered hop list into the versioned `reason` wire string.
125
+ * Deterministic; never throws. See the module doc for the format and the
126
+ * three truncation triggers (caller flag, unencodable hop, byte cap).
127
+ */
128
+ export declare function encodeTaintPath(hops: readonly TaintPathHopInput[], options?: EncodeTaintPathOptions): EncodedTaintPath;
129
+ /**
130
+ * Decode a `reason` wire string. Returns a typed failure for anything that is
131
+ * not a well-formed version-`1` path — never throws. A truncated path decodes
132
+ * `ok: true` with `truncated: true` ("path incomplete", per KTD6).
133
+ */
134
+ export declare function decodeTaintPath(reason: unknown): TaintPathDecodeResult;
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Taint-path reason codec (#2083 M3 U4/U6, plan KTD6).
3
+ *
4
+ * THE one shared encoder/decoder for the hop-encoded `reason` carried on
5
+ * persisted `TAINTED` edges: the U4 emit path writes it, the U6 MCP `explain`
6
+ * tool reads it. Two hand-rolled copies of a wire format drift — both sides
7
+ * MUST import from here.
8
+ *
9
+ * ## Wire format (version `1`)
10
+ *
11
+ * ```
12
+ * 1[;<kind>]|<name>:<line>[:<flags>]|<name>:<line>[:<flags>]|…[|~]
13
+ * ```
14
+ *
15
+ * - One-character version prefix (`TAINT_PATH_CODEC_VERSION`), then an
16
+ * OPTIONAL `;<kind>` header segment, then ordered source→sink hops, each
17
+ * `variable:line[:flags]`.
18
+ * - `kind` is the finding's sink category (`SinkKind`, e.g.
19
+ * `command-injection`). It rides the reason because it is the ONLY
20
+ * persisted channel: the CodeRelation columns are
21
+ * `type/confidence/reason/step` — `step` is INT32 and the emit-time edge id
22
+ * (which embeds the kind) is not a stored column. The U6 `explain` tool
23
+ * reads it for finding classification. Charset `[a-z0-9-]` (printable
24
+ * ASCII, disjoint from every structural delimiter); `;` itself is printable
25
+ * ASCII and never appears in hop names (identifier charset) or flags.
26
+ * U6 deviation note: this header was added by U6 WITHIN version `1` —
27
+ * U4 and U6 ship in the same release, so no reason string without the
28
+ * header was ever persisted by a released build; the decoder still accepts
29
+ * header-less strings (`kind` simply decodes as `undefined`).
30
+ * - `flags` is a lowercase-letter set; only `c` (= the hop passed through an
31
+ * unmodeled call, KTD5 `viaCall`) is defined today — the rest of the
32
+ * alphabet is RESERVED, and the decoder accepts unknown flag letters so a
33
+ * future writer's output stays decodable.
34
+ * - A trailing `|~` segment is the TRUNCATION MARKER: the encoded path is a
35
+ * source-side PREFIX of the real one (hop cap, byte cap, or an unencodable
36
+ * hop name). Decoders MUST report it as "path incomplete" — never an error.
37
+ *
38
+ * ## Delimiter / round-trip discipline (KTD6)
39
+ *
40
+ * Every structural character (`|`, `:`, `~`, digits, flag letters) is
41
+ * printable ASCII: `sanitizeUTF8` (csv-generator.ts) strips control
42
+ * characters, lone surrogates, and U+FFFE/FFFF — printable ASCII passes
43
+ * through byte-exact, so the encoding survives `escapeCSVField ∘
44
+ * sanitizeUTF8` and the DB load unchanged (pinned by the round-trip test).
45
+ * None of the delimiters can appear in a JS identifier.
46
+ *
47
+ * Hop names are identifier-charset by U1 construction (the harvest records
48
+ * binding names), but the encoder DEFENDS anyway: a hop whose name falls
49
+ * outside the safe charset (or whose line is not a non-negative integer) is
50
+ * never emitted — encoding stops at the offending hop and sets the truncation
51
+ * marker, preserving the prefix-of-the-true-path invariant rather than
52
+ * corrupting the format. (`#` is in the charset: JS private names are
53
+ * `#field`, and the propagation engine's fallback hop names are `#<idx>`.)
54
+ *
55
+ * The byte cap (`TAINT_REASON_MAX_BYTES`, KTD6's "absolute reason-byte cap")
56
+ * bounds the persisted reason column regardless of hop caps: overflow drops
57
+ * TRAILING hops (keeps the source side) and sets the marker. All structural
58
+ * chars and valid names are single-byte ASCII, so `string.length` IS the
59
+ * byte length.
60
+ */
61
+ /** One-character format version prefix. Bump on any wire-format change. */
62
+ export const TAINT_PATH_CODEC_VERSION = '1';
63
+ /**
64
+ * Absolute cap on the encoded reason's byte length (KTD6). 4096 comfortably
65
+ * holds ~100 hops of realistic identifiers — far beyond the default hop cap
66
+ * (32) — while bounding the persisted column even at `maxHops: 0` (unlimited).
67
+ */
68
+ export const TAINT_REASON_MAX_BYTES = 4096;
69
+ /** The truncation-marker segment content (rides as a trailing `|~`). */
70
+ export const TAINT_PATH_TRUNCATION_MARKER = '~';
71
+ /**
72
+ * Safe hop-name charset: ASCII identifier characters plus `#` (JS private
73
+ * names / the propagation engine's `#<idx>` fallback). Deliberately ASCII-only
74
+ * — a Unicode identifier is VALID JS but is skipped (truncation marker) rather
75
+ * than risking a `sanitizeUTF8` byte change breaking decode (defensive
76
+ * simplification; documented FN on path completeness, never on the finding).
77
+ */
78
+ const SAFE_NAME = /^[A-Za-z0-9_$#]+$/;
79
+ /** Decoder-side flags charset — `c` defined, the rest reserved (see module doc). */
80
+ const FLAGS = /^[a-z]*$/;
81
+ /**
82
+ * Kind-header charset: lowercase + digits + hyphen — covers every `SinkKind`
83
+ * label and stays disjoint from the structural delimiters (`;|:~`).
84
+ */
85
+ const SAFE_KIND = /^[a-z0-9-]+$/;
86
+ /**
87
+ * Encode an ordered hop list into the versioned `reason` wire string.
88
+ * Deterministic; never throws. See the module doc for the format and the
89
+ * three truncation triggers (caller flag, unencodable hop, byte cap).
90
+ */
91
+ export function encodeTaintPath(hops, options) {
92
+ // Kind header (defensively validated — see EncodeTaintPathOptions.kind).
93
+ const kindHeader = typeof options?.kind === 'string' && SAFE_KIND.test(options.kind) ? `;${options.kind}` : '';
94
+ // Floor: version char + kind header + room for the marker — a smaller cap
95
+ // could not hold even the empty truncated path. The header is identity
96
+ // material (finding classification), so it is never sacrificed to the byte
97
+ // cap; trailing hops are.
98
+ const maxBytes = Math.max(options?.maxBytes ?? TAINT_REASON_MAX_BYTES, TAINT_PATH_CODEC_VERSION.length + kindHeader.length + 2);
99
+ let truncated = options?.truncated === true;
100
+ const segments = [];
101
+ let total = TAINT_PATH_CODEC_VERSION.length + kindHeader.length;
102
+ for (const hop of hops) {
103
+ if (typeof hop.name !== 'string' ||
104
+ !SAFE_NAME.test(hop.name) ||
105
+ !Number.isInteger(hop.line) ||
106
+ hop.line < 0) {
107
+ // Unencodable hop: drop it AND everything after it so the emitted hops
108
+ // stay a faithful source-side prefix (a silent mid-path gap would lie).
109
+ truncated = true;
110
+ break;
111
+ }
112
+ const segment = `|${hop.name}:${hop.line}${hop.viaCall === true ? ':c' : ''}`;
113
+ if (total + segment.length > maxBytes) {
114
+ truncated = true;
115
+ break;
116
+ }
117
+ segments.push(segment);
118
+ total += segment.length;
119
+ }
120
+ if (truncated) {
121
+ // Make room for the trailing `|~` marker (drop trailing hops as needed).
122
+ while (segments.length > 0 && total + 2 > maxBytes) {
123
+ total -= segments.pop().length;
124
+ }
125
+ }
126
+ const reason = TAINT_PATH_CODEC_VERSION +
127
+ kindHeader +
128
+ segments.join('') +
129
+ (truncated ? `|${TAINT_PATH_TRUNCATION_MARKER}` : '');
130
+ return { reason, truncated };
131
+ }
132
+ /**
133
+ * Decode a `reason` wire string. Returns a typed failure for anything that is
134
+ * not a well-formed version-`1` path — never throws. A truncated path decodes
135
+ * `ok: true` with `truncated: true` ("path incomplete", per KTD6).
136
+ */
137
+ export function decodeTaintPath(reason) {
138
+ if (typeof reason !== 'string' || reason.length === 0) {
139
+ return { ok: false, error: 'empty or non-string reason' };
140
+ }
141
+ const version = reason[0];
142
+ if (version !== TAINT_PATH_CODEC_VERSION) {
143
+ return { ok: false, error: `unsupported taint-path version '${version}'` };
144
+ }
145
+ let body = reason.slice(1);
146
+ // Optional `;<kind>` header segment (finding sink category — see module doc).
147
+ let kind;
148
+ if (body.startsWith(';')) {
149
+ const headerEnd = body.indexOf('|');
150
+ kind = headerEnd === -1 ? body.slice(1) : body.slice(1, headerEnd);
151
+ if (!SAFE_KIND.test(kind)) {
152
+ return { ok: false, error: `invalid kind header '${kind}'` };
153
+ }
154
+ body = headerEnd === -1 ? '' : body.slice(headerEnd);
155
+ }
156
+ const hops = [];
157
+ if (body.length === 0)
158
+ return { ok: true, version, ...(kind ? { kind } : {}), hops, truncated: false };
159
+ if (!body.startsWith('|')) {
160
+ return { ok: false, error: 'malformed body: expected a hop separator after the version' };
161
+ }
162
+ const parts = body.slice(1).split('|');
163
+ let truncated = false;
164
+ for (let i = 0; i < parts.length; i++) {
165
+ const part = parts[i];
166
+ if (part === TAINT_PATH_TRUNCATION_MARKER) {
167
+ if (i !== parts.length - 1) {
168
+ return { ok: false, error: 'truncation marker not in trailing position' };
169
+ }
170
+ truncated = true;
171
+ break;
172
+ }
173
+ const fields = part.split(':');
174
+ if (fields.length < 2 || fields.length > 3) {
175
+ return { ok: false, error: `malformed hop segment '${part}'` };
176
+ }
177
+ const [name, lineStr, flags = ''] = fields;
178
+ if (!SAFE_NAME.test(name)) {
179
+ return { ok: false, error: `invalid hop variable '${name}'` };
180
+ }
181
+ if (!/^\d+$/.test(lineStr)) {
182
+ return { ok: false, error: `invalid hop line '${lineStr}'` };
183
+ }
184
+ if (!FLAGS.test(flags)) {
185
+ return { ok: false, error: `invalid hop flags '${flags}'` };
186
+ }
187
+ hops.push({ variable: name, line: Number(lineStr), viaCall: flags.includes('c') });
188
+ }
189
+ return { ok: true, version, ...(kind ? { kind } : {}), hops, truncated };
190
+ }