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
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interprocedural taint fixpoint (#2084 M4 U3).
|
|
3
|
+
*
|
|
4
|
+
* Composes per-function {@link FunctionSummary} objects over the resolved
|
|
5
|
+
* `CALLS` graph to find source→sink flows that cross function and file
|
|
6
|
+
* boundaries. PURE AND DETERMINISTIC (no graph, no I/O, no logger) — the phase
|
|
7
|
+
* builds the inputs from `ctx.graph` and persists the outputs.
|
|
8
|
+
*
|
|
9
|
+
* ## The model — whole-parameter taint reachability
|
|
10
|
+
*
|
|
11
|
+
* The unit of taint is `(function, parameter)`. The fixpoint computes the set
|
|
12
|
+
* of parameters that can hold source-derived data, then fires a finding
|
|
13
|
+
* whenever a tainted parameter feeds a modelled sink (`paramToSink`).
|
|
14
|
+
*
|
|
15
|
+
* - **Seeds** — every `sourceToCallArg` edge: a function generates a source and
|
|
16
|
+
* passes it into argument `argIndex` of a call at `callLine`. Resolving that
|
|
17
|
+
* call site against the caller's outgoing `CALLS` edges yields the callee;
|
|
18
|
+
* the callee's parameter `argIndex` becomes tainted, with the generating
|
|
19
|
+
* function recorded as the flow's source.
|
|
20
|
+
* - **Propagation** — every `paramToCallArg` edge of a function whose parameter
|
|
21
|
+
* is ALREADY tainted: `param i → arg j of callee` taints the callee's
|
|
22
|
+
* parameter `j` (TITO composition). Iterated to a fixpoint.
|
|
23
|
+
* - **Findings** — whenever a parameter becomes tainted and the owning
|
|
24
|
+
* function's `paramToSink` contains that parameter, a cross-function finding
|
|
25
|
+
* is emitted (source function → sink function, with the kind).
|
|
26
|
+
*
|
|
27
|
+
* ## Cycle safety (recursion)
|
|
28
|
+
*
|
|
29
|
+
* The tainted-parameter set is monotone over a FINITE lattice (`Σ functions ×
|
|
30
|
+
* params`), so the worklist fixpoint converges: a recursive or mutually
|
|
31
|
+
* recursive call merely re-proposes an already-tainted parameter, which the
|
|
32
|
+
* visited-set absorbs — no infinite descent. This is the functional/summary
|
|
33
|
+
* method's standard termination argument (Sharir-Pnueli; Pysa, Mariana Trench,
|
|
34
|
+
* and Infer all rely on it). SCC condensation would only refine the PROCESSING
|
|
35
|
+
* ORDER; correctness and termination do not require it.
|
|
36
|
+
*
|
|
37
|
+
* ## Context-insensitivity & the name-join over-approximation
|
|
38
|
+
*
|
|
39
|
+
* One summary per function, applied at every call site — return/param merging
|
|
40
|
+
* is accepted (the security-conservative direction). The call-arg→callee join
|
|
41
|
+
* is by callee NAME (not line), so when one caller invokes two DISTINCT
|
|
42
|
+
* same-named callees (`x.handler(src)` and `y.handler(clean)`), a source that
|
|
43
|
+
* flowed into ONE of them taints BOTH callees' parameter — an extra finding on
|
|
44
|
+
* the callee the source did not reach. This is sound (over-attribution, never a
|
|
45
|
+
* missed flow — the conservative direction for a security tool) and is the
|
|
46
|
+
* documented price of dropping the fragile line-based join; the `explain` tool
|
|
47
|
+
* surfaces it ("may over-attribute among same-named callees"). Other known
|
|
48
|
+
* precision losses (call-site conflation, shared dispatch, callbacks) are the
|
|
49
|
+
* documented M4 trade-offs; refinements are deferred (plan KTD).
|
|
50
|
+
*/
|
|
51
|
+
export const DEFAULT_MAX_INTERPROC_HOPS = 32;
|
|
52
|
+
/**
|
|
53
|
+
* Default per-run cap on cross-function findings (#2084 review P1-3). Like the
|
|
54
|
+
* other pdg caps it is resolved into `RepoMeta.pdg` so `pdgModeMismatch`
|
|
55
|
+
* stamps it; `0` ⇒ unlimited. 2000 is generous for a real repo — more deduped
|
|
56
|
+
* `(source, sink, kind)` findings than that is a fixture or a runaway fan-in,
|
|
57
|
+
* and the overflow is deterministic + counted (`droppedFindings`).
|
|
58
|
+
*/
|
|
59
|
+
export const DEFAULT_PDG_MAX_INTERPROC_FINDINGS = 2000;
|
|
60
|
+
/**
|
|
61
|
+
* Taint-state key — `(function, parameter, SOURCE)`. The source discriminator
|
|
62
|
+
* is load-bearing: without it, a parameter tainted by source A is marked
|
|
63
|
+
* visited and a later flow from source B to the SAME parameter is dropped
|
|
64
|
+
* before it can fire that function's sink, silently losing B→sink (the
|
|
65
|
+
* multi-source collapse — the recurring M3 bug class). Including the source
|
|
66
|
+
* keeps each origin's flow independent; the lattice stays finite (`fn × param ×
|
|
67
|
+
* source`), so the monotone worklist still terminates and is cycle-safe.
|
|
68
|
+
*/
|
|
69
|
+
const pkey = (fnId, param, sourceFnId) => `${fnId}#${param}#${sourceFnId}`;
|
|
70
|
+
/**
|
|
71
|
+
* Run the interprocedural taint fixpoint. `summaries` is keyed by function node
|
|
72
|
+
* id; `callEdges` is the resolved `CALLS` graph (caller→callee with call-site
|
|
73
|
+
* lines). Deterministic: inputs in, sorted findings out.
|
|
74
|
+
*/
|
|
75
|
+
export function solveInterprocTaint(summaries, callEdges, limits) {
|
|
76
|
+
const maxHops = limits?.maxHops && limits.maxHops > 0 ? limits.maxHops : DEFAULT_MAX_INTERPROC_HOPS;
|
|
77
|
+
// Adjacency built ONCE (#2084 review P3-8): callerId → outgoing edges, AND
|
|
78
|
+
// callerId → calleeName → edges. The summary's call-arg edges resolve by
|
|
79
|
+
// callee NAME, so the per-name index turns each resolution into an O(1)
|
|
80
|
+
// lookup instead of a per-worklist-step `.filter` allocation (the
|
|
81
|
+
// build-index-once pattern).
|
|
82
|
+
const callsByCaller = new Map();
|
|
83
|
+
const callsByCallerName = new Map();
|
|
84
|
+
for (const e of callEdges) {
|
|
85
|
+
const list = callsByCaller.get(e.callerId);
|
|
86
|
+
if (list)
|
|
87
|
+
list.push(e);
|
|
88
|
+
else
|
|
89
|
+
callsByCaller.set(e.callerId, [e]);
|
|
90
|
+
let byName = callsByCallerName.get(e.callerId);
|
|
91
|
+
if (!byName) {
|
|
92
|
+
byName = new Map();
|
|
93
|
+
callsByCallerName.set(e.callerId, byName);
|
|
94
|
+
}
|
|
95
|
+
const named = byName.get(e.calleeName);
|
|
96
|
+
if (named)
|
|
97
|
+
named.push(e);
|
|
98
|
+
else
|
|
99
|
+
byName.set(e.calleeName, [e]);
|
|
100
|
+
}
|
|
101
|
+
let unmatchedCallSites = 0;
|
|
102
|
+
/** Edges to `name` from `callerId` (O(1)); empty if none — non-counting. */
|
|
103
|
+
const calleesByName = (callerId, name) => callsByCallerName.get(callerId)?.get(name) ?? [];
|
|
104
|
+
// Resolve a caller's call-arg edge (by callee name) to concrete callee edges.
|
|
105
|
+
// An unknown callee name (chain not statically resolvable) conservatively
|
|
106
|
+
// matches EVERY outgoing call — sound over-approximation (may over-taint).
|
|
107
|
+
const resolveCallees = (callerId, calleeName) => {
|
|
108
|
+
const candidates = callsByCaller.get(callerId);
|
|
109
|
+
if (!candidates || candidates.length === 0) {
|
|
110
|
+
unmatchedCallSites++;
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
if (calleeName === undefined)
|
|
114
|
+
return candidates;
|
|
115
|
+
const named = calleesByName(callerId, calleeName);
|
|
116
|
+
if (named.length === 0) {
|
|
117
|
+
unmatchedCallSites++;
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
return named;
|
|
121
|
+
};
|
|
122
|
+
// ── findings + worklist ───────────────────────────────────────────────────
|
|
123
|
+
const findingsByKey = new Map();
|
|
124
|
+
const tainted = new Map();
|
|
125
|
+
const queue = [];
|
|
126
|
+
const recordFinding = (sourceFnId, sinkFnId, sinkKind, hops, truncated) => {
|
|
127
|
+
const key = `${sourceFnId}|${sinkFnId}|${sinkKind}`;
|
|
128
|
+
if (findingsByKey.has(key))
|
|
129
|
+
return;
|
|
130
|
+
findingsByKey.set(key, { sourceFnId, sinkFnId, sinkKind, hops, hopsTruncated: truncated });
|
|
131
|
+
};
|
|
132
|
+
/** Fire every `paramToSink` of `tp`'s param, except kinds it neutralised. */
|
|
133
|
+
const fireSinks = (tp) => {
|
|
134
|
+
const summary = summaries.get(tp.fnId);
|
|
135
|
+
if (!summary)
|
|
136
|
+
return;
|
|
137
|
+
for (const ps of summary.paramToSink) {
|
|
138
|
+
if (ps.param !== tp.paramIndex)
|
|
139
|
+
continue;
|
|
140
|
+
if (tp.neutralized.has(ps.sinkKind))
|
|
141
|
+
continue; // sanitised across the boundary (P1-2)
|
|
142
|
+
// `tp.hops` already terminates at this (tainted) function — it IS the
|
|
143
|
+
// source→sink chain, no extra hop to append.
|
|
144
|
+
recordFinding(tp.sourceFnId, tp.fnId, ps.sinkKind, tp.hops, tp.truncated);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Mark (fnId, paramIndex, source) tainted; enqueue. On a fresh key, taint +
|
|
149
|
+
* fire sinks. On revisit, INTERSECT the neutralised set (a kind stays
|
|
150
|
+
* neutralised only if EVERY path neutralises it — the sound direction); if it
|
|
151
|
+
* shrank, re-enqueue + re-fire so a less-neutralised path's sinks surface
|
|
152
|
+
* (the shrink-reprocess guard, mirroring `propagate.ts:deriveTaint`). Without
|
|
153
|
+
* it, a first more-neutralised path would freeze out a real finding (FN).
|
|
154
|
+
*/
|
|
155
|
+
const taint = (tp) => {
|
|
156
|
+
const key = pkey(tp.fnId, tp.paramIndex, tp.sourceFnId);
|
|
157
|
+
const existing = tainted.get(key);
|
|
158
|
+
if (existing) {
|
|
159
|
+
const inter = new Set();
|
|
160
|
+
for (const k of existing.neutralized)
|
|
161
|
+
if (tp.neutralized.has(k))
|
|
162
|
+
inter.add(k);
|
|
163
|
+
if (inter.size >= existing.neutralized.size)
|
|
164
|
+
return; // no shrink — cycle-safe
|
|
165
|
+
const merged = { ...existing, neutralized: inter };
|
|
166
|
+
tainted.set(key, merged);
|
|
167
|
+
queue.push(merged);
|
|
168
|
+
fireSinks(merged);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
tainted.set(key, tp);
|
|
172
|
+
queue.push(tp);
|
|
173
|
+
fireSinks(tp);
|
|
174
|
+
};
|
|
175
|
+
// ── seeds: every source→callee-arg, resolved against CALLS ────────────────
|
|
176
|
+
for (const [callerId, summary] of summaries) {
|
|
177
|
+
for (const sc of summary.sourceToCallArg) {
|
|
178
|
+
for (const edge of resolveCallees(callerId, sc.calleeName)) {
|
|
179
|
+
const callee = summaries.get(edge.calleeId);
|
|
180
|
+
if (!callee)
|
|
181
|
+
continue;
|
|
182
|
+
if (sc.argIndex >= callee.paramCount)
|
|
183
|
+
continue; // arity guard
|
|
184
|
+
// Build the seed path through the capped append so `maxHops` truncates
|
|
185
|
+
// the prefix (#2084 review P2-7), not a 2-entry path flagged truncated.
|
|
186
|
+
const seed = appendHop([{ fnId: callerId }], { fnId: edge.calleeId, callLine: sc.callLine, argIndex: sc.argIndex }, maxHops);
|
|
187
|
+
taint({
|
|
188
|
+
fnId: edge.calleeId,
|
|
189
|
+
paramIndex: sc.argIndex,
|
|
190
|
+
sourceFnId: callerId,
|
|
191
|
+
hops: seed.hops,
|
|
192
|
+
truncated: seed.truncated,
|
|
193
|
+
neutralized: new Set(sc.neutralized ?? []),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// ── generative return composition (#2084 review P1-1) ─────────────────────
|
|
199
|
+
// `genReturns` = functions whose RETURN carries a generated source. Seed with
|
|
200
|
+
// `sourceToReturn`; a caller that returns the result of a generative call is
|
|
201
|
+
// itself generative (transitive — `wrap(){ return getInput() }`). Small
|
|
202
|
+
// monotone fixpoint over the name-resolved call graph (`calleesByName`).
|
|
203
|
+
const genReturns = new Set();
|
|
204
|
+
for (const [id, s] of summaries)
|
|
205
|
+
if (s.sourceToReturn.length > 0)
|
|
206
|
+
genReturns.add(id);
|
|
207
|
+
let grChanged = true;
|
|
208
|
+
while (grChanged) {
|
|
209
|
+
grChanged = false;
|
|
210
|
+
for (const [callerId, s] of summaries) {
|
|
211
|
+
if (genReturns.has(callerId))
|
|
212
|
+
continue;
|
|
213
|
+
for (const cr of s.callResults) {
|
|
214
|
+
if (cr.dest.to !== 'return')
|
|
215
|
+
continue;
|
|
216
|
+
if (calleesByName(callerId, cr.calleeName).some((e) => genReturns.has(e.calleeId))) {
|
|
217
|
+
genReturns.add(callerId);
|
|
218
|
+
grChanged = true;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Compose: a caller using a generative call's result either FIRES (the result
|
|
225
|
+
// hits a sink) or SEEDS (the result flows into another call's arg). The
|
|
226
|
+
// generated source's origin is the generative callee.
|
|
227
|
+
for (const [callerId, s] of summaries) {
|
|
228
|
+
for (const cr of s.callResults) {
|
|
229
|
+
const generative = calleesByName(callerId, cr.calleeName).filter((e) => genReturns.has(e.calleeId));
|
|
230
|
+
if (generative.length === 0)
|
|
231
|
+
continue;
|
|
232
|
+
for (const g of generative) {
|
|
233
|
+
const d = cr.dest;
|
|
234
|
+
if (d.to === 'sink') {
|
|
235
|
+
recordFinding(g.calleeId, callerId, d.sinkKind, [{ fnId: g.calleeId }, { fnId: callerId }], 2 > maxHops);
|
|
236
|
+
}
|
|
237
|
+
else if (d.to === 'callArg') {
|
|
238
|
+
for (const tc of d.toCallee === undefined
|
|
239
|
+
? (callsByCaller.get(callerId) ?? [])
|
|
240
|
+
: calleesByName(callerId, d.toCallee)) {
|
|
241
|
+
const callee = summaries.get(tc.calleeId);
|
|
242
|
+
if (!callee || d.argIndex >= callee.paramCount)
|
|
243
|
+
continue;
|
|
244
|
+
// Capped successive append so `maxHops` truncates the prefix (P2-7).
|
|
245
|
+
const h1 = appendHop([{ fnId: g.calleeId }], { fnId: callerId }, maxHops);
|
|
246
|
+
const h2 = appendHop(h1.hops, { fnId: tc.calleeId, argIndex: d.argIndex }, maxHops);
|
|
247
|
+
taint({
|
|
248
|
+
fnId: tc.calleeId,
|
|
249
|
+
paramIndex: d.argIndex,
|
|
250
|
+
sourceFnId: g.calleeId,
|
|
251
|
+
hops: h2.hops,
|
|
252
|
+
truncated: h1.truncated || h2.truncated,
|
|
253
|
+
neutralized: new Set(),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// dest:'return' is already folded into `genReturns` above.
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// ── propagation worklist ──────────────────────────────────────────────────
|
|
262
|
+
let head = 0;
|
|
263
|
+
while (head < queue.length) {
|
|
264
|
+
const tp = queue[head++];
|
|
265
|
+
const summary = summaries.get(tp.fnId);
|
|
266
|
+
if (!summary)
|
|
267
|
+
continue;
|
|
268
|
+
// This function's tainted param flows into callee args via paramToCallArg.
|
|
269
|
+
for (const pc of summary.paramToCallArg) {
|
|
270
|
+
if (pc.param !== tp.paramIndex)
|
|
271
|
+
continue;
|
|
272
|
+
for (const edge of resolveCallees(tp.fnId, pc.calleeName)) {
|
|
273
|
+
const callee = summaries.get(edge.calleeId);
|
|
274
|
+
if (!callee)
|
|
275
|
+
continue;
|
|
276
|
+
if (pc.argIndex >= callee.paramCount)
|
|
277
|
+
continue;
|
|
278
|
+
const next = appendHop(tp.hops, { fnId: edge.calleeId, callLine: pc.callLine, argIndex: pc.argIndex }, maxHops);
|
|
279
|
+
// Union the edge's neutralised kinds onto the composed path (a
|
|
280
|
+
// sanitizer between this param and the callee arg stays neutralised).
|
|
281
|
+
const neutralized = pc.neutralized && pc.neutralized.length > 0
|
|
282
|
+
? new Set([...tp.neutralized, ...pc.neutralized])
|
|
283
|
+
: tp.neutralized;
|
|
284
|
+
taint({
|
|
285
|
+
fnId: edge.calleeId,
|
|
286
|
+
paramIndex: pc.argIndex,
|
|
287
|
+
sourceFnId: tp.sourceFnId,
|
|
288
|
+
hops: next.hops,
|
|
289
|
+
truncated: tp.truncated || next.truncated,
|
|
290
|
+
neutralized,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// ── deterministic assembly ────────────────────────────────────────────────
|
|
296
|
+
const all = [...findingsByKey.values()].sort((a, b) => a.sourceFnId.localeCompare(b.sourceFnId) ||
|
|
297
|
+
a.sinkFnId.localeCompare(b.sinkFnId) ||
|
|
298
|
+
a.sinkKind.localeCompare(b.sinkKind));
|
|
299
|
+
const maxFindings = limits?.maxFindings && limits.maxFindings > 0 ? limits.maxFindings : Infinity;
|
|
300
|
+
const findings = all.length > maxFindings ? all.slice(0, maxFindings) : all;
|
|
301
|
+
return {
|
|
302
|
+
findings,
|
|
303
|
+
droppedFindings: all.length - findings.length,
|
|
304
|
+
unmatchedCallSites,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
/** Append a hop, respecting the hop cap (keeps the source-side prefix). */
|
|
308
|
+
function appendHop(hops, hop, maxHops) {
|
|
309
|
+
if (hops.length >= maxHops)
|
|
310
|
+
return { hops, truncated: true };
|
|
311
|
+
return { hops: [...hops, hop], truncated: hops.length + 1 > maxHops };
|
|
312
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import-aware taint-site matcher (#2083 M3 U2, plan KTD7).
|
|
3
|
+
*
|
|
4
|
+
* Classifies a function's harvested {@link SiteRecord}s against a registered
|
|
5
|
+
* {@link SourceSinkSanitizerSpec}: which member reads are SOURCES, which
|
|
6
|
+
* call/new sites are SINKS (and at which argument positions), and which are
|
|
7
|
+
* SANITIZERS. Pure main-thread data work — sites + bindings come from the U1
|
|
8
|
+
* worker harvest, imports from `ParsedFile.parsedImports`; no AST, no I/O.
|
|
9
|
+
*
|
|
10
|
+
* PRECONDITION: the caller must gate the CFG through `hasTaintSafeSites`
|
|
11
|
+
* (taint/site-safety.ts) first — this module dereferences binding/site
|
|
12
|
+
* indices without re-validating them.
|
|
13
|
+
*
|
|
14
|
+
* ## Callee resolution precedence (bare and member-rooted calls)
|
|
15
|
+
*
|
|
16
|
+
* 1. ESM import join — the callee root's local name is resolved through the
|
|
17
|
+
* {@link TaintImportIndex} built from `parsedImports` (`named`/`alias`
|
|
18
|
+
* members, `namespace`/default-import module handles); `import { exec as
|
|
19
|
+
* run } from 'child_process'` makes `run(c)` resolve to
|
|
20
|
+
* `child_process.exec`, and `import * as cp …` makes `cp.exec(c)` resolve
|
|
21
|
+
* the same way.
|
|
22
|
+
* 2. require-literal join — a binding whose in-function defining site carries
|
|
23
|
+
* `requireArg` resolves like a namespace handle (`const cp =
|
|
24
|
+
* require('child_process'); cp.exec(c)`). A BARE call of a require-joined
|
|
25
|
+
* binding is matched under BOTH interpretations, `<module>.default` (the
|
|
26
|
+
* module/default export invoked directly) and `<module>.<localName>`
|
|
27
|
+
* (non-renamed destructured require — the harvest attaches `resultDefs`
|
|
28
|
+
* to destructured bindings without recording the property path, and the
|
|
29
|
+
* binding name IS the member name in the non-renamed case).
|
|
30
|
+
* 3. Bare-name fallback — TRUE GLOBALS only (`global: true` entries: `eval`,
|
|
31
|
+
* `new Function`, `encodeURIComponent`), and only when the name is neither
|
|
32
|
+
* import-bound nor shadowed. Conventional receiver names (`req`/`request`
|
|
33
|
+
* member-read sources, `res.send`, `.query`/`.execute`) are matched
|
|
34
|
+
* name-based by their own mechanisms, never via the global fallback.
|
|
35
|
+
*
|
|
36
|
+
* ## Shadowing rule (exact)
|
|
37
|
+
*
|
|
38
|
+
* A name is treated as function-local — blocking import/global resolution —
|
|
39
|
+
* iff the function's binding table contains a NON-`synthetic` entry with that
|
|
40
|
+
* name (an in-function `function exec(){}` / `const exec = …`). Synthetic
|
|
41
|
+
* bindings (kind `module`, `synthetic: true`) are imports, true globals, or
|
|
42
|
+
* enclosing-scope captures and do not shadow. Member-call roots use the
|
|
43
|
+
* harvested `receiver` binding index directly (no name scan).
|
|
44
|
+
*
|
|
45
|
+
* ## Documented resolution gaps (direction stated, per plan KTD10)
|
|
46
|
+
*
|
|
47
|
+
* - MODULE-LEVEL `const cp = require('child_process')`: the binding is
|
|
48
|
+
* synthetic inside the function, produces no `ParsedImport`, and its
|
|
49
|
+
* defining site lives outside the function's harvested sites — the
|
|
50
|
+
* require join cannot see it. Module-mechanism sinks miss (FN) and
|
|
51
|
+
* sanitizers don't kill (FP noise — never a false kill, the safe
|
|
52
|
+
* direction). Only in-function requires resolve.
|
|
53
|
+
* - RENAMED destructured require (`const { exec: run } = require(…)`):
|
|
54
|
+
* the dual interpretation resolves `run` to `child_process.run` — no
|
|
55
|
+
* match (FN). Non-renamed destructures resolve exactly.
|
|
56
|
+
* - CONSERVATIVE shadow scan for bare calls: ANY non-synthetic binding of
|
|
57
|
+
* the callee name anywhere in the function blocks import/global
|
|
58
|
+
* resolution, even when the shadow is block-scoped elsewhere and the call
|
|
59
|
+
* site actually sees the import (FN; rare; safe for sanitizers).
|
|
60
|
+
* - MODULE-LEVEL user declarations are indistinguishable from imports in
|
|
61
|
+
* the binding table (both synthetic). ESM forbids a module-level
|
|
62
|
+
* declaration colliding with an import name, so the import join is
|
|
63
|
+
* authoritative when an import exists; a module-level user function
|
|
64
|
+
* shadowing a TRUE GLOBAL (e.g. a local `encodeURIComponent`) is not
|
|
65
|
+
* detectable and would still match (pathological; accepted).
|
|
66
|
+
* - Handle COPIES (`const c2 = cp; c2.exec(…)`) are not followed — joins
|
|
67
|
+
* are one level deep (binding → import/require), never through
|
|
68
|
+
* assignments (FN).
|
|
69
|
+
* - `this.`/`super.`-rooted and call-rooted callee chains have no
|
|
70
|
+
* resolvable root: only the syntactic `anyReceiver`/`receivers`
|
|
71
|
+
* mechanisms can match them.
|
|
72
|
+
* - `reexport`/`wildcard`/`dynamic-*`/`side-effect` imports introduce no
|
|
73
|
+
* matcher-visible local binding and are skipped by the index.
|
|
74
|
+
*/
|
|
75
|
+
import type { ParsedImport } from '../../../_shared/index.js';
|
|
76
|
+
import type { FunctionCfg } from '../cfg/types.js';
|
|
77
|
+
import type { SourceSinkSanitizerSpec, TaintMemberSourceEntry, TaintSanitizerEntry, TaintSinkEntry } from './source-sink-config.js';
|
|
78
|
+
/** What a local name imported into the file denotes. */
|
|
79
|
+
export interface TaintImportBinding {
|
|
80
|
+
/** Normalized module specifier (`node:` scheme stripped). */
|
|
81
|
+
readonly module: string;
|
|
82
|
+
/**
|
|
83
|
+
* Exported member bound by a named/aliased import; `undefined` when the
|
|
84
|
+
* local name is a MODULE HANDLE (namespace import, or a default import —
|
|
85
|
+
* CJS interop makes the default export ≈ the module object).
|
|
86
|
+
*/
|
|
87
|
+
readonly member?: string;
|
|
88
|
+
}
|
|
89
|
+
/** Local name → import provenance for one file. Build once per file (U4). */
|
|
90
|
+
export type TaintImportIndex = ReadonlyMap<string, TaintImportBinding>;
|
|
91
|
+
/** A member-read site matched as a taint source. */
|
|
92
|
+
export interface MatchedSourceRead {
|
|
93
|
+
/** Index into the owning statement's `sites` array. */
|
|
94
|
+
readonly siteIndex: number;
|
|
95
|
+
readonly entry: TaintMemberSourceEntry;
|
|
96
|
+
}
|
|
97
|
+
/** A call/new site matched as a sink. */
|
|
98
|
+
export interface MatchedSinkCall {
|
|
99
|
+
/** Index into the owning statement's `sites` array. */
|
|
100
|
+
readonly siteIndex: number;
|
|
101
|
+
readonly entry: TaintSinkEntry;
|
|
102
|
+
/**
|
|
103
|
+
* Positions (indices into `site.args`) that are registered sink positions
|
|
104
|
+
* AND carry at least one recorded binding occurrence, after the spread
|
|
105
|
+
* rule (a recorded position ≥ `site.spread` matches when any registered
|
|
106
|
+
* position ≥ the spread index exists — runtime positions after a spread
|
|
107
|
+
* are unknowable) and the template rule (`template: true` aggregates all
|
|
108
|
+
* substitutions at position 0 and matches any-position). Never empty — a
|
|
109
|
+
* sink whose dangerous positions carry no occurrences cannot produce a
|
|
110
|
+
* finding and is not reported.
|
|
111
|
+
*/
|
|
112
|
+
readonly argPositions: readonly number[];
|
|
113
|
+
}
|
|
114
|
+
/** A call site matched as a sanitizer (import-aware/global only — see module doc). */
|
|
115
|
+
export interface MatchedSanitizerCall {
|
|
116
|
+
/** Index into the owning statement's `sites` array. */
|
|
117
|
+
readonly siteIndex: number;
|
|
118
|
+
readonly entry: TaintSanitizerEntry;
|
|
119
|
+
/**
|
|
120
|
+
* Bindings the sanitizer's result defines directly (`const b = escape(t)`
|
|
121
|
+
* ⇒ `b`) — U3's kill targets (KTD4b). Empty for value-position sanitizer
|
|
122
|
+
* calls (`exec(escape(x))`), whose effect is occurrence INTERPOSITION via
|
|
123
|
+
* the site's `parent`/via-tag chain, not a def kill.
|
|
124
|
+
*/
|
|
125
|
+
readonly resultDefs: readonly number[];
|
|
126
|
+
}
|
|
127
|
+
/** All matches within one statement. Emitted only when at least one list is non-empty. */
|
|
128
|
+
export interface StatementMatches {
|
|
129
|
+
readonly blockIndex: number;
|
|
130
|
+
readonly statementIndex: number;
|
|
131
|
+
readonly line: number;
|
|
132
|
+
readonly sources: readonly MatchedSourceRead[];
|
|
133
|
+
readonly sinks: readonly MatchedSinkCall[];
|
|
134
|
+
readonly sanitizers: readonly MatchedSanitizerCall[];
|
|
135
|
+
}
|
|
136
|
+
/** Classified sites for one function, in (block, statement, site, entry) order. */
|
|
137
|
+
export interface FunctionSiteMatches {
|
|
138
|
+
readonly statements: readonly StatementMatches[];
|
|
139
|
+
/** Fast-path gates for U4: the solver runs only when both are true. */
|
|
140
|
+
readonly hasSource: boolean;
|
|
141
|
+
readonly hasSink: boolean;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Build the local-name → module/member index from a file's `parsedImports`.
|
|
145
|
+
* Only `named`/`alias`/`namespace` kinds bind matcher-visible local names;
|
|
146
|
+
* `importedName === 'default'` collapses to a module handle.
|
|
147
|
+
*/
|
|
148
|
+
export declare function buildTaintImportIndex(imports: readonly ParsedImport[]): TaintImportIndex;
|
|
149
|
+
/**
|
|
150
|
+
* Classify a function's harvested sites against a language spec. See the
|
|
151
|
+
* module doc for resolution precedence, the shadowing rule, and gaps.
|
|
152
|
+
*/
|
|
153
|
+
export declare function matchFunctionSites(cfg: FunctionCfg, spec: SourceSinkSanitizerSpec, imports: TaintImportIndex): FunctionSiteMatches;
|