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
@@ -20,11 +20,16 @@ import { searchFTSFromLbug } from '../core/search/bm25-index.js';
20
20
  import { hybridSearch } from '../core/search/hybrid-search.js';
21
21
  import { LocalBackend } from '../mcp/local/local-backend.js';
22
22
  import { mountMCPEndpoints } from './mcp-http.js';
23
- import { fork } from 'child_process';
24
- import { fileURLToPath, pathToFileURL } from 'url';
23
+ import { fileURLToPath } from 'url';
25
24
  import { JobManager } from './analyze-job.js';
26
25
  import { assertString, escapeRegExp, BadRequestError, createRouteLimiter } from './validation.js';
27
- import { extractRepoName, getCloneDir, cloneOrPull } from './git-clone.js';
26
+ import { extractRepoName, getCloneDir, cloneOrPull, warnIfInsecureAzureConfig, GITHUB_TOKEN_HOSTS, } from './git-clone.js';
27
+ import { createAnalyzeUploadHandler } from './analyze-upload.js';
28
+ import { createLocalhostOriginGuard, normalizeBoundHost } from './middleware.js';
29
+ import { createLaunchAnalysisWorker } from './analyze-launch.js';
30
+ import { UPLOAD_ROOT } from './upload-paths.js';
31
+ import { sweepStaleUploads } from './upload-sweep.js';
32
+ import { isRfc1918PrivateIpv4 } from './private-ip.js';
28
33
  import { logger, flushLoggerSync } from '../core/logger.js';
29
34
  const _require = createRequire(import.meta.url);
30
35
  const pkg = _require('../../package.json');
@@ -75,21 +80,7 @@ export const isAllowedOrigin = (origin) => {
75
80
  // Only allow HTTP(S) origins — reject ftp://, file://, etc.
76
81
  if (protocol !== 'http:' && protocol !== 'https:')
77
82
  return false;
78
- const octets = hostname.split('.').map(Number);
79
- if (octets.length !== 4 || octets.some((o) => !Number.isInteger(o) || o < 0 || o > 255)) {
80
- return false;
81
- }
82
- const [a, b] = octets;
83
- // 10.0.0.0/8
84
- if (a === 10)
85
- return true;
86
- // 172.16.0.0/12 → 172.16.x.x – 172.31.x.x
87
- if (a === 172 && b >= 16 && b <= 31)
88
- return true;
89
- // 192.168.0.0/16
90
- if (a === 192 && b === 168)
91
- return true;
92
- return false;
83
+ return isRfc1918PrivateIpv4(hostname);
93
84
  };
94
85
  export class ClientDisconnectedError extends Error {
95
86
  constructor() {
@@ -589,7 +580,45 @@ export const handleQueryRequest = async (req, res, resolveRepo) => {
589
580
  res.status(500).json({ error: err.message || 'Query failed' });
590
581
  }
591
582
  };
583
+ /**
584
+ * Validate the optional `token` field of POST /api/analyze. Returns an
585
+ * { status, error } to send, or null when the token is absent or valid.
586
+ *
587
+ * The token is a GitHub PAT: charset-restricted (blocks CRLF header
588
+ * smuggling), length-bounded (1–256), and bound to github.com using the SAME
589
+ * GITHUB_TOKEN_HOSTS allowlist + hostname parse as resolveGitCredential, so a
590
+ * token the API accepts is exactly the one buildGitEnv will inject — and one
591
+ * it rejects is never sent off github.com.
592
+ *
593
+ * Exported for unit tests (the route validation is otherwise only reachable
594
+ * by booting the server).
595
+ */
596
+ export function validateAnalyzeToken(repoToken, repoUrl) {
597
+ if (repoToken === undefined)
598
+ return null;
599
+ if (typeof repoToken !== 'string')
600
+ return { status: 400, error: '"token" must be a string' };
601
+ if (repoToken.length === 0 || repoToken.length > 256)
602
+ return { status: 400, error: '"token" length must be between 1 and 256' };
603
+ if (!/^[A-Za-z0-9._~+/=-]+$/.test(repoToken))
604
+ return { status: 400, error: '"token" contains invalid characters' };
605
+ if (!repoUrl || typeof repoUrl !== 'string')
606
+ return { status: 400, error: '"token" requires "url"' };
607
+ let tokenHost;
608
+ try {
609
+ tokenHost = new URL(repoUrl).hostname.toLowerCase();
610
+ }
611
+ catch {
612
+ return { status: 400, error: '"url" must be a valid URL when "token" is provided' };
613
+ }
614
+ if (!GITHUB_TOKEN_HOSTS.has(tokenHost))
615
+ return { status: 400, error: '"token" is only supported for github.com URLs' };
616
+ return null;
617
+ }
592
618
  export const createServer = async (port, host = '127.0.0.1') => {
619
+ // Surface a cleartext Azure DevOps PAT config at boot (operators rarely
620
+ // read per-request logs). Warn-only — http:// self-hosted stays supported.
621
+ warnIfInsecureAzureConfig();
593
622
  const app = express();
594
623
  app.disable('x-powered-by');
595
624
  // Trust X-Forwarded-* headers only when the connection comes from the
@@ -630,6 +659,17 @@ export const createServer = async (port, host = '127.0.0.1') => {
630
659
  },
631
660
  }));
632
661
  app.use(express.json({ limit: '10mb' }));
662
+ // Same-host origin guard for write routes. Only allows loopback and the
663
+ // server's own bound host — scoped to prevent CSRF from other LAN devices.
664
+ const requireLocalhostOrigin = createLocalhostOriginGuard(host);
665
+ // A wildcard bind (`0.0.0.0`/`::`) has no single host identity for the
666
+ // same-host check, so browser write routes accept only loopback origins.
667
+ // Warn the operator so a remote-access deployment isn't silently write-blocked.
668
+ if (host && normalizeBoundHost(host) === undefined) {
669
+ logger.warn({ host }, `[gitnexus serve] Bound to a wildcard address (${host}); browser write routes ` +
670
+ `accept only loopback origins (localhost/127.0.0.1/[::1]). To allow writes from a ` +
671
+ `specific LAN address, bind --host <that-address> instead of a wildcard.`);
672
+ }
633
673
  // No explicit OPTIONS route is registered. The Chromium Private Network
634
674
  // Access header is set by the global middleware above (pre-cors), and
635
675
  // `cors()` itself handles OPTIONS preflights for every path. Registering a
@@ -640,6 +680,8 @@ export const createServer = async (port, host = '127.0.0.1') => {
640
680
  await backend.init();
641
681
  const cleanupMcp = mountMCPEndpoints(app, backend);
642
682
  const jobManager = new JobManager();
683
+ // Backstop: remove any upload staging dirs orphaned by a previous crash.
684
+ void sweepStaleUploads().catch(() => { });
643
685
  // Shared repo lock — prevents concurrent analyze + embed on the same repo path,
644
686
  // which would corrupt LadybugDB (analyze calls closeLbug + initLbug while embed has queries in flight).
645
687
  const activeRepoPaths = new Set();
@@ -653,6 +695,14 @@ export const createServer = async (port, host = '127.0.0.1') => {
653
695
  const releaseRepoLock = (repoPath) => {
654
696
  activeRepoPaths.delete(repoPath);
655
697
  };
698
+ // Launch the analyze worker for an already-resolved repo directory. Shared by
699
+ // the JSON /api/analyze route and the multipart /api/analyze/upload route.
700
+ const launchAnalysisWorker = createLaunchAnalysisWorker({
701
+ jobManager,
702
+ backend,
703
+ acquireRepoLock,
704
+ releaseRepoLock,
705
+ });
656
706
  /**
657
707
  * Maximum time the hold-queue will wait for an active analysis job to complete.
658
708
  * Must stay in sync with the frontend's `fetchRepoInfo({ awaitAnalysis: true })` timeout.
@@ -816,7 +866,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
816
866
  // Rate-limited (CodeQL js/missing-rate-limiting): destructive operation
817
867
  // doing fs.rm of clone + storage dirs. Default 60 rpm/IP is generous for
818
868
  // delete; tighten if abuse is observed.
819
- app.delete('/api/repo', createRouteLimiter(), async (req, res) => {
869
+ app.delete('/api/repo', createRouteLimiter(), requireLocalhostOrigin, async (req, res) => {
820
870
  try {
821
871
  const repoName = requestedRepo(req);
822
872
  if (!repoName) {
@@ -867,6 +917,13 @@ export const createServer = async (port, host = '127.0.0.1') => {
867
917
  /* clone dir may not exist */
868
918
  }
869
919
  }
920
+ // 2b. Delete the uploaded repo dir if entry.path lives under
921
+ // UPLOAD_ROOT. Drive this off entry.path (not a name-rederived dir) so
922
+ // a same-named clone is never affected.
923
+ const resolvedEntry = path.resolve(entry.path);
924
+ if (resolvedEntry === UPLOAD_ROOT || resolvedEntry.startsWith(UPLOAD_ROOT + path.sep)) {
925
+ await fs.rm(resolvedEntry, { recursive: true, force: true }).catch(() => { });
926
+ }
870
927
  // 3. Unregister from the global registry
871
928
  const { unregisterRepo } = await import('../storage/repo-manager.js');
872
929
  await unregisterRepo(entry.path);
@@ -1251,9 +1308,9 @@ export const createServer = async (port, host = '127.0.0.1') => {
1251
1308
  });
1252
1309
  // ── Analyze API ──────────────────────────────────────────────────────
1253
1310
  // POST /api/analyze — start a new analysis job
1254
- app.post('/api/analyze', createRouteLimiter({ limit: 10 }), async (req, res) => {
1311
+ app.post('/api/analyze', createRouteLimiter({ limit: 10 }), requireLocalhostOrigin, async (req, res) => {
1255
1312
  try {
1256
- const { url: repoUrl, path: repoLocalPath, force, embeddings, dropEmbeddings } = req.body;
1313
+ const { url: repoUrl, path: repoLocalPath, force, embeddings, dropEmbeddings, token: repoToken, } = req.body;
1257
1314
  // Input type validation
1258
1315
  if (repoUrl !== undefined && typeof repoUrl !== 'string') {
1259
1316
  res.status(400).json({ error: '"url" must be a string' });
@@ -1267,21 +1324,43 @@ export const createServer = async (port, host = '127.0.0.1') => {
1267
1324
  res.status(400).json({ error: 'Provide "url" (git URL) or "path" (local path)' });
1268
1325
  return;
1269
1326
  }
1270
- // Path validation: require absolute path, reject traversal (e.g. /tmp/../etc/passwd)
1271
- if (repoLocalPath) {
1272
- if (!path.isAbsolute(repoLocalPath)) {
1273
- res.status(400).json({ error: '"path" must be an absolute path' });
1274
- return;
1275
- }
1276
- if (path.normalize(repoLocalPath) !== path.resolve(repoLocalPath)) {
1277
- res.status(400).json({ error: '"path" must not contain traversal sequences' });
1278
- return;
1279
- }
1327
+ // Token: optional, restricted charset to prevent header smuggling
1328
+ // (CRLF), bound length, and bound to github.com (see validateAnalyzeToken).
1329
+ const tokenError = validateAnalyzeToken(repoToken, repoUrl);
1330
+ if (tokenError) {
1331
+ res.status(tokenError.status).json({ error: tokenError.error });
1332
+ return;
1333
+ }
1334
+ // Path validation. The previous `normalize !== resolve` guard was inert
1335
+ // (both collapse `..` identically) and only false-rejected trailing
1336
+ // slashes, so it is dropped. Analyzing a local path the operator names
1337
+ // is the tool's intended capability (same as the CLI); the dangerous
1338
+ // part was cross-origin reach, which is closed by requireLocalhostOrigin
1339
+ // on this route (scoped to the server's own bound host — other LAN
1340
+ // devices are NOT trusted). We only require an absolute path here and
1341
+ // let the analyze worker surface a clear error if it does not exist.
1342
+ // (We do NOT realpath/stat the path in-route: that would be a
1343
+ // user-controlled filesystem read — CodeQL js/path-injection — for no
1344
+ // security gain.)
1345
+ if (repoLocalPath && !path.isAbsolute(repoLocalPath)) {
1346
+ res.status(400).json({ error: '"path" must be an absolute path' });
1347
+ return;
1280
1348
  }
1281
1349
  const job = jobManager.createJob({ repoUrl, repoPath: repoLocalPath });
1282
- // If job was already running (dedup), just return its id
1350
+ // If job was already running (dedup), just return its id. The token is
1351
+ // not part of the dedup identity and is never stored on the job, so a
1352
+ // token on THIS request had no effect — the existing job already
1353
+ // cloned (or is cloning) with whatever credentials its originating
1354
+ // request supplied. Surface `tokenIgnored` so an authenticated caller
1355
+ // isn't misled into thinking their PAT took effect on a reused job.
1283
1356
  if (job.status !== 'queued') {
1284
- res.status(202).json({ jobId: job.id, status: job.status });
1357
+ const body = {
1358
+ jobId: job.id,
1359
+ status: job.status,
1360
+ };
1361
+ if (repoToken !== undefined)
1362
+ body.tokenIgnored = true;
1363
+ res.status(202).json(body);
1285
1364
  return;
1286
1365
  }
1287
1366
  // Mark as active synchronously to prevent race with concurrent requests
@@ -1303,138 +1382,12 @@ export const createServer = async (port, host = '127.0.0.1') => {
1303
1382
  jobManager.updateJob(job.id, {
1304
1383
  progress: { phase: progress.phase, percent: 5, message: progress.message },
1305
1384
  });
1306
- });
1385
+ }, repoToken ? { token: repoToken } : undefined);
1307
1386
  }
1308
1387
  if (!targetPath) {
1309
1388
  throw new Error('No target path resolved');
1310
1389
  }
1311
- // Acquire shared repo lock (keyed on storagePath to match embed handler)
1312
- const analyzeLockKey = getStoragePath(targetPath);
1313
- const lockErr = acquireRepoLock(analyzeLockKey);
1314
- if (lockErr) {
1315
- jobManager.updateJob(job.id, { status: 'failed', error: lockErr });
1316
- return;
1317
- }
1318
- jobManager.updateJob(job.id, { repoPath: targetPath, status: 'analyzing' });
1319
- // ── Worker fork with auto-retry ──────────────────────────────
1320
- //
1321
- // Forks a child process with 8GB heap. If the worker crashes
1322
- // (OOM, native addon segfault, etc.), it retries up to
1323
- // MAX_WORKER_RETRIES times with exponential backoff before
1324
- // marking the job as permanently failed.
1325
- //
1326
- // In dev mode (tsx), registers the tsx ESM hook via a file://
1327
- // URL so the child can compile TypeScript on-the-fly.
1328
- const MAX_WORKER_RETRIES = 2;
1329
- const callerPath = fileURLToPath(import.meta.url);
1330
- const isDev = callerPath.endsWith('.ts');
1331
- const workerFile = isDev ? 'analyze-worker.ts' : 'analyze-worker.js';
1332
- const workerPath = path.join(path.dirname(callerPath), workerFile);
1333
- const tsxHookArgs = isDev
1334
- ? ['--import', pathToFileURL(_require.resolve('tsx/esm')).href]
1335
- : [];
1336
- const forkWorker = () => {
1337
- const currentJob = jobManager.getJob(job.id);
1338
- if (!currentJob || currentJob.status === 'complete' || currentJob.status === 'failed')
1339
- return;
1340
- const child = fork(workerPath, [], {
1341
- execArgv: [...tsxHookArgs, '--max-old-space-size=8192'],
1342
- stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
1343
- });
1344
- // Capture stderr for crash diagnostics
1345
- let stderrChunks = '';
1346
- child.stderr?.on('data', (chunk) => {
1347
- stderrChunks += chunk.toString();
1348
- if (stderrChunks.length > 4096)
1349
- stderrChunks = stderrChunks.slice(-4096);
1350
- });
1351
- child.on('message', (msg) => {
1352
- if (msg.type === 'progress') {
1353
- jobManager.updateJob(job.id, {
1354
- status: 'analyzing',
1355
- progress: { phase: msg.phase, percent: msg.percent, message: msg.message },
1356
- });
1357
- }
1358
- else if (msg.type === 'complete') {
1359
- releaseRepoLock(analyzeLockKey);
1360
- // Reinitialize backend BEFORE marking complete — ensures the new
1361
- // repo is queryable when the client receives the SSE complete event.
1362
- backend
1363
- .init()
1364
- .then(() => {
1365
- jobManager.updateJob(job.id, {
1366
- status: 'complete',
1367
- repoName: msg.result.repoName,
1368
- });
1369
- })
1370
- .catch((err) => {
1371
- logger.error({ err }, 'backend.init() failed after analyze:');
1372
- jobManager.updateJob(job.id, {
1373
- status: 'failed',
1374
- error: 'Server failed to reload after analysis. Try again.',
1375
- });
1376
- });
1377
- }
1378
- else if (msg.type === 'error') {
1379
- releaseRepoLock(analyzeLockKey);
1380
- jobManager.updateJob(job.id, {
1381
- status: 'failed',
1382
- error: msg.message,
1383
- });
1384
- }
1385
- });
1386
- child.on('error', (err) => {
1387
- releaseRepoLock(analyzeLockKey);
1388
- jobManager.updateJob(job.id, {
1389
- status: 'failed',
1390
- error: `Worker process error: ${err.message}`,
1391
- });
1392
- });
1393
- child.on('exit', (code) => {
1394
- const j = jobManager.getJob(job.id);
1395
- if (!j || j.status === 'complete' || j.status === 'failed')
1396
- return;
1397
- // Worker crashed — attempt retry if under the limit
1398
- if (j.retryCount < MAX_WORKER_RETRIES) {
1399
- j.retryCount++;
1400
- const delay = 1000 * Math.pow(2, j.retryCount - 1); // 1s, 2s
1401
- const lastErr = stderrChunks.trim().split('\n').pop() || '';
1402
- logger.warn(`Analyze worker crashed (code ${code}), retry ${j.retryCount}/${MAX_WORKER_RETRIES} in ${delay}ms` +
1403
- (lastErr ? `: ${lastErr}` : ''));
1404
- jobManager.updateJob(job.id, {
1405
- status: 'analyzing',
1406
- progress: {
1407
- phase: 'retrying',
1408
- percent: j.progress.percent,
1409
- message: `Worker crashed, retrying (${j.retryCount}/${MAX_WORKER_RETRIES})...`,
1410
- },
1411
- });
1412
- stderrChunks = '';
1413
- setTimeout(forkWorker, delay);
1414
- }
1415
- else {
1416
- // Exhausted retries — permanent failure
1417
- releaseRepoLock(analyzeLockKey);
1418
- jobManager.updateJob(job.id, {
1419
- status: 'failed',
1420
- error: `Worker crashed ${MAX_WORKER_RETRIES + 1} times (code ${code})${stderrChunks ? ': ' + stderrChunks.trim().split('\n').pop() : ''}`,
1421
- });
1422
- }
1423
- });
1424
- // Register child for cancellation + timeout tracking
1425
- jobManager.registerChild(job.id, child);
1426
- // Send start command to child
1427
- child.send({
1428
- type: 'start',
1429
- repoPath: targetPath,
1430
- options: {
1431
- force: !!force,
1432
- embeddings: !!embeddings,
1433
- dropEmbeddings: !!dropEmbeddings,
1434
- },
1435
- });
1436
- };
1437
- forkWorker();
1390
+ launchAnalysisWorker(job, targetPath, { force, embeddings, dropEmbeddings });
1438
1391
  }
1439
1392
  catch (err) {
1440
1393
  if (targetPath)
@@ -1456,6 +1409,15 @@ export const createServer = async (port, host = '127.0.0.1') => {
1456
1409
  }
1457
1410
  }
1458
1411
  });
1412
+ // POST /api/analyze/upload — analyze a browser folder upload.
1413
+ // Securely ingests the multipart upload into a sandbox, promotes it to a
1414
+ // persistent dir, and analyzes it via the shared job/worker machinery.
1415
+ // localhost-only (no cross-origin write reach) + conservative rate limit.
1416
+ app.post('/api/analyze/upload', createRouteLimiter({ limit: 5 }), requireLocalhostOrigin, createAnalyzeUploadHandler({
1417
+ createJob: (params) => jobManager.createJob(params),
1418
+ launch: (job, targetPath, opts) => launchAnalysisWorker(job, targetPath, opts),
1419
+ failJob: (jobId, error) => jobManager.updateJob(jobId, { status: 'failed', error }),
1420
+ }));
1459
1421
  // GET /api/analyze/:jobId — poll job status
1460
1422
  app.get('/api/analyze/:jobId', (req, res) => {
1461
1423
  const job = jobManager.getJob(req.params.jobId);
@@ -1478,8 +1440,9 @@ export const createServer = async (port, host = '127.0.0.1') => {
1478
1440
  // GET /api/analyze/:jobId/progress — SSE stream (shared helper)
1479
1441
  mountSSEProgress(app, '/api/analyze/:jobId/progress', jobManager);
1480
1442
  // DELETE /api/analyze/:jobId — cancel a running analysis job
1481
- app.delete('/api/analyze/:jobId', (req, res) => {
1482
- const job = jobManager.getJob(req.params.jobId);
1443
+ app.delete('/api/analyze/:jobId', requireLocalhostOrigin, (req, res) => {
1444
+ const jobId = req.params.jobId;
1445
+ const job = jobManager.getJob(jobId);
1483
1446
  if (!job) {
1484
1447
  res.status(404).json({ error: 'Job not found' });
1485
1448
  return;
@@ -1488,13 +1451,13 @@ export const createServer = async (port, host = '127.0.0.1') => {
1488
1451
  res.status(400).json({ error: `Job already ${job.status}` });
1489
1452
  return;
1490
1453
  }
1491
- jobManager.cancelJob(req.params.jobId, 'Cancelled by user');
1454
+ jobManager.cancelJob(jobId, 'Cancelled by user');
1492
1455
  res.json({ id: job.id, status: 'failed', error: 'Cancelled by user' });
1493
1456
  });
1494
1457
  // ── Embedding endpoints ────────────────────────────────────────────
1495
1458
  const embedJobManager = new JobManager();
1496
1459
  // POST /api/embed — trigger server-side embedding generation
1497
- app.post('/api/embed', createRouteLimiter({ limit: 20 }), async (req, res) => {
1460
+ app.post('/api/embed', createRouteLimiter({ limit: 20 }), requireLocalhostOrigin, async (req, res) => {
1498
1461
  try {
1499
1462
  const entry = await resolveRepo(requestedRepo(req));
1500
1463
  if (!entry) {
@@ -1616,8 +1579,9 @@ export const createServer = async (port, host = '127.0.0.1') => {
1616
1579
  // GET /api/embed/:jobId/progress — SSE stream (shared helper)
1617
1580
  mountSSEProgress(app, '/api/embed/:jobId/progress', embedJobManager);
1618
1581
  // DELETE /api/embed/:jobId — cancel embedding job
1619
- app.delete('/api/embed/:jobId', (req, res) => {
1620
- const job = embedJobManager.getJob(req.params.jobId);
1582
+ app.delete('/api/embed/:jobId', requireLocalhostOrigin, (req, res) => {
1583
+ const jobId = req.params.jobId;
1584
+ const job = embedJobManager.getJob(jobId);
1621
1585
  if (!job) {
1622
1586
  res.status(404).json({ error: 'Job not found' });
1623
1587
  return;
@@ -1626,7 +1590,7 @@ export const createServer = async (port, host = '127.0.0.1') => {
1626
1590
  res.status(400).json({ error: `Job already ${job.status}` });
1627
1591
  return;
1628
1592
  }
1629
- embedJobManager.cancelJob(req.params.jobId, 'Cancelled by user');
1593
+ embedJobManager.cancelJob(jobId, 'Cancelled by user');
1630
1594
  res.json({ id: job.id, status: 'failed', error: 'Cancelled by user' });
1631
1595
  });
1632
1596
  // ── Web UI (served at root) ───────────────────────────────────────
@@ -1,9 +1,10 @@
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
+ export declare const REPO_NAME_PATTERN: RegExp;
7
8
  /**
8
9
  * Extract the repository name from a git URL (HTTPS or SSH).
9
10
  *
@@ -34,6 +35,25 @@ export interface CloneProgress {
34
35
  *
35
36
  * Exported so the separator placement is testable without mocking spawn.
36
37
  */
38
+ /**
39
+ * Detect Azure DevOps URLs — both self-hosted (via AZURE_DEVOPS_URL env)
40
+ * and cloud (dev.azure.com / *.visualstudio.com).
41
+ *
42
+ * Self-hosted Azure DevOps Server instances use arbitrary hostnames
43
+ * (e.g. `http://tfs.corp.example/Collection/Project/_git/Repo`), so the
44
+ * function checks `AZURE_DEVOPS_URL` first. Cloud addresses are a
45
+ * hardcoded fallback so PAT injection works out-of-the-box for
46
+ * dev.azure.com without extra configuration.
47
+ */
48
+ export declare function isAzureDevOpsUrl(url: string): boolean;
49
+ /**
50
+ * One-time startup warning when AZURE_DEVOPS_URL is configured over cleartext
51
+ * http:// — the Azure DevOps PAT would then be sent unencrypted on every
52
+ * clone. Self-hosted instances that only serve http are still supported (we
53
+ * do not refuse), but operators rarely read request-time logs, so surface it
54
+ * at boot too. Call once from server startup.
55
+ */
56
+ export declare function warnIfInsecureAzureConfig(): void;
37
57
  export declare function buildCloneArgs(url: string, targetDir: string): string[];
38
58
  /**
39
59
  * Normalize a git URL into a comparable form.
@@ -96,4 +116,25 @@ export declare function assertRemoteMatchesRequestedUrl(targetDir: string, reque
96
116
  * `--` (e.g. `--upload-pack=evil`) cannot be interpreted as a git option
97
117
  * (CodeQL js/second-order-command-line-injection).
98
118
  */
99
- export declare function cloneOrPull(url: string, targetDir: string, onProgress?: (progress: CloneProgress) => void): Promise<string>;
119
+ export declare function cloneOrPull(url: string, targetDir: string, onProgress?: (progress: CloneProgress) => void, options?: {
120
+ token?: string;
121
+ }): Promise<string>;
122
+ /**
123
+ * Hosts the per-request GitHub PAT may be sent to. Exported so the
124
+ * /api/analyze boundary check and this injection-site check share one
125
+ * allowlist (they must agree, or a token accepted by the API could be
126
+ * silently dropped — or worse — at injection).
127
+ */
128
+ export declare const GITHUB_TOKEN_HOSTS: ReadonlySet<string>;
129
+ /**
130
+ * Build the spawn env for `git`. Suppresses credential prompts and, when a
131
+ * credential resolves (see resolveGitCredential), injects a single
132
+ * host-scoped Authorization header via the `GIT_CONFIG_*` env protocol
133
+ * (git ≥2.31) so credentials never appear in argv or the URL. Appends after
134
+ * any existing `GIT_CONFIG_COUNT` rather than overwriting it. Exported for
135
+ * unit tests.
136
+ */
137
+ export declare function buildGitEnv(baseEnv: NodeJS.ProcessEnv, options?: {
138
+ token?: string;
139
+ url?: string;
140
+ }): NodeJS.ProcessEnv;