gitnexus 1.6.8-rc.8 → 1.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +23 -2
- package/dist/core/ingestion/languages/c-cpp.js +17 -5
- 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 +8 -1
- 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 +44 -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/clone-safety.d.ts +109 -0
- package/dist/core/ingestion/workers/clone-safety.js +465 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +23 -0
- package/dist/core/ingestion/workers/parse-worker.js +81 -53
- package/dist/core/ingestion/workers/post-result.d.ts +22 -0
- package/dist/core/ingestion/workers/post-result.js +87 -0
- package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
- package/dist/core/ingestion/workers/result-merge.js +43 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +15 -40
- package/dist/core/ingestion/workers/worker-pool.js +43 -16
- 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-ipc.d.ts +58 -0
- package/dist/server/analyze-worker-ipc.js +16 -0
- package/dist/server/analyze-worker.d.ts +17 -1
- package/dist/server/analyze-worker.js +7 -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 +44 -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,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-function taint SUMMARY harvest (#2084 M4 U1).
|
|
3
|
+
*
|
|
4
|
+
* Pure, deterministic derivation of one function's {@link FunctionSummary}
|
|
5
|
+
* facts from the SAME substrate the M3 intra-procedural pass consumes — the M2
|
|
6
|
+
* reaching-definition facts (`computeReachingDefs`) and the matched taint sites
|
|
7
|
+
* (`matchFunctionSites`). No graph, no I/O, no logger; mirrors the
|
|
8
|
+
* `computeReachingDefs` / `computeTaintFlows` contract (insertion-ordered
|
|
9
|
+
* worklist, explicitly sorted outputs) so snapshot tests and the version stamp
|
|
10
|
+
* stay stable. Runs IN-PHASE inside the scope-resolution pdg window where the
|
|
11
|
+
* CFG side channel is live (plan KTD1); the cross-function fixpoint that
|
|
12
|
+
* COMPOSES these summaries runs afterward over the complete call graph.
|
|
13
|
+
*
|
|
14
|
+
* ## What a summary captures (whole-parameter granularity)
|
|
15
|
+
*
|
|
16
|
+
* Seeding each formal parameter as taint and running forward reachability over
|
|
17
|
+
* the def→use facts yields four edge categories:
|
|
18
|
+
*
|
|
19
|
+
* - **param→return** — a param's value reaches a `return <expr>`. Return
|
|
20
|
+
* statements are identified structurally: the SOURCE block of every CFG edge
|
|
21
|
+
* of kind `return` terminates in the return jump (the M2 edge-kind
|
|
22
|
+
* invariant), so its last statement's `uses` are the returned bindings.
|
|
23
|
+
* - **param→callee-arg** — a param occurrence lands in argument position
|
|
24
|
+
* `argIndex` of a call at `callLine`. The fixpoint resolves `callLine` to a
|
|
25
|
+
* callee via the caller's `CALLS` edges and applies the callee's summary
|
|
26
|
+
* (TITO composition).
|
|
27
|
+
* - **param→sink** — a param reaches a modelled sink position (the partial
|
|
28
|
+
* flow that a cross-function source completes).
|
|
29
|
+
* - **source→return** — a modelled source read (`req.body`) reaches the return
|
|
30
|
+
* (a generative summary: calling the function yields tainted data).
|
|
31
|
+
*
|
|
32
|
+
* ## Soundness model (context-insensitive first cut)
|
|
33
|
+
*
|
|
34
|
+
* Onward propagation uses the M3 STATEMENT-LEVEL precision floor: a statement
|
|
35
|
+
* that uses a tainted binding taints all of its defs (and `mayDefs`). This is
|
|
36
|
+
* the same sound over-approximation M3 documents — it may over-taint
|
|
37
|
+
* (multi-declarator conflation) but never drops a real flow. Sanitizer
|
|
38
|
+
* `resultDefs` narrow the EXCLUSION set (a def produced by a matched sanitizer
|
|
39
|
+
* carries that sanitizer's neutralised `SinkKind`s), so a sanitised value does
|
|
40
|
+
* not trigger a downstream sink of the neutralised kind — the kind-set
|
|
41
|
+
* exclusion model, simplified to the result-def channel (occurrence
|
|
42
|
+
* interposition, field paths, and callbacks are deferred — plan KTD).
|
|
43
|
+
*
|
|
44
|
+
* The summary edges themselves (return / call-arg / sink) are recorded from
|
|
45
|
+
* ACTUAL binding occurrences (a tainted binding present in a return's uses, a
|
|
46
|
+
* call's arg list, or a matched sink position), never the floor — the floor
|
|
47
|
+
* governs only onward def-tainting, keeping the recorded edges precise.
|
|
48
|
+
*
|
|
49
|
+
* ## Known limitation — destructured / rest params (documented FN)
|
|
50
|
+
*
|
|
51
|
+
* Param indices are assigned by ORDINAL over the flattened param-binding list,
|
|
52
|
+
* which equals the FORMAL parameter position only when every param is a simple
|
|
53
|
+
* identifier. A destructured or rest param contributes several bindings (or
|
|
54
|
+
* shifts the count), so a simple param positioned AFTER one
|
|
55
|
+
* (`function f([a, b], x) { sink(x) }`) gets a summary port index that does not
|
|
56
|
+
* match the formal argument position the interprocedural solver joins against
|
|
57
|
+
* — a cross-function false negative for that function. The precise fix needs a
|
|
58
|
+
* formal-param index threaded from the worker harvest (`BindingEntry`), a
|
|
59
|
+
* cache-namespace-affecting change deferred with the other documented FN
|
|
60
|
+
* classes (closures, fields — see the taint skill). Functions with all-simple
|
|
61
|
+
* params (the common case) are unaffected.
|
|
62
|
+
*/
|
|
63
|
+
import type { FunctionCfg } from '../cfg/types.js';
|
|
64
|
+
import { type FunctionDefUse } from '../cfg/reaching-defs.js';
|
|
65
|
+
import type { FunctionSiteMatches } from './match.js';
|
|
66
|
+
import type { CallResult, ParamToCallArg, ParamToReturn, ParamToSink, SourceToCallArg, SourceToReturn } from './summary-model.js';
|
|
67
|
+
/** The own-facts portion of a summary (fnId/version are added by the caller). */
|
|
68
|
+
export interface HarvestedSummaryFacts {
|
|
69
|
+
readonly paramCount: number;
|
|
70
|
+
readonly paramToReturn: readonly ParamToReturn[];
|
|
71
|
+
readonly paramToCallArg: readonly ParamToCallArg[];
|
|
72
|
+
readonly paramToSink: readonly ParamToSink[];
|
|
73
|
+
readonly sourceToReturn: readonly SourceToReturn[];
|
|
74
|
+
readonly sourceToCallArg: readonly SourceToCallArg[];
|
|
75
|
+
readonly callResults: readonly CallResult[];
|
|
76
|
+
}
|
|
77
|
+
export interface HarvestResult {
|
|
78
|
+
/** `computed` — facts derived; `coverage-gap` — the RD solver was not
|
|
79
|
+
* `computed`, so no summary is produced (consistent with M3 R4). */
|
|
80
|
+
readonly status: 'computed' | 'coverage-gap';
|
|
81
|
+
readonly gapReason?: FunctionDefUse['status'];
|
|
82
|
+
readonly facts: HarvestedSummaryFacts;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Harvest the summary facts for one function. PRECONDITION: `cfg` is
|
|
86
|
+
* `isEmitSafeCfg`-filtered and `defUse` was computed from it; sites are assumed
|
|
87
|
+
* `hasTaintSafeSites`-valid (the caller gates exactly as the M3 emit path does).
|
|
88
|
+
*/
|
|
89
|
+
export declare function harvestFunctionSummary(cfg: FunctionCfg, defUse: FunctionDefUse, matches: FunctionSiteMatches): HarvestResult;
|
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-function taint SUMMARY harvest (#2084 M4 U1).
|
|
3
|
+
*
|
|
4
|
+
* Pure, deterministic derivation of one function's {@link FunctionSummary}
|
|
5
|
+
* facts from the SAME substrate the M3 intra-procedural pass consumes — the M2
|
|
6
|
+
* reaching-definition facts (`computeReachingDefs`) and the matched taint sites
|
|
7
|
+
* (`matchFunctionSites`). No graph, no I/O, no logger; mirrors the
|
|
8
|
+
* `computeReachingDefs` / `computeTaintFlows` contract (insertion-ordered
|
|
9
|
+
* worklist, explicitly sorted outputs) so snapshot tests and the version stamp
|
|
10
|
+
* stay stable. Runs IN-PHASE inside the scope-resolution pdg window where the
|
|
11
|
+
* CFG side channel is live (plan KTD1); the cross-function fixpoint that
|
|
12
|
+
* COMPOSES these summaries runs afterward over the complete call graph.
|
|
13
|
+
*
|
|
14
|
+
* ## What a summary captures (whole-parameter granularity)
|
|
15
|
+
*
|
|
16
|
+
* Seeding each formal parameter as taint and running forward reachability over
|
|
17
|
+
* the def→use facts yields four edge categories:
|
|
18
|
+
*
|
|
19
|
+
* - **param→return** — a param's value reaches a `return <expr>`. Return
|
|
20
|
+
* statements are identified structurally: the SOURCE block of every CFG edge
|
|
21
|
+
* of kind `return` terminates in the return jump (the M2 edge-kind
|
|
22
|
+
* invariant), so its last statement's `uses` are the returned bindings.
|
|
23
|
+
* - **param→callee-arg** — a param occurrence lands in argument position
|
|
24
|
+
* `argIndex` of a call at `callLine`. The fixpoint resolves `callLine` to a
|
|
25
|
+
* callee via the caller's `CALLS` edges and applies the callee's summary
|
|
26
|
+
* (TITO composition).
|
|
27
|
+
* - **param→sink** — a param reaches a modelled sink position (the partial
|
|
28
|
+
* flow that a cross-function source completes).
|
|
29
|
+
* - **source→return** — a modelled source read (`req.body`) reaches the return
|
|
30
|
+
* (a generative summary: calling the function yields tainted data).
|
|
31
|
+
*
|
|
32
|
+
* ## Soundness model (context-insensitive first cut)
|
|
33
|
+
*
|
|
34
|
+
* Onward propagation uses the M3 STATEMENT-LEVEL precision floor: a statement
|
|
35
|
+
* that uses a tainted binding taints all of its defs (and `mayDefs`). This is
|
|
36
|
+
* the same sound over-approximation M3 documents — it may over-taint
|
|
37
|
+
* (multi-declarator conflation) but never drops a real flow. Sanitizer
|
|
38
|
+
* `resultDefs` narrow the EXCLUSION set (a def produced by a matched sanitizer
|
|
39
|
+
* carries that sanitizer's neutralised `SinkKind`s), so a sanitised value does
|
|
40
|
+
* not trigger a downstream sink of the neutralised kind — the kind-set
|
|
41
|
+
* exclusion model, simplified to the result-def channel (occurrence
|
|
42
|
+
* interposition, field paths, and callbacks are deferred — plan KTD).
|
|
43
|
+
*
|
|
44
|
+
* The summary edges themselves (return / call-arg / sink) are recorded from
|
|
45
|
+
* ACTUAL binding occurrences (a tainted binding present in a return's uses, a
|
|
46
|
+
* call's arg list, or a matched sink position), never the floor — the floor
|
|
47
|
+
* governs only onward def-tainting, keeping the recorded edges precise.
|
|
48
|
+
*
|
|
49
|
+
* ## Known limitation — destructured / rest params (documented FN)
|
|
50
|
+
*
|
|
51
|
+
* Param indices are assigned by ORDINAL over the flattened param-binding list,
|
|
52
|
+
* which equals the FORMAL parameter position only when every param is a simple
|
|
53
|
+
* identifier. A destructured or rest param contributes several bindings (or
|
|
54
|
+
* shifts the count), so a simple param positioned AFTER one
|
|
55
|
+
* (`function f([a, b], x) { sink(x) }`) gets a summary port index that does not
|
|
56
|
+
* match the formal argument position the interprocedural solver joins against
|
|
57
|
+
* — a cross-function false negative for that function. The precise fix needs a
|
|
58
|
+
* formal-param index threaded from the worker harvest (`BindingEntry`), a
|
|
59
|
+
* cache-namespace-affecting change deferred with the other documented FN
|
|
60
|
+
* classes (closures, fields — see the taint skill). Functions with all-simple
|
|
61
|
+
* params (the common case) are unaffected.
|
|
62
|
+
*/
|
|
63
|
+
import { pointKey } from '../cfg/reaching-defs.js';
|
|
64
|
+
import { sinkKindRank, sortSinkKinds } from './source-sink-config.js';
|
|
65
|
+
const EMPTY_FACTS = {
|
|
66
|
+
paramCount: 0,
|
|
67
|
+
paramToReturn: [],
|
|
68
|
+
paramToCallArg: [],
|
|
69
|
+
paramToSink: [],
|
|
70
|
+
sourceToReturn: [],
|
|
71
|
+
sourceToCallArg: [],
|
|
72
|
+
callResults: [],
|
|
73
|
+
};
|
|
74
|
+
/** Last segment of a dotted callee path (`child_process.exec` ⇒ `exec`). */
|
|
75
|
+
const calleeTail = (callee) => callee === undefined ? undefined : (callee.split('.').pop() ?? callee);
|
|
76
|
+
/**
|
|
77
|
+
* Harvest the summary facts for one function. PRECONDITION: `cfg` is
|
|
78
|
+
* `isEmitSafeCfg`-filtered and `defUse` was computed from it; sites are assumed
|
|
79
|
+
* `hasTaintSafeSites`-valid (the caller gates exactly as the M3 emit path does).
|
|
80
|
+
*/
|
|
81
|
+
export function harvestFunctionSummary(cfg, defUse, matches) {
|
|
82
|
+
if (defUse.status !== 'computed') {
|
|
83
|
+
return { status: 'coverage-gap', gapReason: defUse.status, facts: EMPTY_FACTS };
|
|
84
|
+
}
|
|
85
|
+
const bindings = defUse.bindings;
|
|
86
|
+
// ── param bindings → param index (declaration order) ──────────────────────
|
|
87
|
+
// `kind:'param'` bindings, ordered by declaration site (declLine/declColumn).
|
|
88
|
+
const paramBindings = bindings
|
|
89
|
+
.map((b, idx) => ({ b, idx }))
|
|
90
|
+
.filter((e) => e.b.kind === 'param')
|
|
91
|
+
.sort((a, b) => a.b.declLine - b.b.declLine || a.b.declColumn - b.b.declColumn);
|
|
92
|
+
const paramIndexOf = new Map();
|
|
93
|
+
paramBindings.forEach((e, paramIdx) => paramIndexOf.set(e.idx, paramIdx));
|
|
94
|
+
const paramCount = paramBindings.length;
|
|
95
|
+
// ── return points: source block of every `return` CFG edge ────────────────
|
|
96
|
+
// The M2 edge-kind invariant: a `return` edge's SOURCE block terminates in
|
|
97
|
+
// the return jump, so its LAST statement is the `return <expr>` — its `uses`
|
|
98
|
+
// are the returned bindings. (`return;` with no value has empty uses.)
|
|
99
|
+
const returnUseStmtKeys = new Set();
|
|
100
|
+
for (const e of cfg.edges) {
|
|
101
|
+
if (e.kind !== 'return')
|
|
102
|
+
continue;
|
|
103
|
+
const block = cfg.blocks[e.from];
|
|
104
|
+
const stmts = block?.statements;
|
|
105
|
+
if (!stmts || stmts.length === 0)
|
|
106
|
+
continue;
|
|
107
|
+
returnUseStmtKeys.add(`${e.from}:${stmts.length - 1}`);
|
|
108
|
+
}
|
|
109
|
+
// ── per-statement match context (sink/source/sanitizer by site) ───────────
|
|
110
|
+
const sinkPosBySite = new Map(); // stmtKey → site → argPositions
|
|
111
|
+
const sinkKindByEntry = new Map(); // stmtKey → site → kinds at any pos
|
|
112
|
+
const sanitizerResultDefKinds = new Map(); // stmtKey → resultDef binding → kinds
|
|
113
|
+
// Matched sink/sanitizer sites (`stmtKey:siteIndex`) — EXCLUDED from the
|
|
114
|
+
// call-result seed (#2084 review P1-1): their result semantics are already
|
|
115
|
+
// modelled (a sanitizer's result rides U2 exclusions; a sink returns void).
|
|
116
|
+
const modeledSites = new Set();
|
|
117
|
+
for (const sm of matches.statements) {
|
|
118
|
+
const stmtKey = `${sm.blockIndex}:${sm.statementIndex}`;
|
|
119
|
+
for (const s of sm.sinks)
|
|
120
|
+
modeledSites.add(`${stmtKey}:${s.siteIndex}`);
|
|
121
|
+
for (const s of sm.sanitizers)
|
|
122
|
+
modeledSites.add(`${stmtKey}:${s.siteIndex}`);
|
|
123
|
+
if (sm.sinks.length > 0) {
|
|
124
|
+
const bySite = new Map();
|
|
125
|
+
const kindBySite = new Map();
|
|
126
|
+
for (const sink of sm.sinks) {
|
|
127
|
+
const pos = bySite.get(sink.siteIndex) ?? new Set();
|
|
128
|
+
for (const p of sink.argPositions)
|
|
129
|
+
pos.add(p);
|
|
130
|
+
bySite.set(sink.siteIndex, pos);
|
|
131
|
+
const ks = kindBySite.get(sink.siteIndex) ?? [];
|
|
132
|
+
ks.push(sink.entry.kind);
|
|
133
|
+
kindBySite.set(sink.siteIndex, ks);
|
|
134
|
+
}
|
|
135
|
+
sinkPosBySite.set(stmtKey, bySite);
|
|
136
|
+
sinkKindByEntry.set(stmtKey, kindBySite);
|
|
137
|
+
}
|
|
138
|
+
if (sm.sanitizers.length > 0) {
|
|
139
|
+
const byDef = new Map();
|
|
140
|
+
for (const san of sm.sanitizers) {
|
|
141
|
+
for (const d of san.resultDefs) {
|
|
142
|
+
const ks = byDef.get(d) ?? [];
|
|
143
|
+
ks.push(...san.entry.neutralizes);
|
|
144
|
+
byDef.set(d, ks);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
sanitizerResultDefKinds.set(stmtKey, byDef);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const stmtAt = (p) => cfg.blocks[p.blockIndex]?.statements?.[p.stmtIndex];
|
|
151
|
+
// ── def→use index ─────────────────────────────────────────────────────────
|
|
152
|
+
const factsByDef = new Map();
|
|
153
|
+
for (const f of defUse.facts) {
|
|
154
|
+
const key = `${f.bindingIdx}:${pointKey(f.def)}`;
|
|
155
|
+
const list = factsByDef.get(key);
|
|
156
|
+
const entry = { bindingIdx: f.bindingIdx, use: f.use };
|
|
157
|
+
if (list)
|
|
158
|
+
list.push(entry);
|
|
159
|
+
else
|
|
160
|
+
factsByDef.set(key, [entry]);
|
|
161
|
+
}
|
|
162
|
+
// ── accumulators (deduped by string identity) ─────────────────────────────
|
|
163
|
+
const paramReturn = new Map(); // param → neutralized intersection
|
|
164
|
+
const paramReturnSeen = new Set();
|
|
165
|
+
const paramCallArg = new Map();
|
|
166
|
+
const sourceCallArg = new Map();
|
|
167
|
+
// Intersection-over-paths of the neutralized kinds reaching each call-arg
|
|
168
|
+
// edge (#2084 review P1-2, deepening correction a). MUST intersect, not
|
|
169
|
+
// first-write-wins: a second, un-sanitized occurrence path to the same edge
|
|
170
|
+
// (`relay(x){ exec(x); exec(escape(x)); }`) shrinks the set to ∅ — mirror
|
|
171
|
+
// `recordReturn`. `*Seen` tracks first-write so the initial set is a copy.
|
|
172
|
+
const paramCallArgKinds = new Map();
|
|
173
|
+
const sourceCallArgKinds = new Map();
|
|
174
|
+
const intersectKinds = (store, key, incoming) => {
|
|
175
|
+
const cur = store.get(key);
|
|
176
|
+
if (cur === undefined)
|
|
177
|
+
store.set(key, new Set(incoming));
|
|
178
|
+
else
|
|
179
|
+
for (const k of [...cur])
|
|
180
|
+
if (!incoming.has(k))
|
|
181
|
+
cur.delete(k);
|
|
182
|
+
};
|
|
183
|
+
const paramSink = new Set();
|
|
184
|
+
const paramSinkOut = [];
|
|
185
|
+
const sourceReturn = new Set();
|
|
186
|
+
// Caller-side call-result flows (#2084 review P1-1), deduped by a structural key.
|
|
187
|
+
const callResults = new Map();
|
|
188
|
+
const recordCallResult = (cr) => {
|
|
189
|
+
const d = cr.dest;
|
|
190
|
+
const destKey = d.to === 'sink'
|
|
191
|
+
? `sink:${d.sinkKind}`
|
|
192
|
+
: d.to === 'return'
|
|
193
|
+
? 'return'
|
|
194
|
+
: `arg:${d.toCallee ?? ''}:${d.argIndex}`;
|
|
195
|
+
const key = `${cr.calleeName}|${destKey}`;
|
|
196
|
+
if (!callResults.has(key))
|
|
197
|
+
callResults.set(key, cr);
|
|
198
|
+
};
|
|
199
|
+
/** Record param→return, intersecting neutralized kinds across paths. */
|
|
200
|
+
const recordReturn = (param, exclusions) => {
|
|
201
|
+
if (!paramReturnSeen.has(param)) {
|
|
202
|
+
paramReturnSeen.add(param);
|
|
203
|
+
paramReturn.set(param, new Set(exclusions));
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
const cur = paramReturn.get(param);
|
|
207
|
+
for (const k of [...cur])
|
|
208
|
+
if (!exclusions.has(k))
|
|
209
|
+
cur.delete(k);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
// ── seeds: each param at its entry def point + each source statement ───────
|
|
213
|
+
// seedId 0..paramCount-1 = params; -1 = source.
|
|
214
|
+
const queue = [];
|
|
215
|
+
const visited = new Set();
|
|
216
|
+
const enqueue = (t) => {
|
|
217
|
+
// originCallee discriminates call-result seeds (all share seedId -2) so two
|
|
218
|
+
// distinct callees' results on the same binding are not collapsed.
|
|
219
|
+
const key = `${t.seedId}:${t.originCallee ?? ''}:${t.bindingIdx}:${pointKey(t.point)}:${[...t.exclusions].sort().join(',')}`;
|
|
220
|
+
if (visited.has(key))
|
|
221
|
+
return;
|
|
222
|
+
visited.add(key);
|
|
223
|
+
queue.push(t);
|
|
224
|
+
};
|
|
225
|
+
// Param seeds: find each param's def point(s) in the def→use facts (params are
|
|
226
|
+
// defined at ENTRY; any fact whose def-binding is the param and whose def
|
|
227
|
+
// sits in the entry block is a param-origin edge).
|
|
228
|
+
for (const { idx } of paramBindings) {
|
|
229
|
+
const paramIdx = paramIndexOf.get(idx);
|
|
230
|
+
// Seed at every def point of this param binding in the entry block.
|
|
231
|
+
for (const f of defUse.facts) {
|
|
232
|
+
if (f.bindingIdx === idx && f.def.blockIndex === cfg.entryIndex) {
|
|
233
|
+
enqueue({ bindingIdx: idx, point: f.def, seedId: paramIdx, exclusions: new Set() });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Source seeds: a statement with a matched source taints its own defs; a bare
|
|
238
|
+
// `return <source>` is a direct source→return. The source's value rides the
|
|
239
|
+
// statement's defs (resultDefs of the assignment) under the floor.
|
|
240
|
+
for (const sm of matches.statements) {
|
|
241
|
+
if (sm.sources.length === 0)
|
|
242
|
+
continue;
|
|
243
|
+
const stmtKey = `${sm.blockIndex}:${sm.statementIndex}`;
|
|
244
|
+
const facts = cfg.blocks[sm.blockIndex]?.statements?.[sm.statementIndex];
|
|
245
|
+
if (!facts)
|
|
246
|
+
continue;
|
|
247
|
+
const point = {
|
|
248
|
+
blockIndex: sm.blockIndex,
|
|
249
|
+
stmtIndex: sm.statementIndex,
|
|
250
|
+
line: facts.line,
|
|
251
|
+
};
|
|
252
|
+
if (returnUseStmtKeys.has(stmtKey)) {
|
|
253
|
+
for (const src of sm.sources)
|
|
254
|
+
sourceReturn.add(src.entry.kind);
|
|
255
|
+
}
|
|
256
|
+
for (const d of [...facts.defs, ...(facts.mayDefs ?? [])]) {
|
|
257
|
+
enqueue({ bindingIdx: d, point, seedId: -1, exclusions: new Set() });
|
|
258
|
+
}
|
|
259
|
+
// DIRECT source-in-call-arg (`runIt(req.body)`): no intermediate binding is
|
|
260
|
+
// defined, so the floor seed above records nothing. Climb the source
|
|
261
|
+
// member-read's `parent` chain — each enclosing call/new site is a
|
|
262
|
+
// `sourceToCallArg` (the cross-function fixpoint seed). A sink ancestor is
|
|
263
|
+
// M3's intra-procedural concern and harmless to also record here.
|
|
264
|
+
for (const src of sm.sources) {
|
|
265
|
+
let cur = facts.sites?.[src.siteIndex];
|
|
266
|
+
const guard = new Set([src.siteIndex]);
|
|
267
|
+
while (cur?.parent) {
|
|
268
|
+
const [siteIdx, argPos] = cur.parent;
|
|
269
|
+
if (guard.has(siteIdx))
|
|
270
|
+
break;
|
|
271
|
+
guard.add(siteIdx);
|
|
272
|
+
const ancestor = facts.sites?.[siteIdx];
|
|
273
|
+
if (!ancestor)
|
|
274
|
+
break;
|
|
275
|
+
if (ancestor.kind === 'call' || ancestor.kind === 'new') {
|
|
276
|
+
const tail = calleeTail(ancestor.callee);
|
|
277
|
+
const scKey = `${facts.line}:${argPos}:${tail ?? ''}`;
|
|
278
|
+
if (!sourceCallArg.has(scKey)) {
|
|
279
|
+
sourceCallArg.set(scKey, {
|
|
280
|
+
sourceKind: src.entry.kind,
|
|
281
|
+
callLine: facts.line,
|
|
282
|
+
argIndex: argPos,
|
|
283
|
+
...(tail ? { calleeName: tail } : {}),
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
cur = ancestor;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// Call-result seeds (#2084 review P1-1): a call to a (potentially generative)
|
|
292
|
+
// USER function is a NEW taint origin — `matchFunctionSites` only sources
|
|
293
|
+
// member-reads, so the result of `getInput()` is invisible today. Seed every
|
|
294
|
+
// call/new site that is NOT a matched sink/sanitizer and carries a resolvable
|
|
295
|
+
// callee name; the worklist then records a CallResult edge when the result
|
|
296
|
+
// reaches a sink / return / another call arg. The fixpoint composes it with
|
|
297
|
+
// the callee's `sourceToReturn` (the floor cannot — the source is in the
|
|
298
|
+
// callee, so the caller passes no tainted input).
|
|
299
|
+
//
|
|
300
|
+
// Documented limitation: a result passed DIRECTLY into a modelled sink with
|
|
301
|
+
// no binding (`exec(getInput())`) is not recorded as `dest:sink` — the sink
|
|
302
|
+
// is occurrence-gated by `matchFunctionSites` and a bare call result is not a
|
|
303
|
+
// binding occurrence, so `exec` reads as a plain call (recorded `dest:callArg`
|
|
304
|
+
// to a callee with no summary → uncomposed). The binding form
|
|
305
|
+
// (`const t = getInput(); exec(t)`) is the supported path.
|
|
306
|
+
for (const block of cfg.blocks) {
|
|
307
|
+
block.statements?.forEach((facts, stmtIdx) => {
|
|
308
|
+
const stmtKey = `${block.index}:${stmtIdx}`;
|
|
309
|
+
const point = { blockIndex: block.index, stmtIndex: stmtIdx, line: facts.line };
|
|
310
|
+
facts.sites?.forEach((site, siteIndex) => {
|
|
311
|
+
if (site.kind !== 'call' && site.kind !== 'new')
|
|
312
|
+
return;
|
|
313
|
+
if (modeledSites.has(`${stmtKey}:${siteIndex}`))
|
|
314
|
+
return; // sink/sanitizer — modelled
|
|
315
|
+
const tail = calleeTail(site.callee);
|
|
316
|
+
if (tail === undefined)
|
|
317
|
+
return; // unresolvable callee — cannot compose
|
|
318
|
+
// Binding case (`const t = getInput(); …`): seed the result bindings.
|
|
319
|
+
for (const d of site.resultDefs ?? []) {
|
|
320
|
+
enqueue({ bindingIdx: d, point, seedId: -2, exclusions: new Set(), originCallee: tail });
|
|
321
|
+
}
|
|
322
|
+
// Direct case (`exec(getInput())` / `return getInput()`): no result
|
|
323
|
+
// binding — climb the call's parent chain (or detect a bare return).
|
|
324
|
+
if ((site.resultDefs?.length ?? 0) === 0) {
|
|
325
|
+
if (site.parent === undefined && returnUseStmtKeys.has(stmtKey)) {
|
|
326
|
+
recordCallResult({ calleeName: tail, dest: { to: 'return' } });
|
|
327
|
+
}
|
|
328
|
+
let cur = site;
|
|
329
|
+
const guard = new Set([siteIndex]);
|
|
330
|
+
while (cur?.parent) {
|
|
331
|
+
const [ancIdx, argPos] = cur.parent;
|
|
332
|
+
if (guard.has(ancIdx))
|
|
333
|
+
break;
|
|
334
|
+
guard.add(ancIdx);
|
|
335
|
+
const ancestor = facts.sites?.[ancIdx];
|
|
336
|
+
if (!ancestor)
|
|
337
|
+
break;
|
|
338
|
+
const ancKey = `${stmtKey}:${ancIdx}`;
|
|
339
|
+
const sinkPositions = sinkPosBySite.get(stmtKey)?.get(ancIdx);
|
|
340
|
+
if (sinkPositions?.has(argPos)) {
|
|
341
|
+
for (const kind of sinkKindByEntry.get(stmtKey)?.get(ancIdx) ?? []) {
|
|
342
|
+
recordCallResult({ calleeName: tail, dest: { to: 'sink', sinkKind: kind } });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
else if (!modeledSites.has(ancKey) &&
|
|
346
|
+
(ancestor.kind === 'call' || ancestor.kind === 'new')) {
|
|
347
|
+
recordCallResult({
|
|
348
|
+
calleeName: tail,
|
|
349
|
+
dest: {
|
|
350
|
+
to: 'callArg',
|
|
351
|
+
...(calleeTail(ancestor.callee) ? { toCallee: calleeTail(ancestor.callee) } : {}),
|
|
352
|
+
argIndex: argPos,
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
cur = ancestor;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
// ── forward reachability ──────────────────────────────────────────────────
|
|
363
|
+
let head = 0;
|
|
364
|
+
while (head < queue.length) {
|
|
365
|
+
const t = queue[head++];
|
|
366
|
+
const b = t.bindingIdx;
|
|
367
|
+
for (const fact of factsByDef.get(`${b}:${pointKey(t.point)}`) ?? []) {
|
|
368
|
+
const useStmt = stmtAt(fact.use);
|
|
369
|
+
if (!useStmt)
|
|
370
|
+
continue;
|
|
371
|
+
const useKey = `${fact.use.blockIndex}:${fact.use.stmtIndex}`;
|
|
372
|
+
// (1) return reach
|
|
373
|
+
if (returnUseStmtKeys.has(useKey) && useStmt.uses.includes(b)) {
|
|
374
|
+
if (t.originCallee !== undefined) {
|
|
375
|
+
recordCallResult({ calleeName: t.originCallee, dest: { to: 'return' } });
|
|
376
|
+
}
|
|
377
|
+
else if (t.seedId >= 0)
|
|
378
|
+
recordReturn(t.seedId, t.exclusions);
|
|
379
|
+
else
|
|
380
|
+
sourceReturn.add('remote-input');
|
|
381
|
+
}
|
|
382
|
+
// (2) call-arg + sink reach: occurrences of b in this statement's sites.
|
|
383
|
+
const sinkBySite = sinkPosBySite.get(useKey);
|
|
384
|
+
const kindBySite = sinkKindByEntry.get(useKey);
|
|
385
|
+
useStmt.sites?.forEach((site, siteIndex) => {
|
|
386
|
+
const argHits = occurrencesInArgs(site, b);
|
|
387
|
+
for (const argPos of argHits) {
|
|
388
|
+
const callLine = useStmt.line;
|
|
389
|
+
const tail = calleeTail(site.callee);
|
|
390
|
+
if (t.originCallee !== undefined) {
|
|
391
|
+
// Call-result seed (#2084 review P1-1): the result of a call to
|
|
392
|
+
// `originCallee` flows into THIS call's arg `argPos`.
|
|
393
|
+
recordCallResult({
|
|
394
|
+
calleeName: t.originCallee,
|
|
395
|
+
dest: { to: 'callArg', ...(tail ? { toCallee: tail } : {}), argIndex: argPos },
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
else if (t.seedId >= 0) {
|
|
399
|
+
const caKey = `${t.seedId}:${callLine}:${argPos}:${tail ?? ''}`;
|
|
400
|
+
if (!paramCallArg.has(caKey)) {
|
|
401
|
+
paramCallArg.set(caKey, {
|
|
402
|
+
param: t.seedId,
|
|
403
|
+
callLine,
|
|
404
|
+
argIndex: argPos,
|
|
405
|
+
...(tail ? { calleeName: tail } : {}),
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
// Carry the sanitizer exclusions on the path INTO this call arg,
|
|
409
|
+
// intersected over occurrence paths (P1-2).
|
|
410
|
+
intersectKinds(paramCallArgKinds, caKey, t.exclusions);
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
// Source-seeded: a generated source flowing into a call argument is
|
|
414
|
+
// a fixpoint SEED (it taints the callee's param). One source kind
|
|
415
|
+
// today ('remote-input'); when more exist the seed must carry it.
|
|
416
|
+
const scKey = `${callLine}:${argPos}:${tail ?? ''}`;
|
|
417
|
+
if (!sourceCallArg.has(scKey)) {
|
|
418
|
+
sourceCallArg.set(scKey, {
|
|
419
|
+
sourceKind: 'remote-input',
|
|
420
|
+
callLine,
|
|
421
|
+
argIndex: argPos,
|
|
422
|
+
...(tail ? { calleeName: tail } : {}),
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
intersectKinds(sourceCallArgKinds, scKey, t.exclusions);
|
|
426
|
+
}
|
|
427
|
+
// matched sink at this position?
|
|
428
|
+
const sinkPositions = sinkBySite?.get(siteIndex);
|
|
429
|
+
if (sinkPositions?.has(argPos)) {
|
|
430
|
+
for (const kind of kindBySite?.get(siteIndex) ?? []) {
|
|
431
|
+
if (t.exclusions.has(kind))
|
|
432
|
+
continue;
|
|
433
|
+
if (t.originCallee !== undefined) {
|
|
434
|
+
// A generated source returned by `originCallee` reaches a sink.
|
|
435
|
+
recordCallResult({
|
|
436
|
+
calleeName: t.originCallee,
|
|
437
|
+
dest: { to: 'sink', sinkKind: kind },
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
else if (t.seedId >= 0) {
|
|
441
|
+
const sKey = `${t.seedId}:${kind}`;
|
|
442
|
+
if (!paramSink.has(sKey)) {
|
|
443
|
+
paramSink.add(sKey);
|
|
444
|
+
paramSinkOut.push({ param: t.seedId, sinkKind: kind });
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
// (3) onward floor: this statement's defs become tainted, with sanitizer
|
|
452
|
+
// result-def exclusions accumulated.
|
|
453
|
+
const sanByDef = sanitizerResultDefKinds.get(useKey);
|
|
454
|
+
for (const d of [...useStmt.defs, ...(useStmt.mayDefs ?? [])]) {
|
|
455
|
+
const added = sanByDef?.get(d);
|
|
456
|
+
const exclusions = added && added.length > 0 ? new Set([...t.exclusions, ...added]) : t.exclusions;
|
|
457
|
+
enqueue({
|
|
458
|
+
bindingIdx: d,
|
|
459
|
+
point: {
|
|
460
|
+
blockIndex: fact.use.blockIndex,
|
|
461
|
+
stmtIndex: fact.use.stmtIndex,
|
|
462
|
+
line: useStmt.line,
|
|
463
|
+
},
|
|
464
|
+
seedId: t.seedId,
|
|
465
|
+
exclusions,
|
|
466
|
+
...(t.originCallee !== undefined ? { originCallee: t.originCallee } : {}),
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// ── deterministic assembly ────────────────────────────────────────────────
|
|
472
|
+
const paramToReturn = [...paramReturn.entries()]
|
|
473
|
+
.map(([param, kinds]) => ({
|
|
474
|
+
param,
|
|
475
|
+
...(kinds.size > 0 ? { neutralized: sortSinkKinds(kinds) } : {}),
|
|
476
|
+
}))
|
|
477
|
+
.sort((a, b) => a.param - b.param);
|
|
478
|
+
const paramToCallArg = [...paramCallArg.entries()]
|
|
479
|
+
.map(([key, edge]) => {
|
|
480
|
+
const kinds = paramCallArgKinds.get(key);
|
|
481
|
+
return kinds && kinds.size > 0 ? { ...edge, neutralized: sortSinkKinds(kinds) } : edge;
|
|
482
|
+
})
|
|
483
|
+
.sort((a, b) => a.param - b.param ||
|
|
484
|
+
a.callLine - b.callLine ||
|
|
485
|
+
a.argIndex - b.argIndex ||
|
|
486
|
+
(a.calleeName ?? '').localeCompare(b.calleeName ?? ''));
|
|
487
|
+
const paramToSink = paramSinkOut.sort((a, b) => a.param - b.param || sinkKindRank(a.sinkKind) - sinkKindRank(b.sinkKind));
|
|
488
|
+
const sourceToReturn = sourceReturn.size > 0 ? [{ sourceKind: 'remote-input' }] : [];
|
|
489
|
+
const sourceToCallArg = [...sourceCallArg.entries()]
|
|
490
|
+
.map(([key, edge]) => {
|
|
491
|
+
const kinds = sourceCallArgKinds.get(key);
|
|
492
|
+
return kinds && kinds.size > 0 ? { ...edge, neutralized: sortSinkKinds(kinds) } : edge;
|
|
493
|
+
})
|
|
494
|
+
.sort((a, b) => a.callLine - b.callLine ||
|
|
495
|
+
a.argIndex - b.argIndex ||
|
|
496
|
+
(a.calleeName ?? '').localeCompare(b.calleeName ?? ''));
|
|
497
|
+
const callResultsOut = [...callResults.values()].sort((a, b) => {
|
|
498
|
+
const ord = (cr) => {
|
|
499
|
+
const d = cr.dest;
|
|
500
|
+
const dest = d.to === 'sink'
|
|
501
|
+
? `1sink:${d.sinkKind}`
|
|
502
|
+
: d.to === 'return'
|
|
503
|
+
? '2return'
|
|
504
|
+
: `0arg:${d.toCallee ?? ''}:${d.argIndex}`;
|
|
505
|
+
return `${cr.calleeName}|${dest}`;
|
|
506
|
+
};
|
|
507
|
+
return ord(a).localeCompare(ord(b));
|
|
508
|
+
});
|
|
509
|
+
return {
|
|
510
|
+
status: 'computed',
|
|
511
|
+
facts: {
|
|
512
|
+
paramCount,
|
|
513
|
+
paramToReturn,
|
|
514
|
+
paramToCallArg,
|
|
515
|
+
paramToSink,
|
|
516
|
+
sourceToReturn,
|
|
517
|
+
sourceToCallArg,
|
|
518
|
+
callResults: callResultsOut,
|
|
519
|
+
},
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
/** Argument positions where binding `b` occurs (direct or via a nested site). */
|
|
523
|
+
function occurrencesInArgs(site, b) {
|
|
524
|
+
const hits = [];
|
|
525
|
+
site.args?.forEach((entries, argPos) => {
|
|
526
|
+
for (const e of entries) {
|
|
527
|
+
if (typeof e === 'number') {
|
|
528
|
+
if (e === b)
|
|
529
|
+
hits.push(argPos);
|
|
530
|
+
}
|
|
531
|
+
else if (e[0] === b) {
|
|
532
|
+
hits.push(argPos);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
return hits;
|
|
537
|
+
}
|