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.
- package/README.md +3 -3
- package/dist/_shared/graph/types.d.ts +31 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +11 -1
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +6 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/symbol-definition.d.ts +6 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
- package/dist/cli/ai-context.d.ts +31 -1
- package/dist/cli/ai-context.js +18 -16
- package/dist/cli/analyze-config.js +11 -0
- package/dist/cli/analyze.d.ts +14 -0
- package/dist/cli/analyze.js +98 -0
- package/dist/cli/embedding-dims.d.ts +33 -0
- package/dist/cli/embedding-dims.js +39 -0
- package/dist/cli/eval-server.d.ts +8 -0
- package/dist/cli/eval-server.js +242 -2
- package/dist/cli/help-i18n.js +19 -0
- package/dist/cli/i18n/en.d.ts +15 -1
- package/dist/cli/i18n/en.js +15 -1
- package/dist/cli/i18n/resources.d.ts +29 -1
- package/dist/cli/i18n/zh-CN.d.ts +14 -0
- package/dist/cli/i18n/zh-CN.js +15 -1
- package/dist/cli/index.js +89 -1
- package/dist/cli/mcp.d.ts +6 -1
- package/dist/cli/mcp.js +26 -1
- package/dist/cli/optional-grammars.d.ts +6 -8
- package/dist/cli/optional-grammars.js +8 -11
- package/dist/cli/setup.d.ts +3 -1
- package/dist/cli/setup.js +67 -17
- package/dist/cli/skill-gen.js +2 -1
- package/dist/cli/tool.d.ts +19 -1
- package/dist/cli/tool.js +96 -3
- package/dist/core/embeddings/hf-env.d.ts +0 -66
- package/dist/core/embeddings/http-client.d.ts +7 -0
- package/dist/core/embeddings/http-client.js +5 -3
- package/dist/core/embeddings/server-mapping.d.ts +1 -1
- package/dist/core/embeddings/server-mapping.js +7 -3
- package/dist/core/graph/import-cycles.d.ts +10 -0
- package/dist/core/graph/import-cycles.js +103 -0
- package/dist/core/group/cross-impact.d.ts +2 -1
- package/dist/core/group/cross-impact.js +5 -1
- package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
- package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
- package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
- package/dist/core/group/extractors/http-route-extractor.js +22 -4
- package/dist/core/group/extractors/include-extractor.js +22 -13
- package/dist/core/group/sync.d.ts +27 -1
- package/dist/core/group/sync.js +0 -0
- package/dist/core/incremental/subgraph-extract.js +18 -1
- package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
- package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
- package/dist/core/ingestion/cfg/collect.d.ts +51 -0
- package/dist/core/ingestion/cfg/collect.js +77 -0
- package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
- package/dist/core/ingestion/cfg/control-dependence.js +182 -0
- package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
- package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
- package/dist/core/ingestion/cfg/emit.d.ts +293 -0
- package/dist/core/ingestion/cfg/emit.js +646 -0
- package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
- package/dist/core/ingestion/cfg/post-dominators.js +176 -0
- package/dist/core/ingestion/cfg/reaching-def-reason-codec.d.ts +106 -0
- package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
- package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
- package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
- package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
- package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
- package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
- package/dist/core/ingestion/cfg/synthetic-escape.js +300 -0
- package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
- package/dist/core/ingestion/cfg/traversal-result.js +2 -0
- package/dist/core/ingestion/cfg/types.d.ts +274 -0
- package/dist/core/ingestion/cfg/types.js +13 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
- package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
- package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
- package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
- package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
- package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
- package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
- package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
- package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
- package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
- package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
- package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
- package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
- package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
- package/dist/core/ingestion/cfg/visitors/go.js +638 -0
- package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
- package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
- package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
- package/dist/core/ingestion/cfg/visitors/java.js +812 -0
- package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
- package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
- package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
- package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
- package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
- package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
- package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
- package/dist/core/ingestion/cfg/visitors/php.js +721 -0
- package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
- package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
- package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
- package/dist/core/ingestion/cfg/visitors/python.js +558 -0
- package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
- package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
- package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
- package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
- package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
- package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
- package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
- package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
- package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
- package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
- package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
- package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
- package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
- package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
- package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
- package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
- package/dist/core/ingestion/import-resolvers/utils.js +2 -0
- package/dist/core/ingestion/language-provider.d.ts +11 -0
- package/dist/core/ingestion/languages/c-cpp.js +6 -1
- package/dist/core/ingestion/languages/cobol.js +4 -0
- package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
- package/dist/core/ingestion/languages/cpp/captures.js +50 -1
- package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
- package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
- package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
- package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
- package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
- package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
- package/dist/core/ingestion/languages/cpp/query.js +23 -0
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
- package/dist/core/ingestion/languages/csharp.js +2 -0
- package/dist/core/ingestion/languages/dart.js +2 -0
- package/dist/core/ingestion/languages/go.js +2 -0
- package/dist/core/ingestion/languages/java.js +3 -0
- package/dist/core/ingestion/languages/kotlin/query.js +3 -2
- package/dist/core/ingestion/languages/kotlin.js +3 -0
- package/dist/core/ingestion/languages/php.js +2 -0
- package/dist/core/ingestion/languages/python.js +2 -0
- package/dist/core/ingestion/languages/ruby.js +2 -0
- package/dist/core/ingestion/languages/rust.js +2 -0
- package/dist/core/ingestion/languages/swift.js +2 -0
- package/dist/core/ingestion/languages/typescript.js +5 -0
- package/dist/core/ingestion/languages/vue.js +6 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
- package/dist/core/ingestion/method-extractors/generic.js +1 -0
- package/dist/core/ingestion/method-types.d.ts +2 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
- package/dist/core/ingestion/model/symbol-table.js +1 -0
- package/dist/core/ingestion/mro-processor.js +109 -0
- package/dist/core/ingestion/parsing-processor.js +23 -0
- package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
- package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
- package/dist/core/ingestion/pipeline-phases/index.js +2 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
- package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
- package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
- package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
- package/dist/core/ingestion/pipeline.d.ts +92 -10
- package/dist/core/ingestion/pipeline.js +11 -2
- package/dist/core/ingestion/scope-extractor.js +12 -2
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
- package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
- package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
- package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
- package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
- package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
- package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
- package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
- package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
- package/dist/core/ingestion/taint/call-summary-model.js +25 -0
- package/dist/core/ingestion/taint/emit.d.ts +125 -0
- package/dist/core/ingestion/taint/emit.js +208 -0
- package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
- package/dist/core/ingestion/taint/interproc-emit.js +88 -0
- package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
- package/dist/core/ingestion/taint/interproc-solver.js +312 -0
- package/dist/core/ingestion/taint/match.d.ts +153 -0
- package/dist/core/ingestion/taint/match.js +278 -0
- package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
- package/dist/core/ingestion/taint/path-codec.js +190 -0
- package/dist/core/ingestion/taint/propagate.d.ts +216 -0
- package/dist/core/ingestion/taint/propagate.js +659 -0
- package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
- package/dist/core/ingestion/taint/site-safety.js +98 -0
- package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
- package/dist/core/ingestion/taint/source-sink-config.js +35 -12
- package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
- package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
- package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +80 -0
- package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
- package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
- package/dist/core/ingestion/taint/summary-harvest.js +537 -0
- package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
- package/dist/core/ingestion/taint/summary-model.js +86 -0
- package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
- package/dist/core/ingestion/taint/typescript-model.js +102 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +21 -0
- package/dist/core/ingestion/utils/env.d.ts +7 -0
- package/dist/core/ingestion/utils/env.js +12 -0
- package/dist/core/ingestion/utils/method-props.js +1 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +13 -0
- package/dist/core/ingestion/workers/parse-worker.js +76 -17
- package/dist/core/ingestion/workers/worker-pool.d.ts +9 -40
- package/dist/core/ingestion/workers/worker-pool.js +15 -2
- package/dist/core/lbug/csv-generator.d.ts +51 -3
- package/dist/core/lbug/csv-generator.js +369 -255
- package/dist/core/lbug/lbug-adapter.d.ts +58 -18
- package/dist/core/lbug/lbug-adapter.js +346 -84
- package/dist/core/lbug/lbug-config.d.ts +19 -0
- package/dist/core/lbug/lbug-config.js +29 -0
- package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
- package/dist/core/lbug/pdg-emit-sink.js +358 -0
- package/dist/core/lbug/pool-adapter.d.ts +35 -0
- package/dist/core/lbug/pool-adapter.js +90 -1
- package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
- package/dist/core/lbug/rel-pair-routing.js +140 -0
- package/dist/core/lbug/schema.d.ts +2 -2
- package/dist/core/lbug/schema.js +3 -0
- package/dist/core/logger.d.ts +0 -5
- package/dist/core/run-analyze.d.ts +96 -0
- package/dist/core/run-analyze.js +207 -3
- package/dist/core/tree-sitter/parser-loader.js +5 -4
- package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
- package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
- package/dist/core/tree-sitter/vendored-grammars.js +57 -0
- package/dist/core/wiki/generator.js +3 -1
- package/dist/core/wiki/graph-queries.d.ts +6 -0
- package/dist/core/wiki/graph-queries.js +9 -1
- package/dist/mcp/http-transport.d.ts +134 -0
- package/dist/mcp/http-transport.js +503 -0
- package/dist/mcp/local/local-backend.d.ts +110 -24
- package/dist/mcp/local/local-backend.js +1225 -19
- package/dist/mcp/local/pdg-impact.d.ts +384 -0
- package/dist/mcp/local/pdg-impact.js +1972 -0
- package/dist/mcp/resources.js +13 -1
- package/dist/mcp/tools.d.ts +12 -0
- package/dist/mcp/tools.js +214 -7
- package/dist/server/analyze-launch.d.ts +29 -0
- package/dist/server/analyze-launch.js +137 -0
- package/dist/server/analyze-upload.d.ts +33 -0
- package/dist/server/analyze-upload.js +123 -0
- package/dist/server/analyze-worker.d.ts +17 -1
- package/dist/server/api.d.ts +17 -0
- package/dist/server/api.js +132 -168
- package/dist/server/git-clone.d.ts +43 -2
- package/dist/server/git-clone.js +211 -16
- package/dist/server/mcp-http.d.ts +5 -6
- package/dist/server/mcp-http.js +9 -82
- package/dist/server/middleware.d.ts +40 -0
- package/dist/server/middleware.js +92 -0
- package/dist/server/private-ip.d.ts +1 -0
- package/dist/server/private-ip.js +15 -0
- package/dist/server/upload-ingest.d.ts +56 -0
- package/dist/server/upload-ingest.js +276 -0
- package/dist/server/upload-paths.d.ts +37 -0
- package/dist/server/upload-paths.js +57 -0
- package/dist/server/upload-sweep.d.ts +20 -0
- package/dist/server/upload-sweep.js +57 -0
- package/dist/storage/git.js +5 -8
- package/dist/storage/parse-cache.d.ts +22 -1
- package/dist/storage/parse-cache.js +41 -10
- package/dist/storage/repo-manager.d.ts +121 -10
- package/dist/storage/repo-manager.js +43 -5
- package/dist/types/pipeline.d.ts +9 -0
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
- package/hooks/claude/gitnexus-hook.cjs +110 -13
- package/hooks/claude/hook-db-lock-probe.cjs +513 -26
- package/package.json +8 -2
- package/scripts/assert-publish-grammar-coverage.cjs +31 -0
- package/scripts/build-tree-sitter-grammars.cjs +16 -10
- package/scripts/cross-platform-tests.ts +1 -0
- package/skills/gitnexus-debugging.md +16 -4
- package/skills/gitnexus-exploring.md +3 -3
- package/skills/gitnexus-guide.md +33 -0
- package/skills/gitnexus-pdg-query.md +89 -0
- package/skills/gitnexus-refactoring.md +1 -1
- package/skills/gitnexus-taint-analysis.md +178 -0
- package/vendor/tree-sitter-c/package.json +1 -1
- package/vendor/tree-sitter-dart/package.json +1 -1
- package/vendor/tree-sitter-kotlin/package.json +1 -1
- package/vendor/tree-sitter-proto/package.json +1 -1
- package/vendor/tree-sitter-swift/package.json +1 -1
- package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
- package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
- package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
- package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
- package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
- package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
- package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
- package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
- package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
- package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
- package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
- package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
- package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
- package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
- package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
- package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
- package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
- package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
- package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
- package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
- package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
- package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
- package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
- package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
- package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
- package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
- package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
- package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
- package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
- package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
- package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
- package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
- package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
- package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
- package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
- package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
- package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
- package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
- package/web/assets/index-BvYev1UJ.js +635 -0
- package/web/assets/index-_lgn7hs5.css +2 -0
- package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
- package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
- package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
- package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
- package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
- package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
- package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
- package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
- package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
- package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
- package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
- package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
- package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
- package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
- package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
- package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
- package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
- package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
- package/web/index.html +13 -15
- package/scripts/materialize-vendor-grammars.cjs +0 -97
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
- package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
- package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
- package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
- package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
- package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
- package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
- package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
- package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
- package/web/assets/chunk-CilyBKbf.js +0 -1
- package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
- package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
- package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
- package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
- package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
- package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
- package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
- package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
- package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
- package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
- package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
- package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
- package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
- package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
- package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
- package/web/assets/index-BKWA-m7o.css +0 -2
- package/web/assets/index-CG6q8eTs.js +0 -626
- package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
- package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
- package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
- package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
- package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
- /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
package/dist/server/git-clone.js
CHANGED
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Git Clone Utility
|
|
3
3
|
*
|
|
4
|
-
* Shallow-clones repositories into
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
*
|
|
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';
|
package/dist/server/mcp-http.js
CHANGED
|
@@ -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
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
*
|
|
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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
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>;
|