gitnexus 1.6.8-rc.9 → 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 (430) 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 +11 -0
  135. package/dist/core/ingestion/languages/c-cpp.js +6 -1
  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 +3 -0
  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 +23 -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/parse-worker.d.ts +13 -0
  238. package/dist/core/ingestion/workers/parse-worker.js +76 -17
  239. package/dist/core/ingestion/workers/worker-pool.d.ts +9 -40
  240. package/dist/core/ingestion/workers/worker-pool.js +15 -2
  241. package/dist/core/lbug/csv-generator.d.ts +51 -3
  242. package/dist/core/lbug/csv-generator.js +369 -255
  243. package/dist/core/lbug/lbug-adapter.d.ts +58 -18
  244. package/dist/core/lbug/lbug-adapter.js +346 -84
  245. package/dist/core/lbug/lbug-config.d.ts +19 -0
  246. package/dist/core/lbug/lbug-config.js +29 -0
  247. package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
  248. package/dist/core/lbug/pdg-emit-sink.js +358 -0
  249. package/dist/core/lbug/pool-adapter.d.ts +35 -0
  250. package/dist/core/lbug/pool-adapter.js +90 -1
  251. package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
  252. package/dist/core/lbug/rel-pair-routing.js +140 -0
  253. package/dist/core/lbug/schema.d.ts +2 -2
  254. package/dist/core/lbug/schema.js +3 -0
  255. package/dist/core/logger.d.ts +0 -5
  256. package/dist/core/run-analyze.d.ts +96 -0
  257. package/dist/core/run-analyze.js +207 -3
  258. package/dist/core/tree-sitter/parser-loader.js +5 -4
  259. package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
  260. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  261. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  262. package/dist/core/wiki/generator.js +3 -1
  263. package/dist/core/wiki/graph-queries.d.ts +6 -0
  264. package/dist/core/wiki/graph-queries.js +9 -1
  265. package/dist/mcp/http-transport.d.ts +134 -0
  266. package/dist/mcp/http-transport.js +503 -0
  267. package/dist/mcp/local/local-backend.d.ts +110 -24
  268. package/dist/mcp/local/local-backend.js +1225 -19
  269. package/dist/mcp/local/pdg-impact.d.ts +384 -0
  270. package/dist/mcp/local/pdg-impact.js +1972 -0
  271. package/dist/mcp/resources.js +13 -1
  272. package/dist/mcp/tools.d.ts +12 -0
  273. package/dist/mcp/tools.js +214 -7
  274. package/dist/server/analyze-launch.d.ts +29 -0
  275. package/dist/server/analyze-launch.js +137 -0
  276. package/dist/server/analyze-upload.d.ts +33 -0
  277. package/dist/server/analyze-upload.js +123 -0
  278. package/dist/server/analyze-worker.d.ts +17 -1
  279. package/dist/server/api.d.ts +17 -0
  280. package/dist/server/api.js +132 -168
  281. package/dist/server/git-clone.d.ts +43 -2
  282. package/dist/server/git-clone.js +211 -16
  283. package/dist/server/mcp-http.d.ts +5 -6
  284. package/dist/server/mcp-http.js +9 -82
  285. package/dist/server/middleware.d.ts +40 -0
  286. package/dist/server/middleware.js +92 -0
  287. package/dist/server/private-ip.d.ts +1 -0
  288. package/dist/server/private-ip.js +15 -0
  289. package/dist/server/upload-ingest.d.ts +56 -0
  290. package/dist/server/upload-ingest.js +276 -0
  291. package/dist/server/upload-paths.d.ts +37 -0
  292. package/dist/server/upload-paths.js +57 -0
  293. package/dist/server/upload-sweep.d.ts +20 -0
  294. package/dist/server/upload-sweep.js +57 -0
  295. package/dist/storage/git.js +5 -8
  296. package/dist/storage/parse-cache.d.ts +22 -1
  297. package/dist/storage/parse-cache.js +41 -10
  298. package/dist/storage/repo-manager.d.ts +121 -10
  299. package/dist/storage/repo-manager.js +43 -5
  300. package/dist/types/pipeline.d.ts +9 -0
  301. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
  302. package/hooks/claude/gitnexus-hook.cjs +110 -13
  303. package/hooks/claude/hook-db-lock-probe.cjs +513 -26
  304. package/package.json +8 -2
  305. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  306. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  307. package/scripts/cross-platform-tests.ts +1 -0
  308. package/skills/gitnexus-debugging.md +16 -4
  309. package/skills/gitnexus-exploring.md +3 -3
  310. package/skills/gitnexus-guide.md +33 -0
  311. package/skills/gitnexus-pdg-query.md +89 -0
  312. package/skills/gitnexus-refactoring.md +1 -1
  313. package/skills/gitnexus-taint-analysis.md +178 -0
  314. package/vendor/tree-sitter-c/package.json +1 -1
  315. package/vendor/tree-sitter-dart/package.json +1 -1
  316. package/vendor/tree-sitter-kotlin/package.json +1 -1
  317. package/vendor/tree-sitter-proto/package.json +1 -1
  318. package/vendor/tree-sitter-swift/package.json +1 -1
  319. package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
  320. package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
  321. package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
  322. package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
  323. package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
  324. package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
  325. package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
  326. package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
  327. package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
  328. package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
  329. package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
  330. package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
  331. package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
  332. package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
  333. package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
  334. package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
  335. package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
  336. package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
  337. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
  338. package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
  339. package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
  340. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
  341. package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
  342. package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
  343. package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
  344. package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
  345. package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
  346. package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
  347. package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
  348. package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
  349. package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
  350. package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
  351. package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
  352. package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
  353. package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
  354. package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
  355. package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
  356. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
  357. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
  358. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
  359. package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
  360. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
  361. package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
  362. package/web/assets/index-BvYev1UJ.js +635 -0
  363. package/web/assets/index-_lgn7hs5.css +2 -0
  364. package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
  365. package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
  366. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
  367. package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
  368. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
  369. package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
  370. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
  371. package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
  372. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
  373. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
  374. package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
  375. package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
  376. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
  377. package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
  378. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
  379. package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
  380. package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
  381. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
  382. package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
  383. package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
  384. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
  385. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
  386. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
  387. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
  388. package/web/index.html +13 -15
  389. package/scripts/materialize-vendor-grammars.cjs +0 -97
  390. package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
  391. package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
  392. package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
  393. package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
  394. package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
  395. package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
  396. package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
  397. package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
  398. package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
  399. package/web/assets/chunk-CilyBKbf.js +0 -1
  400. package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
  401. package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
  402. package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
  403. package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
  404. package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
  405. package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
  406. package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
  407. package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
  408. package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
  409. package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
  410. package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
  411. package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
  412. package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
  413. package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
  414. package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
  415. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
  416. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
  417. package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
  418. package/web/assets/index-BKWA-m7o.css +0 -2
  419. package/web/assets/index-CG6q8eTs.js +0 -626
  420. package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
  421. package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
  422. package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
  423. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
  424. package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
  425. package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
  426. package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
  427. package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
  428. package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
  429. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
  430. /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
@@ -1,23 +1,32 @@
1
1
  /**
2
2
  * Git Clone Utility
3
3
  *
4
- * Shallow-clones repositories into ~/.gitnexus/repos/{name}/.
4
+ * Shallow-clones repositories into the clone root (getGlobalDir()/repos/{name}/).
5
5
  * If already cloned, does git pull instead.
6
6
  */
7
7
  import { spawn } from 'child_process';
8
8
  import path from 'path';
9
- import os from 'os';
10
9
  import fs from 'fs/promises';
11
10
  import { isIP } from 'net';
12
11
  import { logger } from '../core/logger.js';
13
12
  import { parseRepoNameFromUrl } from '../storage/git.js';
14
- /** Root directory for all cloned repositories. Targets must resolve inside this. */
15
- const CLONE_ROOT = path.resolve(path.join(os.homedir(), '.gitnexus', 'repos'));
13
+ import { getGlobalDir } from '../storage/repo-manager.js';
14
+ /**
15
+ * Root directory for all cloned repositories. Targets must resolve inside this.
16
+ *
17
+ * Sourced from getGlobalDir() so it honors GITNEXUS_HOME — the Docker image sets
18
+ * GITNEXUS_HOME=/data/gitnexus, the persistent volume that also holds the
19
+ * registry and indexes. Without this, clones landed in the container's
20
+ * ephemeral ~/.gitnexus/repos and were lost on container recreation while the
21
+ * registry still pointed at the dead path. Falls back to ~/.gitnexus when the
22
+ * env var is unset (CLI / local installs), matching the prior behavior exactly.
23
+ */
24
+ const CLONE_ROOT = path.resolve(path.join(getGlobalDir(), 'repos'));
16
25
  // A valid git repository name is filesystem-safe: alphanumerics plus `. _ -`.
17
26
  // Rejecting anything else (including `..`, `/`, `\`, shell metacharacters)
18
27
  // guarantees getCloneDir(repoName) cannot escape CLONE_ROOT regardless of
19
28
  // how the caller derived repoName.
20
- const REPO_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;
29
+ export const REPO_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;
21
30
  /**
22
31
  * Extract the repository name from a git URL (HTTPS or SSH).
23
32
  *
@@ -197,6 +206,60 @@ function assertNotPrivateIPv4(ip) {
197
206
  *
198
207
  * Exported so the separator placement is testable without mocking spawn.
199
208
  */
209
+ /**
210
+ * Detect Azure DevOps URLs — both self-hosted (via AZURE_DEVOPS_URL env)
211
+ * and cloud (dev.azure.com / *.visualstudio.com).
212
+ *
213
+ * Self-hosted Azure DevOps Server instances use arbitrary hostnames
214
+ * (e.g. `http://tfs.corp.example/Collection/Project/_git/Repo`), so the
215
+ * function checks `AZURE_DEVOPS_URL` first. Cloud addresses are a
216
+ * hardcoded fallback so PAT injection works out-of-the-box for
217
+ * dev.azure.com without extra configuration.
218
+ */
219
+ export function isAzureDevOpsUrl(url) {
220
+ try {
221
+ // Strip a single trailing dot: `dev.azure.com.` is a valid absolute FQDN
222
+ // that resolves to the same host, so it must match too.
223
+ const host = new URL(url).hostname.toLowerCase().replace(/\.$/, '');
224
+ // Self-hosted: match against the configured base URL.
225
+ const configuredBase = process.env.AZURE_DEVOPS_URL;
226
+ if (configuredBase) {
227
+ try {
228
+ const baseHost = new URL(configuredBase).hostname.toLowerCase().replace(/\.$/, '');
229
+ if (host === baseHost)
230
+ return true;
231
+ }
232
+ catch {
233
+ /* invalid AZURE_DEVOPS_URL — fall through to cloud check */
234
+ }
235
+ }
236
+ // Cloud fallback.
237
+ return host === 'dev.azure.com' || host.endsWith('.visualstudio.com');
238
+ }
239
+ catch {
240
+ return false;
241
+ }
242
+ }
243
+ /**
244
+ * One-time startup warning when AZURE_DEVOPS_URL is configured over cleartext
245
+ * http:// — the Azure DevOps PAT would then be sent unencrypted on every
246
+ * clone. Self-hosted instances that only serve http are still supported (we
247
+ * do not refuse), but operators rarely read request-time logs, so surface it
248
+ * at boot too. Call once from server startup.
249
+ */
250
+ export function warnIfInsecureAzureConfig() {
251
+ const base = process.env.AZURE_DEVOPS_URL;
252
+ if (!base)
253
+ return;
254
+ try {
255
+ if (new URL(base).protocol === 'http:') {
256
+ logger.warn('AZURE_DEVOPS_URL is configured over cleartext http:// — the Azure DevOps PAT will be sent unencrypted. Prefer https:// where your instance supports it.');
257
+ }
258
+ }
259
+ catch {
260
+ /* invalid AZURE_DEVOPS_URL — isAzureDevOpsUrl already tolerates this */
261
+ }
262
+ }
200
263
  export function buildCloneArgs(url, targetDir) {
201
264
  return ['clone', '--depth', '1', '--', url, targetDir];
202
265
  }
@@ -327,7 +390,7 @@ export async function assertRemoteMatchesRequestedUrl(targetDir, requestedUrl) {
327
390
  * `--` (e.g. `--upload-pack=evil`) cannot be interpreted as a git option
328
391
  * (CodeQL js/second-order-command-line-injection).
329
392
  */
330
- export async function cloneOrPull(url, targetDir, onProgress) {
393
+ export async function cloneOrPull(url, targetDir, onProgress, options) {
331
394
  // Containment barrier — inline with the canonical path.relative idiom so
332
395
  // CodeQL recognizes the sanitizer at every following filesystem and
333
396
  // subprocess sink. The same `safeTarget` is used for every downstream
@@ -355,28 +418,160 @@ export async function cloneOrPull(url, targetDir, onProgress) {
355
418
  // whatever remote the dir was originally cloned from.
356
419
  await assertRemoteMatchesRequestedUrl(safeTarget, url);
357
420
  onProgress?.({ phase: 'pulling', message: 'Pulling latest changes...' });
358
- await runGit(['pull', '--ff-only'], safeTarget);
421
+ await runGit(['pull', '--ff-only'], safeTarget, { token: options?.token, url });
359
422
  }
360
423
  else {
361
424
  await fs.mkdir(path.dirname(safeTarget), { recursive: true });
362
425
  onProgress?.({ phase: 'cloning', message: `Cloning ${url}...` });
363
- await runGit(buildCloneArgs(url, safeTarget));
426
+ await runGit(buildCloneArgs(url, safeTarget), undefined, { token: options?.token, url });
364
427
  }
365
428
  return safeTarget;
366
429
  }
367
- function runGit(args, cwd) {
430
+ /**
431
+ * Hosts the per-request GitHub PAT may be sent to. Exported so the
432
+ * /api/analyze boundary check and this injection-site check share one
433
+ * allowlist (they must agree, or a token accepted by the API could be
434
+ * silently dropped — or worse — at injection).
435
+ */
436
+ export const GITHUB_TOKEN_HOSTS = new Set(['github.com', 'www.github.com']);
437
+ /**
438
+ * Resolve at most ONE git credential for a clone/pull, by server-side policy
439
+ * keyed on the clone host against a fixed allowlist (never a free-form user
440
+ * toggle):
441
+ * 1. a per-request GitHub PAT — only for hosts in GITHUB_TOKEN_HOSTS;
442
+ * 2. else the server's AZURE_DEVOPS_PAT — only for Azure DevOps hosts;
443
+ * 3. else none.
444
+ * The two host sets are disjoint, so at most one credential ever applies; the
445
+ * GitHub token taking precedence is deterministic for the pathological case
446
+ * where AZURE_DEVOPS_URL is itself configured to a github.com host. Returns
447
+ * the base64 of the Basic-auth `user:secret` pair, or undefined.
448
+ *
449
+ * Security note (re CodeQL js/user-controlled-bypass): the clone URL is
450
+ * user-controlled and selects WHICH credential applies, but it cannot
451
+ * redirect a credential to an arbitrary host — the host is matched against
452
+ * fixed server-side allowlists (GITHUB_TOKEN_HOSTS, isAzureDevOpsUrl's
453
+ * dev.azure.com/*.visualstudio.com/configured AZURE_DEVOPS_URL), and the
454
+ * emitted header is host-scoped (buildExtraHeaderKey). A URL outside the
455
+ * allowlists yields no credential. The selection is therefore server-policy,
456
+ * not a bypass the user can steer.
457
+ */
458
+ function resolveGitCredential(options) {
459
+ const url = options?.url;
460
+ if (!url)
461
+ return undefined;
462
+ let host;
463
+ try {
464
+ host = new URL(url).hostname.toLowerCase();
465
+ }
466
+ catch {
467
+ return undefined;
468
+ }
469
+ // 1. Per-request GitHub PAT — github.com only (mirrors the /api/analyze
470
+ // host-bind so the user's token is never sent off github.com).
471
+ if (options.token && GITHUB_TOKEN_HOSTS.has(host)) {
472
+ return Buffer.from(`x-access-token:${options.token}`).toString('base64');
473
+ }
474
+ // 2. Server-configured Azure DevOps PAT — Azure hosts only.
475
+ const azurePat = process.env.AZURE_DEVOPS_PAT;
476
+ if (azurePat && isAzureDevOpsUrl(url)) {
477
+ return Buffer.from(`:${azurePat}`).toString('base64');
478
+ }
479
+ return undefined;
480
+ }
481
+ /**
482
+ * Build the host-scoped git config key `http.<origin+path>.extraHeader` from
483
+ * the raw clone URL, so the Authorization header is attached only to the
484
+ * intended origin (and its clone sub-requests like /info/refs), never a
485
+ * redirect target. Derived from the SAME raw URL git clones from — not the
486
+ * normalize-for-compare form, which strips `.git` and would desync the key
487
+ * from the wire URL and silently disable the header. Userinfo/query/fragment
488
+ * are dropped (not part of git's URL match) and control characters stripped
489
+ * (git rejects a newline in a config key outright).
490
+ */
491
+ function buildExtraHeaderKey(url) {
492
+ let scoped;
493
+ try {
494
+ const u = new URL(url);
495
+ u.username = '';
496
+ u.password = '';
497
+ u.search = '';
498
+ u.hash = '';
499
+ scoped = `${u.protocol}//${u.host}${u.pathname}`;
500
+ }
501
+ catch {
502
+ return undefined;
503
+ }
504
+ scoped = scoped.replace(/[\r\n\0]/g, '');
505
+ return `http.${scoped}.extraHeader`;
506
+ }
507
+ /**
508
+ * Warn (do not block) when a credential is about to be sent over cleartext
509
+ * http://. Base64 is encoding, not encryption, so an on-path observer can
510
+ * read the PAT. We keep http:// working for self-hosted Azure DevOps Server.
511
+ */
512
+ function warnIfCleartextCredential(url) {
513
+ if (!url)
514
+ return;
515
+ try {
516
+ const u = new URL(url);
517
+ if (u.protocol === 'http:') {
518
+ logger.warn(`Sending a git credential over cleartext http:// (${u.host}) — base64 is not encryption. Prefer https:// where the host supports it.`);
519
+ }
520
+ }
521
+ catch {
522
+ /* resolver already validated the URL */
523
+ }
524
+ }
525
+ /**
526
+ * Build the spawn env for `git`. Suppresses credential prompts and, when a
527
+ * credential resolves (see resolveGitCredential), injects a single
528
+ * host-scoped Authorization header via the `GIT_CONFIG_*` env protocol
529
+ * (git ≥2.31) so credentials never appear in argv or the URL. Appends after
530
+ * any existing `GIT_CONFIG_COUNT` rather than overwriting it. Exported for
531
+ * unit tests.
532
+ */
533
+ export function buildGitEnv(baseEnv, options) {
534
+ const env = {
535
+ ...baseEnv,
536
+ // Prevent git from prompting for credentials (hangs the process)
537
+ GIT_TERMINAL_PROMPT: '0',
538
+ // Ensure no credential helper tries to open a GUI prompt
539
+ GIT_ASKPASS: process.platform === 'win32' ? 'echo' : '/bin/true',
540
+ // Scrub git's HTTP/transport trace vars: if inherited from the parent
541
+ // process they dump every request header — including the injected
542
+ // Authorization header — to stderr, which runGit captures and logs.
543
+ // `undefined` makes child_process omit the key from the child env.
544
+ GIT_TRACE: undefined,
545
+ GIT_TRACE_CURL: undefined,
546
+ GIT_TRACE_PACKET: undefined,
547
+ GIT_CURL_VERBOSE: undefined,
548
+ };
549
+ const credential = resolveGitCredential(options);
550
+ const key = options?.url ? buildExtraHeaderKey(options.url) : undefined;
551
+ if (credential && key) {
552
+ // Append after any GIT_CONFIG_* the operator already set, so we never
553
+ // clobber their git config (e.g. an enforced http.sslVerify).
554
+ const existing = Number.parseInt(env.GIT_CONFIG_COUNT ?? '', 10);
555
+ const base = Number.isInteger(existing) && existing > 0 ? existing : 0;
556
+ env.GIT_CONFIG_COUNT = String(base + 1);
557
+ env[`GIT_CONFIG_KEY_${base}`] = key;
558
+ env[`GIT_CONFIG_VALUE_${base}`] = `Authorization: Basic ${credential}`;
559
+ warnIfCleartextCredential(options?.url);
560
+ }
561
+ return env;
562
+ }
563
+ // `options` carries the inputs the credential resolver needs: a per-request
564
+ // GitHub `token` and the clone `url`. buildGitEnv injects at most ONE
565
+ // host-scoped Authorization header (GitHub PAT for github.com, else the
566
+ // server's AZURE_DEVOPS_PAT for Azure hosts) via the GIT_CONFIG_* protocol —
567
+ // never in argv. See resolveGitCredential / buildExtraHeaderKey.
568
+ function runGit(args, cwd, options) {
368
569
  return new Promise((resolve, reject) => {
369
570
  const proc = spawn('git', args, {
370
571
  cwd,
371
572
  stdio: ['ignore', 'pipe', 'pipe'],
372
573
  windowsHide: true,
373
- env: {
374
- ...process.env,
375
- // Prevent git from prompting for credentials (hangs the process)
376
- GIT_TERMINAL_PROMPT: '0',
377
- // Ensure no credential helper tries to open a GUI prompt
378
- GIT_ASKPASS: process.platform === 'win32' ? 'echo' : '/bin/true',
379
- },
574
+ env: buildGitEnv(process.env, options),
380
575
  });
381
576
  let stderr = '';
382
577
  proc.stderr.on('data', (chunk) => {
@@ -1,12 +1,11 @@
1
1
  /**
2
- * MCP over HTTP
2
+ * MCP over HTTP — route mount helper for the web-UI server.
3
3
  *
4
- * Mounts the GitNexus MCP server on Express using StreamableHTTP transport.
5
- * Each connecting client gets its own stateful session; the LocalBackend
6
- * is shared across all sessions (thread-safe — lazy LadybugDB per repo).
4
+ * Mounts the GitNexus MCP endpoint (/api/mcp) onto an existing Express
5
+ * application. Session management lives in mcp/http-transport.ts, preserving
6
+ * the established server/ mcp/ dependency direction.
7
7
  *
8
- * Sessions are cleaned up on explicit close or after SESSION_TTL_MS of inactivity
9
- * (guards against network drops that never trigger onclose).
8
+ * Used by server/api.ts to wire up the full web server.
10
9
  */
11
10
  import type { Express } from 'express';
12
11
  import type { LocalBackend } from '../mcp/local/local-backend.js';
@@ -1,80 +1,18 @@
1
1
  /**
2
- * MCP over HTTP
2
+ * MCP over HTTP — route mount helper for the web-UI server.
3
3
  *
4
- * Mounts the GitNexus MCP server on Express using StreamableHTTP transport.
5
- * Each connecting client gets its own stateful session; the LocalBackend
6
- * is shared across all sessions (thread-safe — lazy LadybugDB per repo).
4
+ * Mounts the GitNexus MCP endpoint (/api/mcp) onto an existing Express
5
+ * application. Session management lives in mcp/http-transport.ts, preserving
6
+ * the established server/ mcp/ dependency direction.
7
7
  *
8
- * Sessions are cleaned up on explicit close or after SESSION_TTL_MS of inactivity
9
- * (guards against network drops that never trigger onclose).
8
+ * Used by server/api.ts to wire up the full web server.
10
9
  */
11
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
12
- import { createMCPServer } from '../mcp/server.js';
13
- import { randomUUID } from 'crypto';
10
+ import { createStreamableHttpHandler } from '../mcp/http-transport.js';
14
11
  import { logger } from '../core/logger.js';
15
- /** Idle sessions are evicted after 30 minutes */
16
- const SESSION_TTL_MS = 30 * 60 * 1000;
17
- /** Cleanup sweep runs every 5 minutes */
18
- const CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
19
12
  export function mountMCPEndpoints(app, backend) {
20
- const sessions = new Map();
21
- // Periodic cleanup of idle sessions (guards against network drops)
22
- const cleanupTimer = setInterval(() => {
23
- const now = Date.now();
24
- for (const [id, session] of sessions) {
25
- if (now - session.lastActivity > SESSION_TTL_MS) {
26
- try {
27
- session.server.close();
28
- }
29
- catch { }
30
- sessions.delete(id);
31
- }
32
- }
33
- }, CLEANUP_INTERVAL_MS);
34
- if (cleanupTimer && typeof cleanupTimer === 'object' && 'unref' in cleanupTimer) {
35
- cleanupTimer.unref();
36
- }
37
- const handleMcpRequest = async (req, res) => {
38
- const sessionId = req.headers['mcp-session-id'];
39
- if (sessionId && sessions.has(sessionId)) {
40
- // Existing session — delegate to its transport
41
- const session = sessions.get(sessionId);
42
- session.lastActivity = Date.now();
43
- await session.transport.handleRequest(req, res, req.body);
44
- }
45
- else if (sessionId) {
46
- // Unknown/expired session ID — tell client to re-initialize (per MCP spec)
47
- res.status(404).json({
48
- jsonrpc: '2.0',
49
- error: { code: -32001, message: 'Session not found. Re-initialize.' },
50
- id: null,
51
- });
52
- }
53
- else if (req.method === 'POST') {
54
- // No session ID — new client initializing
55
- const transport = new StreamableHTTPServerTransport({
56
- sessionIdGenerator: () => randomUUID(),
57
- });
58
- const server = createMCPServer(backend);
59
- await server.connect(transport);
60
- await transport.handleRequest(req, res, req.body);
61
- if (transport.sessionId) {
62
- sessions.set(transport.sessionId, { server, transport, lastActivity: Date.now() });
63
- transport.onclose = () => {
64
- sessions.delete(transport.sessionId);
65
- };
66
- }
67
- }
68
- else {
69
- res.status(400).json({
70
- jsonrpc: '2.0',
71
- error: { code: -32000, message: 'No valid session. Send a POST to initialize.' },
72
- id: null,
73
- });
74
- }
75
- };
13
+ const { handler, cleanup } = createStreamableHttpHandler(backend);
76
14
  app.all('/api/mcp', (req, res) => {
77
- void handleMcpRequest(req, res).catch((err) => {
15
+ void handler(req, res).catch((err) => {
78
16
  logger.error({ err }, 'MCP HTTP request failed:');
79
17
  if (res.headersSent)
80
18
  return;
@@ -85,17 +23,6 @@ export function mountMCPEndpoints(app, backend) {
85
23
  });
86
24
  });
87
25
  });
88
- const cleanup = async () => {
89
- clearInterval(cleanupTimer);
90
- const closers = [...sessions.values()].map(async (session) => {
91
- try {
92
- await Promise.resolve(session.server.close());
93
- }
94
- catch { }
95
- });
96
- sessions.clear();
97
- await Promise.allSettled(closers);
98
- };
99
- console.log('MCP HTTP endpoints mounted at /api/mcp');
26
+ logger.info('MCP HTTP endpoints mounted at /api/mcp');
100
27
  return cleanup;
101
28
  }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Shared Express route guards (alongside createRouteLimiter in validation.ts).
3
+ */
4
+ import type { Request, Response } from 'express';
5
+ /**
6
+ * Canonicalize a bound-host string into the form a browser `Origin` hostname
7
+ * takes after WHATWG URL parsing, so the same-host comparison in
8
+ * {@link createLocalhostOriginGuard} can use a plain `===`.
9
+ *
10
+ * Returns `undefined` when the host carries no single comparable identity:
11
+ * - empty / not provided
12
+ * - a wildcard bind (`0.0.0.0`, `::`, expanded `0:0:0:0:0:0:0:0`) — the server
13
+ * listens on every interface and has no one address a browser Origin maps to,
14
+ * so writes stay loopback-only (we deliberately do NOT trust the whole subnet)
15
+ * - an unparseable value
16
+ *
17
+ * Otherwise returns `new URL(...).hostname` (lowercased, IPv6 bracketed and
18
+ * compressed) — provably identical to how the request Origin is parsed below.
19
+ * Hand-rolling lowercase + bracketing is insufficient: it fails to compress
20
+ * non-canonical IPv6 forms (e.g. `fe80:0:0:0:0:0:0:1`, `::ffff:127.0.0.1`).
21
+ */
22
+ export declare function normalizeBoundHost(boundHost?: string): string | undefined;
23
+ /**
24
+ * Restrict a route to same-host browser origins. Allows:
25
+ * - loopback (`localhost`, `127.0.0.1`, `[::1]`)
26
+ * - the server's own bound host (when non-loopback, e.g. a LAN IP)
27
+ *
28
+ * Non-browser requests (no Origin header, e.g. curl / the CLI) pass through.
29
+ * This closes cross-origin reach to write routes without affecting read routes.
30
+ *
31
+ * @param boundHost - The hostname/IP the server is listening on (from
32
+ * `createServer`'s `host` parameter). When `undefined`, `'localhost'`, or a
33
+ * wildcard (`0.0.0.0`/`::`), only loopback origins are admitted.
34
+ */
35
+ export declare function createLocalhostOriginGuard(boundHost?: string): (req: Request, res: Response, next: () => void) => void;
36
+ /**
37
+ * Default guard that only allows loopback origins. For use in tests or when
38
+ * the bound host is not available.
39
+ */
40
+ export declare const requireLocalhostOrigin: (req: Request, res: Response, next: () => void) => void;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Shared Express route guards (alongside createRouteLimiter in validation.ts).
3
+ */
4
+ /**
5
+ * Canonicalize a bound-host string into the form a browser `Origin` hostname
6
+ * takes after WHATWG URL parsing, so the same-host comparison in
7
+ * {@link createLocalhostOriginGuard} can use a plain `===`.
8
+ *
9
+ * Returns `undefined` when the host carries no single comparable identity:
10
+ * - empty / not provided
11
+ * - a wildcard bind (`0.0.0.0`, `::`, expanded `0:0:0:0:0:0:0:0`) — the server
12
+ * listens on every interface and has no one address a browser Origin maps to,
13
+ * so writes stay loopback-only (we deliberately do NOT trust the whole subnet)
14
+ * - an unparseable value
15
+ *
16
+ * Otherwise returns `new URL(...).hostname` (lowercased, IPv6 bracketed and
17
+ * compressed) — provably identical to how the request Origin is parsed below.
18
+ * Hand-rolling lowercase + bracketing is insufficient: it fails to compress
19
+ * non-canonical IPv6 forms (e.g. `fe80:0:0:0:0:0:0:1`, `::ffff:127.0.0.1`).
20
+ */
21
+ export function normalizeBoundHost(boundHost) {
22
+ if (!boundHost)
23
+ return undefined;
24
+ // Bracket a bare IPv6 literal so `new URL` can parse it as a host.
25
+ const candidate = boundHost.includes(':') && !boundHost.startsWith('[') ? `[${boundHost}]` : boundHost;
26
+ let hostname;
27
+ try {
28
+ hostname = new URL(`http://${candidate}`).hostname;
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ // Wildcard binds have no single host identity → keep writes loopback-only.
34
+ if (hostname === '' || hostname === '0.0.0.0' || hostname === '[::]') {
35
+ return undefined;
36
+ }
37
+ return hostname;
38
+ }
39
+ /**
40
+ * Restrict a route to same-host browser origins. Allows:
41
+ * - loopback (`localhost`, `127.0.0.1`, `[::1]`)
42
+ * - the server's own bound host (when non-loopback, e.g. a LAN IP)
43
+ *
44
+ * Non-browser requests (no Origin header, e.g. curl / the CLI) pass through.
45
+ * This closes cross-origin reach to write routes without affecting read routes.
46
+ *
47
+ * @param boundHost - The hostname/IP the server is listening on (from
48
+ * `createServer`'s `host` parameter). When `undefined`, `'localhost'`, or a
49
+ * wildcard (`0.0.0.0`/`::`), only loopback origins are admitted.
50
+ */
51
+ export function createLocalhostOriginGuard(boundHost) {
52
+ const normalizedBoundHost = normalizeBoundHost(boundHost);
53
+ return function requireLocalhostOrigin(req, res, next) {
54
+ const origin = req.headers.origin;
55
+ if (origin === undefined) {
56
+ next();
57
+ return;
58
+ }
59
+ try {
60
+ const parsed = new URL(origin);
61
+ const hostname = parsed.hostname;
62
+ const protocol = parsed.protocol;
63
+ if (protocol !== 'http:' && protocol !== 'https:') {
64
+ throw new Error('Unsupported origin protocol');
65
+ }
66
+ if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]') {
67
+ next();
68
+ return;
69
+ }
70
+ // Allow origin matching the server's own bound host (same-host check).
71
+ // `normalizedBoundHost` is canonicalized to the WHATWG form `hostname`
72
+ // already carries; it is `undefined` for wildcard/no binds (loopback-only).
73
+ // This covers the case where the operator runs `gitnexus serve --host <LAN-IP>`.
74
+ if (normalizedBoundHost && hostname === normalizedBoundHost) {
75
+ next();
76
+ return;
77
+ }
78
+ }
79
+ catch {
80
+ /* malformed origin → reject */
81
+ }
82
+ res.status(403).json({
83
+ error: 'This endpoint is restricted to same-host origins',
84
+ code: 'origin_not_allowed',
85
+ });
86
+ };
87
+ }
88
+ /**
89
+ * Default guard that only allows loopback origins. For use in tests or when
90
+ * the bound host is not available.
91
+ */
92
+ export const requireLocalhostOrigin = createLocalhostOriginGuard();
@@ -0,0 +1 @@
1
+ export declare const isRfc1918PrivateIpv4: (hostname: string) => boolean;
@@ -0,0 +1,15 @@
1
+ const parseIpv4Octets = (hostname) => {
2
+ if (!/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname))
3
+ return null;
4
+ const octets = hostname.split('.').map(Number);
5
+ if (octets.some((o) => !Number.isInteger(o) || o < 0 || o > 255))
6
+ return null;
7
+ return octets;
8
+ };
9
+ export const isRfc1918PrivateIpv4 = (hostname) => {
10
+ const octets = parseIpv4Octets(hostname);
11
+ if (octets === null)
12
+ return false;
13
+ const [a, b] = octets;
14
+ return a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168);
15
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Secure ingestion of a browser folder upload (multipart/form-data).
3
+ *
4
+ * Replaces the path-injection-prone GET /api/fs/list directory listing. The
5
+ * client streams the selected files plus a JSON `manifest` of their
6
+ * webkitRelativePaths; we write each into an mkdtemp staging dir under
7
+ * UPLOAD_ROOT with PROVABLE containment (resolve-then-contain), hard resource
8
+ * caps, and guaranteed cleanup on every failure/abort path. No client value
9
+ * ever reaches a filesystem READ — the server only writes into a sandbox it
10
+ * created, then hands that sandbox to the analysis pipeline.
11
+ *
12
+ * Security references: CodeQL js/path-injection (resolve + startsWith(root+sep)),
13
+ * OWASP File Upload / Path Traversal.
14
+ */
15
+ import type { IncomingMessage } from 'http';
16
+ export interface IngestLimits {
17
+ /** Aggregate bytes across all files (busboy has no aggregate limit). */
18
+ maxTotalBytes: number;
19
+ /** Per-file byte cap. */
20
+ maxFileBytes: number;
21
+ /** Maximum number of files. */
22
+ maxFiles: number;
23
+ /** Maximum multipart parts (files + fields). */
24
+ maxParts: number;
25
+ /** Maximum directories created (inode-exhaustion guard). */
26
+ maxDirs: number;
27
+ /** Maximum size of the manifest field. */
28
+ maxFieldBytes: number;
29
+ }
30
+ export declare const DEFAULT_INGEST_LIMITS: IngestLimits;
31
+ export interface IngestResult {
32
+ /** Absolute path to the populated staging directory (realpath-canonical). */
33
+ stageRoot: string;
34
+ fileCount: number;
35
+ totalBytes: number;
36
+ /** First path segment shared by the uploaded tree (the picked folder). */
37
+ topLevelName: string;
38
+ }
39
+ /**
40
+ * Resolve a client-provided relative path to an absolute destination PROVABLY
41
+ * contained within `stageRoot`. Throws BadRequestError on any unsafe input.
42
+ * This is the load-bearing path-traversal-on-write control; keep it pure and
43
+ * unit-tested.
44
+ */
45
+ export declare function resolveContainedDest(stageRoot: string, rel: unknown): string;
46
+ export interface IngestOptions {
47
+ /** Override the staging parent dir (defaults to UPLOAD_ROOT; for tests). */
48
+ root?: string;
49
+ }
50
+ /**
51
+ * Parse and securely write a multipart folder upload into a fresh staging
52
+ * directory under UPLOAD_ROOT. Resolves with the populated staging dir, or
53
+ * rejects with a BadRequestError (status 400/413) after removing the staging
54
+ * dir. The caller owns promotion + cleanup of the returned `stageRoot`.
55
+ */
56
+ export declare function ingestUpload(req: IncomingMessage, limitsOverride?: Partial<IngestLimits>, opts?: IngestOptions): Promise<IngestResult>;