@clear-capabilities/agentic-security-scanner 0.195.23 → 0.195.24
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/CHANGELOG.md +13 -0
- package/dist/3596.index.js +1 -1
- package/dist/agentic-security.mjs +9 -9
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/{01087ded263953062654.json → daefda0819bb18309b75.json} +2 -2
- package/package.json +2 -2
- package/src/lineage/CLAUDE.md +12 -0
- package/src/lineage/DESIGN_GRAPH_BUILDER.md +23 -0
- package/src/lineage/driver.js +19 -2
- package/src/lineage/engine.js +24 -1
- package/src/lineage/graph-builder.js +3 -1
- package/src/lineage/lineage-source-catalog.js +143 -0
- package/src/lineage/source-seeding.js +142 -16
- package/src/lineage/summaries.js +21 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
7a00dab21c958eb07e821a422efc871b2ceee9c84e4405ae9754390ec874c165 agentic-security.mjs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clear-capabilities/agentic-security-scanner",
|
|
3
|
-
"version": "0.195.
|
|
3
|
+
"version": "0.195.24",
|
|
4
4
|
"description": "Scanner engine for the agentic-security Claude Code plugin: SAST, SCA with reachability and CISA KEV, secrets, IaC, Haskell and Nix/NixOS, prompt-injection, MCP/agent-tool audit, authZ analysis, SBOM, compliance attestation, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"test:dataflow": "node --test test/privacy-catalog.test.js test/privacy-deep-walker.test.js test/privacy-deep-e2e.test.js test/container-taint.test.js test/stored-taint-families.test.js test/py-annotation-sources.test.js test/fn-reach.test.js test/deep-taint.test.js test/calibration.test.js test/holdout-eval.test.js test/cross-lang-meta.test.js test/cross-lang-queues.test.js test/phase5-xlang.test.js test/phase5-coverage.test.js test/phase6-taint.test.js test/llm-validator-consistency.test.js test/llm-validator-default-on.test.js test/llm-validator-preset.test.js test/parser-py-cst.test.js test/chrome-probe.test.js test/parser-cs-kt.test.js test/parser-cs-annotations.test.js test/parser-cs-control-flow.test.js test/parser-kt-control-flow.test.js test/parser-js-annotations.test.js test/parser-java-annotations.test.js test/parser-java-control-flow.test.js test/parser-java-calls.test.js test/parser-go.test.js test/parser-rust.test.js test/rust-taint-flow.test.js test/parser-php-rb.test.js test/parser-php-control-flow.test.js test/interproc-k2.test.js test/proven-clean.test.js test/backward-default.test.js test/incremental-cache.test.js test/string-regex-lattice.test.js test/closure-capture.test.js test/points-to.test.js test/type-stubs.test.js test/soft-taint.test.js test/ifds.test.js test/symbolic-exec-proof.test.js test/ifds-summary-edges.test.js test/stub-aware-filter.test.js test/cross-repo.test.js test/proof-gate.test.js test/layer-recall.test.js test/java-taint-flow.test.js test/kt-taint-flow.test.js test/sanitizer-typed-flow.test.js test/sanitizer-gate-unit.test.js test/proof-safe.test.js test/collection-taint.test.js test/kcfa-context.test.js test/kcfa-callstring.test.js test/flow-parity.test.js test/callgraph-resolve.test.js test/import-reachability.test.js test/ir-stats.test.js test/parser-cpp.test.js test/parser-js-decorators.test.js test/parser-js-if-else-cfg.test.js test/cpp-integration.test.js test/engine-reconnect.test.js test/phase2-scoping.test.js test/engine-recall.test.js test/catalog-annotation-source.test.js test/catalog-cs-p1.test.js test/catalog-expanded.test.js test/builtin-summaries.test.js test/interproc-mutation-crash.test.js test/mutated-param-taint.test.js test/return-sink-check.test.js test/higher-order-inline-callback.test.js test/interproc-findings-discard.test.js test/runscan-deep-option.test.js test/summary-cache-convergence.test.js test/parser-rb-calls.test.js test/parser-rb-module-level.test.js test/balanced-call.test.js test/destructuring-taint.test.js test/computed-member-taint.test.js test/string-interpolation-taint.test.js test/dataflow-deadcode-severity.test.js test/catalog-dotted-callee-lookup.test.js test/builtin-summary-family-blind-kill.test.js test/deep-mode-annotator-pipeline.test.js test/receiver-type-and-nested-calls.test.js test/member-write-and-loop-taint.test.js test/parser-py-module-level.test.js test/parser-php-module-level.test.js test/r14b-module-level-e2e.test.js test/annotation-taint-engine.test.js test/interproc-nested-call-taint.test.js test/catalog-xpath-injection.test.js test/catalog-ldap-injection.test.js test/catalog-xxe.test.js test/catalog-response-splitting.test.js test/catalog-code-injection.test.js test/parser-rb-control-flow.test.js test/catalog-ruby-p1.test.js test/callee-receiver-taint.test.js test/catalog-command-injection-p4.test.js test/catalog-xss-p4.test.js test/catalog-path-ssrf-p3.test.js test/catalog-ai-model-provider-precision.test.js test/catalog-orm-write.test.js test/dataflow/provenance-pipeline-integration.test.js test/dataflow-progress.test.js test/scan-progress.test.js test/sard-leakage-pipeline.test.js test/sard-split-scoring.test.js test/sard-llm-isolation.test.js test/sard-mutation-adversarial.test.js test/sard-baseline-adaptive-tolerance.test.js test/sard-independent-holdout.test.js test/sard-flow-aware-scoring.test.js test/sard-macro-score-support-floor.test.js test/sard-php-split.test.js test/parser-java-nary-concat.test.js test/sard-naive-baseline.test.js test/field-taint-cross-method.test.js test/field-taint-engine-unit.test.js test/coercion-sanitizer-kill.test.js test/constant-propagation-path-sensitivity.test.js test/parser-java-assignments.test.js test/catalog-java-sard.test.js test/parser-cs-juliet-shapes.test.js test/catalog-cs-sard-p2.test.js test/sard-cwe-key-merge.test.js test/parser-php-backtick.test.js test/parser-php-include.test.js test/php-include-merge.test.js test/parser-php-array-field-taint.test.js test/parser-php-cond-assign.test.js test/parser-rust-redos.test.js test/multi-sink-taint-chain.test.js test/static-field-cross-class-call-string.test.js test/regex-guard-narrowing.test.js test/object-instance-field-taint.test.js test/param-type-inference.test.js test/smt-path-feasibility.test.js test/cross-service-taint.test.js test/dataflow-expr-taint-scaling.test.js test/evaluation/guard-dominance.test.js test/evaluation/engine-mechanisms.test.js test/rx-exec-receiver.test.js test/incremental-dependency.test.js test/incremental-integrity.test.js test/demand-query.test.js test/ir-cache.test.js test/canonicalize-parity.test.js test/dataflow-source-attribution.test.js",
|
|
91
91
|
"test:mcp": "node --test test/mcp.test.js test/mcp-apply-fix-multifile.test.js test/mcp-protocol-smoke.test.js test/mcp-audit.test.js test/audit-cli.test.js test/mcp-scratchpad.test.js test/mcp-offload.test.js test/sca-upgrade.test.js test/lsp-server.test.js test/mcp-dataflow-tools.test.js test/cli/governance-propose-edit.test.js test/cli/remediation-open-update.test.js test/cli/remediation-verify-reopen.test.js test/cli/dataflow-observations.test.js test/cli/federate-declare-list.test.js test/capabilities/tools.test.js",
|
|
92
92
|
"test:report": "node --test test/report-scan-invariants.test.js test/junit.test.js test/ci.test.js test/poc-generator.test.js test/verifier.test.js test/verifier-target.test.js test/annotator-errors.test.js test/git-history-batch.test.js test/default-scan-drift.test.js test/bundle-optional-deps.test.js test/scan-health.test.js test/finding-schema.test.js test/pipeline/finding-schema-provenance.test.js test/evidence-provenance.test.js test/report/provenance-output.test.js test/report/provenance-format-parity.test.js test/legacy-compat.test.js test/annotator-runner.test.js test/detector-runner.test.js test/detector-errors.test.js test/analyzer-supervisor.test.js test/cascade-worker-pool.test.js test/cascade-pool-wiring.test.js test/coverage-ledger.test.js test/coverage-ledger-completeness.test.js test/assurance-mode.test.js test/artifact-registry.test.js test/artifact-registry-completeness.test.js test/retention-policy.test.js test/state-lifecycle-report.test.js test/legal-hold.test.js test/production-feedback.test.js test/calibration-feedback.test.js test/encryption-provider.test.js test/grader-calibration.test.js test/pr-delta-gate.test.js test/vex.test.js test/report-render.test.js test/oscal-conformance.test.js test/cli-fix-apply.test.js test/apply-fix-service.test.js test/high-impact-approval-gate.test.js test/approver-registry.test.js test/suppression-exceptions.test.js test/fix-history.test.js test/producer-registry.test.js test/producer-collector.test.js test/enrichment-completion.test.js",
|
|
93
|
-
"test:lineage": "node --test test/lineage/analysis-completion.test.js test/lineage/schema.test.js test/lineage/ids.test.js test/lineage/protection.test.js test/lineage/classification.test.js test/lineage/validate.test.js test/lineage/json-schema-parity.test.js test/lineage/flagship-fixture.test.js test/lineage/flagship-fixture-semantics.test.js test/lineage/field-identity.test.js test/lineage/engine-expr-resolver.test.js test/lineage/engine-walker.test.js test/lineage/engine-integration.test.js test/lineage/engine-receiver-identity.test.js test/lineage/summaries.test.js test/lineage/driver.test.js test/lineage/engine-provenance.test.js test/lineage/engine-provenance-interprocedural.test.js test/lineage/path-store.test.js test/lineage/path-query.test.js test/lineage/flow-grade.test.js test/lineage/source-registry.test.js test/lineage/sink-registry.test.js test/lineage/transform-catalog.test.js test/lineage/handling-analyzer.test.js test/lineage/registry-real-code.test.js test/lineage/source-seeding.test.js test/lineage/graph-builder.test.js test/lineage/coverage.test.js test/lineage/resolve-destination.test.js test/lineage/queue-detail.test.js test/lineage/index.test.js test/lineage/obligation-mapping.test.js test/lineage/obligation-predicates.test.js test/lineage/obligation-predicates-walkthrough.test.js test/lineage/transit-protection.test.js test/lineage/at-rest-protection.test.js test/lineage/protection-summary.test.js test/lineage/ac01-multi-sink.test.js test/lineage/policy-verdict.test.js test/lineage/governance-refs.test.js test/lineage/export-privacy.test.js test/lineage/export-json.test.js test/lineage/export-csv.test.js test/lineage/decision-story.test.js test/lineage/export-briefing.test.js test/lineage/graph-snapshot.test.js test/lineage/graph-diff.test.js test/lineage/drift-policy.test.js test/lineage/recipient-profile.test.js test/lineage/recipient-registry.test.js test/lineage/recipient-wiring.test.js test/lineage/redact-graph.test.js test/lineage-scan-wiring.test.js test/lineage-artifact-write.test.js test/bench-data-lineage-runner.test.js test/bundle-frontend.test.js test/bundle-frontend-golden.test.js test/generate-html-report.test.js test/export-image.test.js test/lineage/scenario.test.js test/lineage/scenario-engine.test.js test/lineage/scenario-diff.test.js test/lineage/scenario-no-obligation-wiring.test.js test/lineage/impact-assessment.test.js test/lineage/impact-engine.test.js test/lineage/impact-no-obligation-wiring.test.js test/lineage/governance-edit.test.js test/lineage/remediation.test.js test/lineage/runtime-observation.test.js test/lineage/observation-correlation.test.js test/lineage/observation-adapters.test.js test/lineage/observation-store.test.js test/lineage/runtime-corroboration-wiring.test.js test/lineage/cross-repo-link.test.js test/lineage/federation-loader.test.js test/lineage/cross-repo-link-wiring.test.js test/lineage/runtime-assurance.test.js test/lineage/runtime-edge-states.test.js test/lineage/runtime-explorer-view.test.js test/cli/runtime-explorer-cli.test.js test/cli/runtime-identity-cli.test.js test/cli/runtime-twin-cli.test.js test/lineage/cut-planner.test.js test/lineage/scc.test.js test/lineage/scc-solver.test.js test/graph/graph-views.test.js test/lineage/dag07-completion.test.js test/lineage/owned-build.test.js test/dataflow/boundary-propagation.test.js test/lineage/boundary-contract.test.js test/lineage/correlation-v2.test.js test/lineage/path-control-evidence.test.js test/lineage/smt-z3-backend.test.js test/cli/dag-commands.test.js",
|
|
93
|
+
"test:lineage": "node --test test/lineage/analysis-completion.test.js test/lineage/schema.test.js test/lineage/ids.test.js test/lineage/protection.test.js test/lineage/classification.test.js test/lineage/validate.test.js test/lineage/json-schema-parity.test.js test/lineage/flagship-fixture.test.js test/lineage/flagship-fixture-semantics.test.js test/lineage/field-identity.test.js test/lineage/engine-expr-resolver.test.js test/lineage/engine-walker.test.js test/lineage/engine-integration.test.js test/lineage/engine-receiver-identity.test.js test/lineage/summaries.test.js test/lineage/driver.test.js test/lineage/engine-provenance.test.js test/lineage/engine-provenance-interprocedural.test.js test/lineage/path-store.test.js test/lineage/path-query.test.js test/lineage/flow-grade.test.js test/lineage/source-registry.test.js test/lineage/sink-registry.test.js test/lineage/transform-catalog.test.js test/lineage/handling-analyzer.test.js test/lineage/registry-real-code.test.js test/lineage/source-seeding.test.js test/lineage/seeding-across-calls.test.js test/lineage/graph-builder.test.js test/lineage/coverage.test.js test/lineage/resolve-destination.test.js test/lineage/queue-detail.test.js test/lineage/index.test.js test/lineage/obligation-mapping.test.js test/lineage/obligation-predicates.test.js test/lineage/obligation-predicates-walkthrough.test.js test/lineage/transit-protection.test.js test/lineage/at-rest-protection.test.js test/lineage/protection-summary.test.js test/lineage/ac01-multi-sink.test.js test/lineage/policy-verdict.test.js test/lineage/governance-refs.test.js test/lineage/export-privacy.test.js test/lineage/export-json.test.js test/lineage/export-csv.test.js test/lineage/decision-story.test.js test/lineage/export-briefing.test.js test/lineage/graph-snapshot.test.js test/lineage/graph-diff.test.js test/lineage/drift-policy.test.js test/lineage/recipient-profile.test.js test/lineage/recipient-registry.test.js test/lineage/recipient-wiring.test.js test/lineage/redact-graph.test.js test/lineage-scan-wiring.test.js test/lineage-artifact-write.test.js test/bench-data-lineage-runner.test.js test/bundle-frontend.test.js test/bundle-frontend-golden.test.js test/generate-html-report.test.js test/export-image.test.js test/lineage/scenario.test.js test/lineage/scenario-engine.test.js test/lineage/scenario-diff.test.js test/lineage/scenario-no-obligation-wiring.test.js test/lineage/impact-assessment.test.js test/lineage/impact-engine.test.js test/lineage/impact-no-obligation-wiring.test.js test/lineage/governance-edit.test.js test/lineage/remediation.test.js test/lineage/runtime-observation.test.js test/lineage/observation-correlation.test.js test/lineage/observation-adapters.test.js test/lineage/observation-store.test.js test/lineage/runtime-corroboration-wiring.test.js test/lineage/cross-repo-link.test.js test/lineage/federation-loader.test.js test/lineage/cross-repo-link-wiring.test.js test/lineage/runtime-assurance.test.js test/lineage/runtime-edge-states.test.js test/lineage/runtime-explorer-view.test.js test/cli/runtime-explorer-cli.test.js test/cli/runtime-identity-cli.test.js test/cli/runtime-twin-cli.test.js test/lineage/cut-planner.test.js test/lineage/scc.test.js test/lineage/scc-solver.test.js test/graph/graph-views.test.js test/lineage/dag07-completion.test.js test/lineage/owned-build.test.js test/dataflow/boundary-propagation.test.js test/lineage/boundary-contract.test.js test/lineage/correlation-v2.test.js test/lineage/path-control-evidence.test.js test/lineage/smt-z3-backend.test.js test/cli/dag-commands.test.js",
|
|
94
94
|
"test:bench-modules": "node --test test/phase4-harness.test.js test/pipeline.test.js test/proof-corpus-lib.test.js test/proof-corpus-runner.test.js",
|
|
95
95
|
"test:server": "node --test test/server/graph-loader.test.js test/server/security.test.js test/server/routes.test.js test/server/http-server.test.js test/server/cmd-explore.test.js test/server/cmd-dataflow-export.test.js test/server/static-assets.test.js test/server/dataflow-scenario-cli.test.js test/server/dataflow-impact-cli.test.js",
|
|
96
96
|
"test:haskell": "node --test test/haskell/haskell-reachability.test.js test/haskell/haskell-real-code-shapes.test.js test/haskell/haskell-unseen-v5-shapes.test.js test/haskell/haskell-unseen-v4-shapes.test.js test/haskell/haskell-alias-taint.test.js test/haskell/haskell-weak-hash-names.test.js test/haskell/haskell-models-extended.test.js test/haskell/haskell-rules-extended.test.js test/haskell/haskell-injection.test.js test/haskell/haskell-ir.test.js test/haskell/haskell-manifests.test.js test/haskell/haskell-parser.test.js test/haskell/haskell-remediation.test.js test/haskell/haskell-remediation-0190.test.js test/haskell/haskell-remediation-kdf.test.js test/haskell/haskell-remediation-html-text.test.js test/haskell/haskell-remediation-scotty-body.test.js test/haskell/haskell-remediation-entropy.test.js test/haskell/haskell-remediation-text-command.test.js test/haskell/haskell-remediation-path-contain.test.js test/haskell/haskell-remediation-0195-16.test.js test/haskell/haskell-taint-shapes.test.js test/haskell/haskell-real-advisory-shapes.test.js test/haskell/haskell-discovery-shapes.test.js test/haskell/haskell-quasiquote-interp.test.js test/haskell/haskell-record-dot.test.js test/haskell/haskell-global-cell.test.js test/haskell/haskell-syntax-extensions.test.js test/haskell/haskell-rule-precision.test.js test/haskell/haskell-resolved-graph.test.js test/haskell/haskell-sca.test.js test/haskell/haskell-sca-severity.test.js test/haskell/haskell-web-guards.test.js test/haskell/haskell-advisory-feed.test.js test/haskell/haskell-security-rules.test.js test/haskell/haskell-taint.test.js test/haskell/haskell-boundaries.test.js test/haskell/haskell-live-feed-scan.test.js test/haskell/haskell-supply-bounds.test.js test/haskell/haskell-ir-scaling.test.js test/haskell/haskell-boundary-propagation.test.js test/haskell/haskell-generalization.test.js test/haskell/haskell-trust-boundary.test.js test/haskell/haskell-trust-fixed-file.test.js test/haskell/haskell-predictable-random-flow.test.js test/haskell/haskell-comment-invariance.test.js test/haskell/haskell-remediation-classes.test.js test/haskell/haskell-trust-boundary-frameworks.test.js test/haskell/haskell-generic-detectors.test.js",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clear-capabilities/agentic-security-scanner",
|
|
3
|
-
"version": "0.195.
|
|
3
|
+
"version": "0.195.24",
|
|
4
4
|
"description": "Scanner engine for the agentic-security Claude Code plugin: SAST, SCA with reachability and CISA KEV, secrets, IaC, Haskell and Nix/NixOS, prompt-injection, MCP/agent-tool audit, authZ analysis, SBOM, compliance attestation, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"test:dataflow": "node --test test/privacy-catalog.test.js test/privacy-deep-walker.test.js test/privacy-deep-e2e.test.js test/container-taint.test.js test/stored-taint-families.test.js test/py-annotation-sources.test.js test/fn-reach.test.js test/deep-taint.test.js test/calibration.test.js test/holdout-eval.test.js test/cross-lang-meta.test.js test/cross-lang-queues.test.js test/phase5-xlang.test.js test/phase5-coverage.test.js test/phase6-taint.test.js test/llm-validator-consistency.test.js test/llm-validator-default-on.test.js test/llm-validator-preset.test.js test/parser-py-cst.test.js test/chrome-probe.test.js test/parser-cs-kt.test.js test/parser-cs-annotations.test.js test/parser-cs-control-flow.test.js test/parser-kt-control-flow.test.js test/parser-js-annotations.test.js test/parser-java-annotations.test.js test/parser-java-control-flow.test.js test/parser-java-calls.test.js test/parser-go.test.js test/parser-rust.test.js test/rust-taint-flow.test.js test/parser-php-rb.test.js test/parser-php-control-flow.test.js test/interproc-k2.test.js test/proven-clean.test.js test/backward-default.test.js test/incremental-cache.test.js test/string-regex-lattice.test.js test/closure-capture.test.js test/points-to.test.js test/type-stubs.test.js test/soft-taint.test.js test/ifds.test.js test/symbolic-exec-proof.test.js test/ifds-summary-edges.test.js test/stub-aware-filter.test.js test/cross-repo.test.js test/proof-gate.test.js test/layer-recall.test.js test/java-taint-flow.test.js test/kt-taint-flow.test.js test/sanitizer-typed-flow.test.js test/sanitizer-gate-unit.test.js test/proof-safe.test.js test/collection-taint.test.js test/kcfa-context.test.js test/kcfa-callstring.test.js test/flow-parity.test.js test/callgraph-resolve.test.js test/import-reachability.test.js test/ir-stats.test.js test/parser-cpp.test.js test/parser-js-decorators.test.js test/parser-js-if-else-cfg.test.js test/cpp-integration.test.js test/engine-reconnect.test.js test/phase2-scoping.test.js test/engine-recall.test.js test/catalog-annotation-source.test.js test/catalog-cs-p1.test.js test/catalog-expanded.test.js test/builtin-summaries.test.js test/interproc-mutation-crash.test.js test/mutated-param-taint.test.js test/return-sink-check.test.js test/higher-order-inline-callback.test.js test/interproc-findings-discard.test.js test/runscan-deep-option.test.js test/summary-cache-convergence.test.js test/parser-rb-calls.test.js test/parser-rb-module-level.test.js test/balanced-call.test.js test/destructuring-taint.test.js test/computed-member-taint.test.js test/string-interpolation-taint.test.js test/dataflow-deadcode-severity.test.js test/catalog-dotted-callee-lookup.test.js test/builtin-summary-family-blind-kill.test.js test/deep-mode-annotator-pipeline.test.js test/receiver-type-and-nested-calls.test.js test/member-write-and-loop-taint.test.js test/parser-py-module-level.test.js test/parser-php-module-level.test.js test/r14b-module-level-e2e.test.js test/annotation-taint-engine.test.js test/interproc-nested-call-taint.test.js test/catalog-xpath-injection.test.js test/catalog-ldap-injection.test.js test/catalog-xxe.test.js test/catalog-response-splitting.test.js test/catalog-code-injection.test.js test/parser-rb-control-flow.test.js test/catalog-ruby-p1.test.js test/callee-receiver-taint.test.js test/catalog-command-injection-p4.test.js test/catalog-xss-p4.test.js test/catalog-path-ssrf-p3.test.js test/catalog-ai-model-provider-precision.test.js test/catalog-orm-write.test.js test/dataflow/provenance-pipeline-integration.test.js test/dataflow-progress.test.js test/scan-progress.test.js test/sard-leakage-pipeline.test.js test/sard-split-scoring.test.js test/sard-llm-isolation.test.js test/sard-mutation-adversarial.test.js test/sard-baseline-adaptive-tolerance.test.js test/sard-independent-holdout.test.js test/sard-flow-aware-scoring.test.js test/sard-macro-score-support-floor.test.js test/sard-php-split.test.js test/parser-java-nary-concat.test.js test/sard-naive-baseline.test.js test/field-taint-cross-method.test.js test/field-taint-engine-unit.test.js test/coercion-sanitizer-kill.test.js test/constant-propagation-path-sensitivity.test.js test/parser-java-assignments.test.js test/catalog-java-sard.test.js test/parser-cs-juliet-shapes.test.js test/catalog-cs-sard-p2.test.js test/sard-cwe-key-merge.test.js test/parser-php-backtick.test.js test/parser-php-include.test.js test/php-include-merge.test.js test/parser-php-array-field-taint.test.js test/parser-php-cond-assign.test.js test/parser-rust-redos.test.js test/multi-sink-taint-chain.test.js test/static-field-cross-class-call-string.test.js test/regex-guard-narrowing.test.js test/object-instance-field-taint.test.js test/param-type-inference.test.js test/smt-path-feasibility.test.js test/cross-service-taint.test.js test/dataflow-expr-taint-scaling.test.js test/evaluation/guard-dominance.test.js test/evaluation/engine-mechanisms.test.js test/rx-exec-receiver.test.js test/incremental-dependency.test.js test/incremental-integrity.test.js test/demand-query.test.js test/ir-cache.test.js test/canonicalize-parity.test.js test/dataflow-source-attribution.test.js",
|
|
91
91
|
"test:mcp": "node --test test/mcp.test.js test/mcp-apply-fix-multifile.test.js test/mcp-protocol-smoke.test.js test/mcp-audit.test.js test/audit-cli.test.js test/mcp-scratchpad.test.js test/mcp-offload.test.js test/sca-upgrade.test.js test/lsp-server.test.js test/mcp-dataflow-tools.test.js test/cli/governance-propose-edit.test.js test/cli/remediation-open-update.test.js test/cli/remediation-verify-reopen.test.js test/cli/dataflow-observations.test.js test/cli/federate-declare-list.test.js test/capabilities/tools.test.js",
|
|
92
92
|
"test:report": "node --test test/report-scan-invariants.test.js test/junit.test.js test/ci.test.js test/poc-generator.test.js test/verifier.test.js test/verifier-target.test.js test/annotator-errors.test.js test/git-history-batch.test.js test/default-scan-drift.test.js test/bundle-optional-deps.test.js test/scan-health.test.js test/finding-schema.test.js test/pipeline/finding-schema-provenance.test.js test/evidence-provenance.test.js test/report/provenance-output.test.js test/report/provenance-format-parity.test.js test/legacy-compat.test.js test/annotator-runner.test.js test/detector-runner.test.js test/detector-errors.test.js test/analyzer-supervisor.test.js test/cascade-worker-pool.test.js test/cascade-pool-wiring.test.js test/coverage-ledger.test.js test/coverage-ledger-completeness.test.js test/assurance-mode.test.js test/artifact-registry.test.js test/artifact-registry-completeness.test.js test/retention-policy.test.js test/state-lifecycle-report.test.js test/legal-hold.test.js test/production-feedback.test.js test/calibration-feedback.test.js test/encryption-provider.test.js test/grader-calibration.test.js test/pr-delta-gate.test.js test/vex.test.js test/report-render.test.js test/oscal-conformance.test.js test/cli-fix-apply.test.js test/apply-fix-service.test.js test/high-impact-approval-gate.test.js test/approver-registry.test.js test/suppression-exceptions.test.js test/fix-history.test.js test/producer-registry.test.js test/producer-collector.test.js test/enrichment-completion.test.js",
|
|
93
|
-
"test:lineage": "node --test test/lineage/analysis-completion.test.js test/lineage/schema.test.js test/lineage/ids.test.js test/lineage/protection.test.js test/lineage/classification.test.js test/lineage/validate.test.js test/lineage/json-schema-parity.test.js test/lineage/flagship-fixture.test.js test/lineage/flagship-fixture-semantics.test.js test/lineage/field-identity.test.js test/lineage/engine-expr-resolver.test.js test/lineage/engine-walker.test.js test/lineage/engine-integration.test.js test/lineage/engine-receiver-identity.test.js test/lineage/summaries.test.js test/lineage/driver.test.js test/lineage/engine-provenance.test.js test/lineage/engine-provenance-interprocedural.test.js test/lineage/path-store.test.js test/lineage/path-query.test.js test/lineage/flow-grade.test.js test/lineage/source-registry.test.js test/lineage/sink-registry.test.js test/lineage/transform-catalog.test.js test/lineage/handling-analyzer.test.js test/lineage/registry-real-code.test.js test/lineage/source-seeding.test.js test/lineage/graph-builder.test.js test/lineage/coverage.test.js test/lineage/resolve-destination.test.js test/lineage/queue-detail.test.js test/lineage/index.test.js test/lineage/obligation-mapping.test.js test/lineage/obligation-predicates.test.js test/lineage/obligation-predicates-walkthrough.test.js test/lineage/transit-protection.test.js test/lineage/at-rest-protection.test.js test/lineage/protection-summary.test.js test/lineage/ac01-multi-sink.test.js test/lineage/policy-verdict.test.js test/lineage/governance-refs.test.js test/lineage/export-privacy.test.js test/lineage/export-json.test.js test/lineage/export-csv.test.js test/lineage/decision-story.test.js test/lineage/export-briefing.test.js test/lineage/graph-snapshot.test.js test/lineage/graph-diff.test.js test/lineage/drift-policy.test.js test/lineage/recipient-profile.test.js test/lineage/recipient-registry.test.js test/lineage/recipient-wiring.test.js test/lineage/redact-graph.test.js test/lineage-scan-wiring.test.js test/lineage-artifact-write.test.js test/bench-data-lineage-runner.test.js test/bundle-frontend.test.js test/bundle-frontend-golden.test.js test/generate-html-report.test.js test/export-image.test.js test/lineage/scenario.test.js test/lineage/scenario-engine.test.js test/lineage/scenario-diff.test.js test/lineage/scenario-no-obligation-wiring.test.js test/lineage/impact-assessment.test.js test/lineage/impact-engine.test.js test/lineage/impact-no-obligation-wiring.test.js test/lineage/governance-edit.test.js test/lineage/remediation.test.js test/lineage/runtime-observation.test.js test/lineage/observation-correlation.test.js test/lineage/observation-adapters.test.js test/lineage/observation-store.test.js test/lineage/runtime-corroboration-wiring.test.js test/lineage/cross-repo-link.test.js test/lineage/federation-loader.test.js test/lineage/cross-repo-link-wiring.test.js test/lineage/runtime-assurance.test.js test/lineage/runtime-edge-states.test.js test/lineage/runtime-explorer-view.test.js test/cli/runtime-explorer-cli.test.js test/cli/runtime-identity-cli.test.js test/cli/runtime-twin-cli.test.js test/lineage/cut-planner.test.js test/lineage/scc.test.js test/lineage/scc-solver.test.js test/graph/graph-views.test.js test/lineage/dag07-completion.test.js test/lineage/owned-build.test.js test/dataflow/boundary-propagation.test.js test/lineage/boundary-contract.test.js test/lineage/correlation-v2.test.js test/lineage/path-control-evidence.test.js test/lineage/smt-z3-backend.test.js test/cli/dag-commands.test.js",
|
|
93
|
+
"test:lineage": "node --test test/lineage/analysis-completion.test.js test/lineage/schema.test.js test/lineage/ids.test.js test/lineage/protection.test.js test/lineage/classification.test.js test/lineage/validate.test.js test/lineage/json-schema-parity.test.js test/lineage/flagship-fixture.test.js test/lineage/flagship-fixture-semantics.test.js test/lineage/field-identity.test.js test/lineage/engine-expr-resolver.test.js test/lineage/engine-walker.test.js test/lineage/engine-integration.test.js test/lineage/engine-receiver-identity.test.js test/lineage/summaries.test.js test/lineage/driver.test.js test/lineage/engine-provenance.test.js test/lineage/engine-provenance-interprocedural.test.js test/lineage/path-store.test.js test/lineage/path-query.test.js test/lineage/flow-grade.test.js test/lineage/source-registry.test.js test/lineage/sink-registry.test.js test/lineage/transform-catalog.test.js test/lineage/handling-analyzer.test.js test/lineage/registry-real-code.test.js test/lineage/source-seeding.test.js test/lineage/seeding-across-calls.test.js test/lineage/graph-builder.test.js test/lineage/coverage.test.js test/lineage/resolve-destination.test.js test/lineage/queue-detail.test.js test/lineage/index.test.js test/lineage/obligation-mapping.test.js test/lineage/obligation-predicates.test.js test/lineage/obligation-predicates-walkthrough.test.js test/lineage/transit-protection.test.js test/lineage/at-rest-protection.test.js test/lineage/protection-summary.test.js test/lineage/ac01-multi-sink.test.js test/lineage/policy-verdict.test.js test/lineage/governance-refs.test.js test/lineage/export-privacy.test.js test/lineage/export-json.test.js test/lineage/export-csv.test.js test/lineage/decision-story.test.js test/lineage/export-briefing.test.js test/lineage/graph-snapshot.test.js test/lineage/graph-diff.test.js test/lineage/drift-policy.test.js test/lineage/recipient-profile.test.js test/lineage/recipient-registry.test.js test/lineage/recipient-wiring.test.js test/lineage/redact-graph.test.js test/lineage-scan-wiring.test.js test/lineage-artifact-write.test.js test/bench-data-lineage-runner.test.js test/bundle-frontend.test.js test/bundle-frontend-golden.test.js test/generate-html-report.test.js test/export-image.test.js test/lineage/scenario.test.js test/lineage/scenario-engine.test.js test/lineage/scenario-diff.test.js test/lineage/scenario-no-obligation-wiring.test.js test/lineage/impact-assessment.test.js test/lineage/impact-engine.test.js test/lineage/impact-no-obligation-wiring.test.js test/lineage/governance-edit.test.js test/lineage/remediation.test.js test/lineage/runtime-observation.test.js test/lineage/observation-correlation.test.js test/lineage/observation-adapters.test.js test/lineage/observation-store.test.js test/lineage/runtime-corroboration-wiring.test.js test/lineage/cross-repo-link.test.js test/lineage/federation-loader.test.js test/lineage/cross-repo-link-wiring.test.js test/lineage/runtime-assurance.test.js test/lineage/runtime-edge-states.test.js test/lineage/runtime-explorer-view.test.js test/cli/runtime-explorer-cli.test.js test/cli/runtime-identity-cli.test.js test/cli/runtime-twin-cli.test.js test/lineage/cut-planner.test.js test/lineage/scc.test.js test/lineage/scc-solver.test.js test/graph/graph-views.test.js test/lineage/dag07-completion.test.js test/lineage/owned-build.test.js test/dataflow/boundary-propagation.test.js test/lineage/boundary-contract.test.js test/lineage/correlation-v2.test.js test/lineage/path-control-evidence.test.js test/lineage/smt-z3-backend.test.js test/cli/dag-commands.test.js",
|
|
94
94
|
"test:bench-modules": "node --test test/phase4-harness.test.js test/pipeline.test.js test/proof-corpus-lib.test.js test/proof-corpus-runner.test.js",
|
|
95
95
|
"test:server": "node --test test/server/graph-loader.test.js test/server/security.test.js test/server/routes.test.js test/server/http-server.test.js test/server/cmd-explore.test.js test/server/cmd-dataflow-export.test.js test/server/static-assets.test.js test/server/dataflow-scenario-cli.test.js test/server/dataflow-impact-cli.test.js",
|
|
96
96
|
"test:haskell": "node --test test/haskell/haskell-reachability.test.js test/haskell/haskell-real-code-shapes.test.js test/haskell/haskell-unseen-v5-shapes.test.js test/haskell/haskell-unseen-v4-shapes.test.js test/haskell/haskell-alias-taint.test.js test/haskell/haskell-weak-hash-names.test.js test/haskell/haskell-models-extended.test.js test/haskell/haskell-rules-extended.test.js test/haskell/haskell-injection.test.js test/haskell/haskell-ir.test.js test/haskell/haskell-manifests.test.js test/haskell/haskell-parser.test.js test/haskell/haskell-remediation.test.js test/haskell/haskell-remediation-0190.test.js test/haskell/haskell-remediation-kdf.test.js test/haskell/haskell-remediation-html-text.test.js test/haskell/haskell-remediation-scotty-body.test.js test/haskell/haskell-remediation-entropy.test.js test/haskell/haskell-remediation-text-command.test.js test/haskell/haskell-remediation-path-contain.test.js test/haskell/haskell-remediation-0195-16.test.js test/haskell/haskell-taint-shapes.test.js test/haskell/haskell-real-advisory-shapes.test.js test/haskell/haskell-discovery-shapes.test.js test/haskell/haskell-quasiquote-interp.test.js test/haskell/haskell-record-dot.test.js test/haskell/haskell-global-cell.test.js test/haskell/haskell-syntax-extensions.test.js test/haskell/haskell-rule-precision.test.js test/haskell/haskell-resolved-graph.test.js test/haskell/haskell-sca.test.js test/haskell/haskell-sca-severity.test.js test/haskell/haskell-web-guards.test.js test/haskell/haskell-advisory-feed.test.js test/haskell/haskell-security-rules.test.js test/haskell/haskell-taint.test.js test/haskell/haskell-boundaries.test.js test/haskell/haskell-live-feed-scan.test.js test/haskell/haskell-supply-bounds.test.js test/haskell/haskell-ir-scaling.test.js test/haskell/haskell-boundary-propagation.test.js test/haskell/haskell-generalization.test.js test/haskell/haskell-trust-boundary.test.js test/haskell/haskell-trust-fixed-file.test.js test/haskell/haskell-predictable-random-flow.test.js test/haskell/haskell-comment-invariance.test.js test/haskell/haskell-remediation-classes.test.js test/haskell/haskell-trust-boundary-frameworks.test.js test/haskell/haskell-generic-detectors.test.js",
|
package/src/lineage/CLAUDE.md
CHANGED
|
@@ -1328,3 +1328,15 @@ report would read as a real-code measurement. No taint-recall tier is borrowed.
|
|
|
1328
1328
|
## Runtime twin additions (PRD 2 gap closure)
|
|
1329
1329
|
|
|
1330
1330
|
`runtime-edge-states.js` derives the six edge states and the identity / absent-from-static queries from the existing correlation, as a pure read: nothing is written into the static graph. `RUNTIME_EXPLOIT_CORRELATED` is never inferred from observation; it needs validated operator evidence. `identity-token.js` supplies a keyed-hash tokenizer injected into the adapters through `context.tokenizeIdentity`; the adapters stay zero-import and drop identity when no tokenizer is given. The attribute allowlist test pins `identity.token`; widen it deliberately, never by loosening the grammar.
|
|
1331
|
+
|
|
1332
|
+
## Lineage sources across aliases and calls (GA-03.1, 0.195.24)
|
|
1333
|
+
|
|
1334
|
+
The data-lineage corpus (`bench/data-lineage/`) went from 17 of 24 to 24 of 24, every fixture regression-tier, through three engine changes and one runner change. Each is measured by `test/lineage/seeding-across-calls.test.js`.
|
|
1335
|
+
|
|
1336
|
+
| Module | Responsibility |
|
|
1337
|
+
|---|---|
|
|
1338
|
+
| `lineage-source-catalog.js` | Six JavaScript collection points that are lineage sources but NOT taint sources: `process.argv` (`cli-argument`), `req.file`/`req.files` (`http-upload`), `fs.readFileSync` (`storage-read`), bare `prompt()` and `readlineSync.question()` (`user-input`). The main taint catalog had no JavaScript entry for those four categories, and adding them there would make a file's contents or a console prompt attacker-controlled for every SAST finding. Same isolation as `dataflow/privacy-catalog.js`; imports nothing from `dataflow/`. `matchLineageSource(expr, file)` is consulted by `source-seeding.js` after `matchSource` and only for a JavaScript or TypeScript file; `LINEAGE_SOURCE_DECISIONS` proves at load time that every entry's `provenance` resolves through `source-registry.js`. The test pins that `matchSource` returns null for every shape here. |
|
|
1339
|
+
| `source-seeding.js` (extended) | Two seed kinds (`SEED_KINDS`). `entry` is §3's mechanism unchanged. `after-node` is a CALL-shaped source assigned straight to a variable (`const record = fs.readFileSync(p)`, `const data = res.json()`): the call has no access path, so the seed lands on the assignment target in the state LEAVING that CFG node, applied by `engine.js`'s worklist through `ctx.seedAfterNode(qid, nodeId)` (`seedAfterNodeFactory`). A call-shaped source anywhere else is still `unseedable[]`. `extendThroughAliases` follows a container alias within one function (`const body = req.body; body.card_number`): the alias assignment names `body`, every outermost pure member read rooted at an alias mints the field seed on the ORIGINAL path (`req.body.card_number`, classified `PCI`), sited where the data entered; chained aliases are followed for at most four rounds, a wildcard segment is never seeded, a parameter is never an alias. |
|
|
1340
|
+
| `summaries.js` (extended) | `createCallSummaryResolver(cache, lookupCallee, { seedEntryState })` joins the callee's OWN seeds onto the entry state derived from the call arguments: `req.body.card_number` read inside `extractCard` is a source wherever it is called from, so the caller's resolution of `extractCard(req)` returns the PCI element and the `call-resolved` hop joins the caller's path to the callee's return node. The cache key is the joined state, so the driver's analysis of the callee and a clean call site share one key; §3.6's rule (no seeded summary under the empty-entry key) stands, see `DESIGN_GRAPH_BUILDER.md` §3.8. Bind hops and the cap-degradation loss hops enumerate only the call-derived half. `ctx.seedAfterNode` is forwarded to the callee's ctx. `driver.js` memoises the hook per function and passes it to the resolver. |
|
|
1341
|
+
| `engine.js` (extended) | After `step()`, the worklist adds that node's `after-node` seeds to the outgoing state (idempotent on revisit, no hop: the first read creates the path node with no in-edges, an origin, as for an entry seed). **Disclosed gap:** a bare call statement is still never resolved through the summary, so a sink inside the callee never sees an argument's identity; resolving it was tried and reverted (it consumes a summary context per call site and under a tight cap degrades a later resolution, moving seven pinned provenance measurements). Pinned by `seeding-across-calls.test.js` IP/2. |
|
|
1342
|
+
| `bench/data-lineage/runner.mjs` (extended) | `expectUnsupportedSink: true` asserts a `kind: 'process'`, `subtype: null`, `coverageStatus: 'unsupported'` node with a reason (the `exec` fixture). An EMPTY `dataClass` label matches the flow on the two categories alone (a file's contents, a positional argument); a non-empty label is unchanged. |
|
|
@@ -215,6 +215,29 @@ empty one.
|
|
|
215
215
|
`E1/driver-3` proves **both** directions: a local replica of the pre-fix loop
|
|
216
216
|
(the mutant) fabricates; the shipped driver does not.
|
|
217
217
|
|
|
218
|
+
### 3.8 Revision (GA-03.1, 0.195.24): a callee's seeds are facts about its body
|
|
219
|
+
|
|
220
|
+
§3.6's table read "`[]` — correct" for the caller's local after
|
|
221
|
+
`const v = helper(o)`. That was the right KEY and the wrong CONSEQUENCE.
|
|
222
|
+
`helper`'s seed is `req.body.secret`, a source expression inside `helper`'s
|
|
223
|
+
own body; it is read whoever calls `helper`, so the honest answer at every
|
|
224
|
+
call site is that `v` carries it. Under the shipped resolver the caller
|
|
225
|
+
resolved `helper` under the call-derived state alone, the seed was absent
|
|
226
|
+
from it, and the lineage corpus's interprocedural fixture produced the right
|
|
227
|
+
nodes, the right data element and zero flows.
|
|
228
|
+
|
|
229
|
+
`createCallSummaryResolver` now joins the callee's own seeds
|
|
230
|
+
(`seedEntryState(fn)`, the same hook the driver uses, memoised per function)
|
|
231
|
+
onto the call-derived entry state before computing the summary. The rule
|
|
232
|
+
§3.6 established is untouched: the cache key is the JOINED state, so nothing
|
|
233
|
+
is ever written under the empty-entry key for a seeded function, and the
|
|
234
|
+
driver's own analysis of the callee and a clean call site share one key and
|
|
235
|
+
one summary. `E1/driver-3` now pins exactly that in both directions (the
|
|
236
|
+
mutant still stores the seeded answer under the empty key; the shipped
|
|
237
|
+
driver never does, and the caller receives the seed). Bind hops and the
|
|
238
|
+
cap-degradation loss hops enumerate only the call-derived half: a seed is
|
|
239
|
+
not an argument.
|
|
240
|
+
|
|
218
241
|
### 3.7 What real seeding measures
|
|
219
242
|
|
|
220
243
|
| Project | seeds | hops | pnodes / pedges | node kinds reached |
|
package/src/lineage/driver.js
CHANGED
|
@@ -140,14 +140,31 @@ export function runFieldIdentityAnalysis(callGraph, opts = {}) {
|
|
|
140
140
|
// seeding hook. `opts.seedEntryState(fn)` returns the entry state `fn` is
|
|
141
141
|
// analyzed under; a falsy return means "no seed". With no hook, this IS
|
|
142
142
|
// `emptyState()` and every output is byte-identical to the pre-hook driver.
|
|
143
|
-
|
|
143
|
+
// GA-03.1 (0.195.24): the hook's answer is memoised per function, so it
|
|
144
|
+
// is still consulted exactly once per function even though the
|
|
145
|
+
// call-summary resolver below now reads it at every resolved call site.
|
|
146
|
+
const seedMemo = new Map();
|
|
147
|
+
const seedOf = opts.seedEntryState
|
|
148
|
+
? (fn) => {
|
|
149
|
+
if (!seedMemo.has(fn.qid)) seedMemo.set(fn.qid, opts.seedEntryState(fn) || null);
|
|
150
|
+
return seedMemo.get(fn.qid);
|
|
151
|
+
}
|
|
152
|
+
: null;
|
|
153
|
+
const entryStateOf = (fn) => (seedOf ? (seedOf(fn) || emptyState()) : emptyState());
|
|
144
154
|
const ctxFor = (fn) => {
|
|
145
155
|
const lookupCallee = createCallGraphLookup(callGraph, fn.file);
|
|
146
|
-
|
|
156
|
+
// GA-03.1 (0.195.24): the resolver receives the same seeding hook the
|
|
157
|
+
// driver uses, so a callee's own source seeds hold under every call
|
|
158
|
+
// context (summaries.js, createCallSummaryResolver). Without a hook the
|
|
159
|
+
// resolver is built exactly as before.
|
|
160
|
+
const resolveCallSummary = createCallSummaryResolver(cache, lookupCallee, seedOf ? { seedEntryState: seedOf } : {});
|
|
147
161
|
// Path provenance (Sub-project C, increment 3, section 13.7 item 14):
|
|
148
162
|
// thread opts.recordHop CONDITIONALLY so a caller that supplies none gets
|
|
149
163
|
// a byte-identical `{ resolveCallSummary }` object (Decision 7.2).
|
|
150
164
|
const ctx = opts.recordHop ? { resolveCallSummary, recordHop: opts.recordHop } : { resolveCallSummary };
|
|
165
|
+
// GA-03.1: `opts.seedAfterNode(qid, nodeId)` (source-seeding.js's
|
|
166
|
+
// seedAfterNodeFactory), consulted by engine.js after every CFG node.
|
|
167
|
+
if (typeof opts.seedAfterNode === 'function') ctx.seedAfterNode = opts.seedAfterNode;
|
|
151
168
|
// DAG-03: correlation version 2 labels hops with derivation, value-version
|
|
152
169
|
// and invocation evidence. Off unless asked for; needs a recorder.
|
|
153
170
|
if (opts.recordHop && resolveCorrelationVersion(opts.correlationVersion) === 2) ctx.correlationVersion = 2;
|
package/src/lineage/engine.js
CHANGED
|
@@ -923,6 +923,18 @@ function step(node, stateIn, widenings, ctx) {
|
|
|
923
923
|
}
|
|
924
924
|
}
|
|
925
925
|
}
|
|
926
|
+
// Known, disclosed gap (GA-03.1, 0.195.24, measured): a bare call
|
|
927
|
+
// statement is never resolved through `ctx.resolveCallSummary`, so a
|
|
928
|
+
// sink INSIDE the callee (`emit(logger, req.body.card_number)` with
|
|
929
|
+
// `logger.info(v)` in emit) never sees the argument's identity; the
|
|
930
|
+
// value is an escape here and nothing more. Resolving it was tried
|
|
931
|
+
// and reverted in 0.195.24: the extra callee analysis consumes a
|
|
932
|
+
// summary context per call site, and under a tight context cap that
|
|
933
|
+
// degrades a later resolution to an empty summary (the §13.2a hazard
|
|
934
|
+
// of DESIGN_PATH_PROVENANCE.md, in the unsound direction), moving
|
|
935
|
+
// seven pinned provenance measurements. Closing it needs the cap to
|
|
936
|
+
// exempt bare-call contexts or a separate budget; pinned as a gap by
|
|
937
|
+
// test/lineage/seeding-across-calls.test.js (IP/2).
|
|
926
938
|
return { state: stateIn, returnFact: null };
|
|
927
939
|
}
|
|
928
940
|
|
|
@@ -1068,7 +1080,18 @@ export function analyzeFunctionFieldIdentity(fn, entryState, ctx) {
|
|
|
1068
1080
|
recordHop: stepRecord,
|
|
1069
1081
|
}
|
|
1070
1082
|
: ctx;
|
|
1071
|
-
|
|
1083
|
+
let { state: out, returnFact } = step(node, incoming, widenings, stepCtx);
|
|
1084
|
+
// GA-03.1 (0.195.24): `after-node` seeds (source-seeding.js, SEED_KINDS).
|
|
1085
|
+
// A call-shaped source assigned to a variable produces its identity in
|
|
1086
|
+
// the TARGET once this node has run; the assignment's own strong update
|
|
1087
|
+
// has already cleared the target, so this is a plain add, idempotent on
|
|
1088
|
+
// a worklist revisit. Keyed by `(scope, nid)` so the same hook serves a
|
|
1089
|
+
// callee analysed through the call-summary resolver. No hop is recorded
|
|
1090
|
+
// here: the first read of the target creates the path node, which then
|
|
1091
|
+
// has no in-edges and reconstructs as an origin, exactly as an entry
|
|
1092
|
+
// seed's first read does.
|
|
1093
|
+
const after = ctx?.seedAfterNode ? ctx.seedAfterNode(scope, nid) : undefined;
|
|
1094
|
+
if (after) for (const s of after) out = addIdentity(out, s.path, s.id);
|
|
1072
1095
|
if (returnFact && returnFact.size > 0) {
|
|
1073
1096
|
// Union onto any existing fact for this node rather than pushing a
|
|
1074
1097
|
// new array entry every visit — see Fix 2 in the final whole-branch
|
|
@@ -131,7 +131,7 @@ import { classifyHandling } from './handling-analyzer.js';
|
|
|
131
131
|
import { emptyGraphEnvelope } from './schema.js';
|
|
132
132
|
import { emptyProtection, aggregateVerdicts } from './protection.js';
|
|
133
133
|
import * as ids from './ids.js';
|
|
134
|
-
import { planSeeds, seedEntryStateFactory, exprRoots, walkExpr } from './source-seeding.js';
|
|
134
|
+
import { planSeeds, seedEntryStateFactory, seedAfterNodeFactory, exprRoots, walkExpr } from './source-seeding.js';
|
|
135
135
|
import { RECIPIENT_FACT_FIELDS } from './recipient-profile.js';
|
|
136
136
|
import { pathEvidenceEnabled, evaluatePathEvidence, summarizeFlowEvidence } from './path-evidence.js';
|
|
137
137
|
import { sinkFamilyOf } from './control-credit.js';
|
|
@@ -450,6 +450,8 @@ export function buildDataFlowGraph(callGraph, opts = {}) {
|
|
|
450
450
|
const analysis = runFieldIdentityAnalysis(callGraph, {
|
|
451
451
|
recordHop: (h) => hops.push(h),
|
|
452
452
|
seedEntryState: seedEntryStateFactory(seeds),
|
|
453
|
+
// GA-03.1 (0.195.24): call-shaped sources assigned to a variable.
|
|
454
|
+
...(seedAfterNodeFactory(seeds) ? { seedAfterNode: seedAfterNodeFactory(seeds) } : {}),
|
|
453
455
|
...(opts.maxContextsPerFn === undefined ? {} : { maxContextsPerFn: opts.maxContextsPerFn }),
|
|
454
456
|
...(opts.maxIterations === undefined ? {} : { maxIterations: opts.maxIterations }),
|
|
455
457
|
// DAG-03: provenance correlation version 2 (off unless requested).
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
//
|
|
2
|
+
// lineage-source-catalog.js — JavaScript data collection points that are
|
|
3
|
+
// lineage sources but NOT taint sources (GA plan, GA-03.1: "address
|
|
4
|
+
// source-category omissions without broadening the main taint catalog
|
|
5
|
+
// incorrectly").
|
|
6
|
+
//
|
|
7
|
+
// Four of the fourteen reachable `SOURCE_CATEGORIES` had no `language: 'js'`
|
|
8
|
+
// entry in `dataflow/catalog.js` at all: `cli-argument` (only `py-sys-argv`
|
|
9
|
+
// and the argparse entries), `http-upload` (only Django and Symfony),
|
|
10
|
+
// `storage-read` (only `py-open-read` and the C `read`/`fread`/`fgets`
|
|
11
|
+
// entries) and `user-input` (only `py-input`, `gets`, `scanf`). The lineage
|
|
12
|
+
// corpus therefore could not mint a source node for any of them from a
|
|
13
|
+
// JavaScript fixture, and the four corpus entries that name them were
|
|
14
|
+
// written in Python syntax on purpose, as a disclosed limitation.
|
|
15
|
+
//
|
|
16
|
+
// Why these entries live HERE and not in `dataflow/catalog.js`'s `CATALOG`:
|
|
17
|
+
// that catalog is the taint engine's source list, and every entry in it
|
|
18
|
+
// becomes an attacker-controlled input for every SAST and taint finding the
|
|
19
|
+
// engine emits. A file's contents (`fs.readFileSync`) and a console prompt
|
|
20
|
+
// (`prompt()`, `readlineSync.question()`) are places personal data ENTERS a
|
|
21
|
+
// program, which is what a lineage graph records, but treating them as
|
|
22
|
+
// attacker-controlled would turn every configuration read into a taint
|
|
23
|
+
// source and flood the SAST output with findings nobody asked for. The
|
|
24
|
+
// Data Flow Explorer already keeps a second, isolated catalog for the same
|
|
25
|
+
// reason (`dataflow/privacy-catalog.js`, never merged into `CATALOG`); this
|
|
26
|
+
// module follows that precedent on the source side. `test/lineage/
|
|
27
|
+
// lineage-source-catalog.test.js` pins that `dataflow/catalog.js`'s own
|
|
28
|
+
// `matchSource` returns null for every shape listed here, so adding an
|
|
29
|
+
// entry here can never widen the taint engine by accident.
|
|
30
|
+
//
|
|
31
|
+
// Reuse boundary: imports nothing from `dataflow/`. The matcher below is a
|
|
32
|
+
// deliberately small structural match over the parser's expression shape
|
|
33
|
+
// (`ident`/`member`/`call`), not a copy of `catalog.js`'s index machinery;
|
|
34
|
+
// an entry that needs receiver types, keyword arguments or a language family
|
|
35
|
+
// other than JavaScript belongs in the main catalog, with the taint
|
|
36
|
+
// consequences weighed there.
|
|
37
|
+
|
|
38
|
+
import { reclassifySource } from './source-registry.js';
|
|
39
|
+
|
|
40
|
+
const JS_FILE = /\.(?:[cm]?js|jsx|tsx?)$/i;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Each entry mirrors the main catalog's source shape (`kind`, `id`,
|
|
44
|
+
* `language`, `framework`, `match`, `label`, `provenance`) so
|
|
45
|
+
* `reclassifySource` reads it unchanged: `provenance` is the key the
|
|
46
|
+
* registry's `PROVENANCE_MAP` resolves to a `SOURCE_CATEGORIES` value.
|
|
47
|
+
* `lineageOnly: true` is the marker a reader greps for; nothing consults it.
|
|
48
|
+
*/
|
|
49
|
+
export const LINEAGE_SOURCE_CATALOG = Object.freeze([
|
|
50
|
+
{
|
|
51
|
+
kind: 'source', id: 'js-process-argv', language: 'js', framework: 'node', lineageOnly: true,
|
|
52
|
+
match: { type: 'member', object: 'process', prop: 'argv' }, label: 'process.argv', provenance: 'cli',
|
|
53
|
+
why: 'a positional command-line argument; the value is whatever the operator typed, not attacker-controlled in the taint sense, so it is a collection point for lineage only',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
kind: 'source', id: 'js-multer-req-file', language: 'js', framework: 'express', lineageOnly: true,
|
|
57
|
+
match: { type: 'member', object: 'req', prop: 'file' }, label: 'req.file', provenance: 'upload',
|
|
58
|
+
why: 'the single uploaded file multer attaches to the request (name, type, buffer, path)',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
kind: 'source', id: 'js-multer-req-files', language: 'js', framework: 'express', lineageOnly: true,
|
|
62
|
+
match: { type: 'member', object: 'req', prop: 'files' }, label: 'req.files', provenance: 'upload',
|
|
63
|
+
why: 'the uploaded files multer attaches to the request, keyed by field name',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
kind: 'source', id: 'js-fs-readFileSync', language: 'js', framework: 'node', lineageOnly: true,
|
|
67
|
+
match: { type: 'call', callee: 'readFileSync' }, label: 'fs.readFileSync()', provenance: 'file-read',
|
|
68
|
+
why: 'the contents of a file; the callback form `fs.readFile(p, cb)` delivers its contents to a callback and is not seeded (its path argument is already a sink in the main catalog)',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
kind: 'source', id: 'js-window-prompt', language: 'js', framework: 'browser', lineageOnly: true,
|
|
72
|
+
match: { type: 'call', callee: 'prompt', bare: true }, label: 'prompt()', provenance: 'stdin',
|
|
73
|
+
why: 'text a person typed into the browser prompt dialog; only the bare global, never `x.prompt()` (inquirer and friends return a promise of an answers object)',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
kind: 'source', id: 'js-readline-sync-question', language: 'js', framework: 'node', lineageOnly: true,
|
|
77
|
+
match: { type: 'call', callee: 'question', receiver: 'readlineSync' }, label: 'readlineSync.question()', provenance: 'stdin',
|
|
78
|
+
why: 'text a person typed at the console; the readline module\'s own `rl.question(q, cb)` delivers the answer to a callback and is not seeded',
|
|
79
|
+
},
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
/** The receiver chain of a member-call callee, outermost first (`a.b.c()` -> ['a', 'b']). */
|
|
83
|
+
function receiverSegments(callee) {
|
|
84
|
+
const segs = [];
|
|
85
|
+
let cur = callee && callee.kind === 'member' ? callee.object : null;
|
|
86
|
+
let depth = 0;
|
|
87
|
+
while (cur && depth++ < 8) {
|
|
88
|
+
if (cur.kind === 'ident') { segs.unshift(cur.name); break; }
|
|
89
|
+
if (cur.kind === 'member') { if (typeof cur.prop === 'string') segs.unshift(cur.prop); cur = cur.object; continue; }
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
return segs;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function calleeName(callee) {
|
|
96
|
+
if (!callee) return null;
|
|
97
|
+
if (typeof callee === 'string') return callee.slice(callee.lastIndexOf('.') + 1);
|
|
98
|
+
if (callee.kind === 'ident') return callee.name ?? null;
|
|
99
|
+
if (callee.kind === 'member') return typeof callee.prop === 'string' ? callee.prop : null;
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function entryMatches(entry, expr) {
|
|
104
|
+
const m = entry.match;
|
|
105
|
+
if (m.type === 'member') {
|
|
106
|
+
return expr.kind === 'member' && expr.object?.kind === 'ident' && expr.object.name === m.object && expr.prop === m.prop;
|
|
107
|
+
}
|
|
108
|
+
if (m.type === 'call') {
|
|
109
|
+
if (expr.kind !== 'call') return false;
|
|
110
|
+
if (calleeName(expr.callee) !== m.callee) return false;
|
|
111
|
+
if (m.bare) return expr.callee && expr.callee.kind === 'ident';
|
|
112
|
+
if (m.receiver) return receiverSegments(expr.callee).includes(m.receiver);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The lineage-only counterpart of `dataflow/catalog.js`'s `matchSource`:
|
|
120
|
+
* returns the first catalog entry whose `match` fits `expr`, or null.
|
|
121
|
+
* Gated to JavaScript and TypeScript files (an entry here is JavaScript by
|
|
122
|
+
* construction); a missing `file` is permissive, matching `matchSource`.
|
|
123
|
+
* Never throws on a malformed expression.
|
|
124
|
+
*/
|
|
125
|
+
export function matchLineageSource(expr, file) {
|
|
126
|
+
if (!expr || typeof expr !== 'object') return null;
|
|
127
|
+
if (file && !JS_FILE.test(file)) return null;
|
|
128
|
+
for (const entry of LINEAGE_SOURCE_CATALOG) {
|
|
129
|
+
if (entryMatches(entry, expr)) return entry;
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** The registry decision for every entry, computed once: proves at load time that each `provenance` resolves. */
|
|
135
|
+
export const LINEAGE_SOURCE_DECISIONS = Object.freeze(Object.fromEntries(
|
|
136
|
+
LINEAGE_SOURCE_CATALOG.map((e) => {
|
|
137
|
+
const d = reclassifySource(e);
|
|
138
|
+
if (!d || !d.category || d.coverageStatus === 'unsupported') {
|
|
139
|
+
throw new Error(`lineage-source-catalog: ${e.id} has a provenance (${e.provenance}) the source registry cannot classify`);
|
|
140
|
+
}
|
|
141
|
+
return [e.id, d];
|
|
142
|
+
}),
|
|
143
|
+
));
|
|
@@ -30,6 +30,25 @@ import { emptyState, addIdentity } from './field-identity.js';
|
|
|
30
30
|
import { reclassifySource } from './source-registry.js';
|
|
31
31
|
import { classifyDataElementName } from './classification.js';
|
|
32
32
|
import { dataElementId } from './ids.js';
|
|
33
|
+
import { matchLineageSource } from './lineage-source-catalog.js';
|
|
34
|
+
|
|
35
|
+
// ── Seed kinds (GA-03.1, 0.195.24) ──────────────────────────────────────────
|
|
36
|
+
//
|
|
37
|
+
// `entry`: the identity is placed in the function's ENTRY state at
|
|
38
|
+
// `seedPath` (a member-shaped source such as `req.body.card_number`, whose
|
|
39
|
+
// path is stable for the whole function). This is §3's original mechanism.
|
|
40
|
+
//
|
|
41
|
+
// `after-node`: the identity is placed at `seedPath` in the state leaving
|
|
42
|
+
// CFG node `nodeId`, by `engine.js`'s worklist through `ctx.seedAfterNode`.
|
|
43
|
+
// Used for a CALL-shaped source assigned straight to a variable
|
|
44
|
+
// (`const record = fs.readFileSync(p)`, `const ssn = prompt('SSN')`,
|
|
45
|
+
// `const data = res.json()`): the call has no access path, so an entry seed
|
|
46
|
+
// has nowhere to land, and the value only exists in the target variable
|
|
47
|
+
// AFTER the assignment (an entry seed at the target would be killed by the
|
|
48
|
+
// assignment's own strong update). Before 0.195.24 every such match was
|
|
49
|
+
// recorded in `unseedable[]`; a call-shaped source anywhere else (nested
|
|
50
|
+
// inside another call's arguments, returned directly) still is.
|
|
51
|
+
export const SEED_KINDS = Object.freeze(['entry', 'after-node']);
|
|
33
52
|
|
|
34
53
|
// ── §3.1: where the matched expressions come from ──────────────────────────
|
|
35
54
|
//
|
|
@@ -169,6 +188,26 @@ export function planSeeds(callGraph, { repository, maxSeeds, maxNodes, signal, d
|
|
|
169
188
|
if (seeds.length >= seedCap) { stopReason = 'seeding-limit'; continue; }
|
|
170
189
|
if (nodesVisited >= nodeCap) { stopReason = 'seeding-limit'; continue; }
|
|
171
190
|
functionsScanned += 1;
|
|
191
|
+
const fnSeeds = [];
|
|
192
|
+
const mint = ({ nid, node, entry, decision, seedPath, seedKind }) => {
|
|
193
|
+
const canonicalName = seedPath.slice(seedPath.lastIndexOf('.') + 1);
|
|
194
|
+
const seed = {
|
|
195
|
+
file: fn.file, qid: fn.qid, nodeId: nid, line: node.line ?? null,
|
|
196
|
+
entryId: entry.id, seedPath, canonicalName, seedKind,
|
|
197
|
+
category: decision.category, coverageStatus: decision.coverageStatus,
|
|
198
|
+
externality: decision.externality, reason: decision.reason,
|
|
199
|
+
// §3.4's minting rule. `canonicalName` alone is forbidden by
|
|
200
|
+
// PRD §10.4; the discriminator carries the system proxy
|
|
201
|
+
// (repository + file), the access path, and the category.
|
|
202
|
+
// Function scope (`qid`) is deliberately NOT in the
|
|
203
|
+
// discriminator (§3.4).
|
|
204
|
+
dataElementId: dataElementId(canonicalName, [repository, fn.file, seedPath, decision.category ?? '']),
|
|
205
|
+
dataClasses: classifyDataElementName(canonicalName).classes,
|
|
206
|
+
};
|
|
207
|
+
seeds.push(seed);
|
|
208
|
+
fnSeeds.push(seed);
|
|
209
|
+
return seed;
|
|
210
|
+
};
|
|
172
211
|
for (const [nid, node] of Object.entries(fn.cfg?.nodes ?? {})) {
|
|
173
212
|
nodesVisited += 1;
|
|
174
213
|
if (seeds.length >= seedCap) { stopReason = 'seeding-limit'; break; }
|
|
@@ -176,10 +215,21 @@ export function planSeeds(callGraph, { repository, maxSeeds, maxNodes, signal, d
|
|
|
176
215
|
for (const root of exprRoots(node)) walkExpr(root, (e, p) => { if (p) parentOf.set(e, p); });
|
|
177
216
|
for (const root of exprRoots(node)) {
|
|
178
217
|
walkExpr(root, (e) => {
|
|
179
|
-
|
|
218
|
+
// The main taint catalog first (unchanged), then the lineage-only
|
|
219
|
+
// catalog for the JavaScript collection points the main catalog
|
|
220
|
+
// deliberately does not treat as taint sources.
|
|
221
|
+
const entry = matchSource(e, fn.file) ?? matchLineageSource(e, fn.file);
|
|
180
222
|
if (!entry) return;
|
|
181
223
|
const decision = reclassifySource(entry);
|
|
182
|
-
|
|
224
|
+
let seedPath = seedPathFor(e, parentOf);
|
|
225
|
+
let seedKind = 'entry';
|
|
226
|
+
if (!seedPath && node.kind === 'assign' && node.source === e && typeof node.target === 'string') {
|
|
227
|
+
// A call-shaped source that IS the whole right-hand side of an
|
|
228
|
+
// assignment: the value lands in the target, so seed the target
|
|
229
|
+
// once the node has run (see SEED_KINDS).
|
|
230
|
+
seedPath = node.target;
|
|
231
|
+
seedKind = 'after-node';
|
|
232
|
+
}
|
|
183
233
|
if (!seedPath) {
|
|
184
234
|
unseedable.push({
|
|
185
235
|
file: fn.file, qid: fn.qid, nodeId: nid, line: node.line ?? null,
|
|
@@ -187,23 +237,24 @@ export function planSeeds(callGraph, { repository, maxSeeds, maxNodes, signal, d
|
|
|
187
237
|
});
|
|
188
238
|
return;
|
|
189
239
|
}
|
|
190
|
-
|
|
191
|
-
seeds.push({
|
|
192
|
-
file: fn.file, qid: fn.qid, nodeId: nid, line: node.line ?? null,
|
|
193
|
-
entryId: entry.id, seedPath, canonicalName,
|
|
194
|
-
category: decision.category, coverageStatus: decision.coverageStatus,
|
|
195
|
-
externality: decision.externality, reason: decision.reason,
|
|
196
|
-
// §3.4's minting rule. `canonicalName` alone is forbidden by
|
|
197
|
-
// PRD §10.4; the discriminator carries the system proxy
|
|
198
|
-
// (repository + file), the access path, and the category.
|
|
199
|
-
// Function scope (`qid`) is deliberately NOT in the
|
|
200
|
-
// discriminator (§3.4).
|
|
201
|
-
dataElementId: dataElementId(canonicalName, [repository, fn.file, seedPath, decision.category ?? '']),
|
|
202
|
-
dataClasses: classifyDataElementName(canonicalName).classes,
|
|
203
|
-
});
|
|
240
|
+
mint({ nid, node, entry, decision, seedPath, seedKind });
|
|
204
241
|
});
|
|
205
242
|
}
|
|
206
243
|
}
|
|
244
|
+
// GA-03.1 (0.195.24): a container seed read through an ALIAS. §3.2's
|
|
245
|
+
// rule extends a match outward only through member accesses enclosing
|
|
246
|
+
// it in the SAME statement, so `const body = req.body;` seeds the
|
|
247
|
+
// container `req.body` (data element `body`, no classes) and the later
|
|
248
|
+
// `body.card_number` read is a field nothing seeded. Follow the alias
|
|
249
|
+
// within the function: an assignment of a pure access path that starts
|
|
250
|
+
// at a container seed (or at an earlier alias of one) names an alias,
|
|
251
|
+
// and every outermost pure member read rooted at an alias mints the
|
|
252
|
+
// field seed on the ORIGINAL container path. The alias assignment's own
|
|
253
|
+
// byPath forwarding (`engine.js`, the structure-preserving `ident` and
|
|
254
|
+
// `member` cases) then carries the field identity through the hop.
|
|
255
|
+
// Bounded: the alias table is rebuilt at most four rounds (one per
|
|
256
|
+
// hop of chained aliases) and a wildcard segment is never seeded.
|
|
257
|
+
if (fnSeeds.length) extendThroughAliases(fn, fnSeeds, mint);
|
|
207
258
|
}
|
|
208
259
|
const notScanned = functionsTotal - functionsScanned;
|
|
209
260
|
const completion = {
|
|
@@ -219,6 +270,61 @@ export function planSeeds(callGraph, { repository, maxSeeds, maxNodes, signal, d
|
|
|
219
270
|
return { seeds, unseedable, completion };
|
|
220
271
|
}
|
|
221
272
|
|
|
273
|
+
const MAX_ALIAS_ROUNDS = 4;
|
|
274
|
+
|
|
275
|
+
/** `(fn, fnSeeds, mint)`: see the comment at the call site in `planSeeds`. */
|
|
276
|
+
function extendThroughAliases(fn, fnSeeds, mint) {
|
|
277
|
+
const nodes = Object.entries(fn.cfg?.nodes ?? {});
|
|
278
|
+
const containers = fnSeeds.filter((s) => s.seedKind === 'entry');
|
|
279
|
+
if (!containers.length) return;
|
|
280
|
+
const containerFor = (p) => containers.find((s) => p === s.seedPath || p.startsWith(`${s.seedPath}.`)) ?? null;
|
|
281
|
+
// ident -> { basePath, seed }: the container path the alias stands for.
|
|
282
|
+
const aliases = new Map();
|
|
283
|
+
const resolve = (p) => {
|
|
284
|
+
if (!p || p.includes('*')) return null;
|
|
285
|
+
const root = p.slice(0, p.indexOf('.') === -1 ? p.length : p.indexOf('.'));
|
|
286
|
+
const a = aliases.get(root);
|
|
287
|
+
if (a) return { basePath: a.basePath + p.slice(root.length), seed: a.seed };
|
|
288
|
+
const c = containerFor(p);
|
|
289
|
+
return c ? { basePath: p, seed: c } : null;
|
|
290
|
+
};
|
|
291
|
+
for (let round = 0; round < MAX_ALIAS_ROUNDS; round++) {
|
|
292
|
+
let changed = false;
|
|
293
|
+
for (const [, node] of nodes) {
|
|
294
|
+
if (node.kind !== 'assign' || typeof node.target !== 'string' || node.target.includes('.') || node.target.includes('*')) continue;
|
|
295
|
+
if (aliases.has(node.target)) continue;
|
|
296
|
+
const r = resolve(accessPathOf(node.source));
|
|
297
|
+
if (!r) continue;
|
|
298
|
+
aliases.set(node.target, r);
|
|
299
|
+
changed = true;
|
|
300
|
+
}
|
|
301
|
+
if (!changed) break;
|
|
302
|
+
}
|
|
303
|
+
if (!aliases.size) return;
|
|
304
|
+
const minted = new Set(fnSeeds.map((s) => s.seedPath));
|
|
305
|
+
for (const [, node] of nodes) {
|
|
306
|
+
const parentOf = new Map();
|
|
307
|
+
for (const root of exprRoots(node)) walkExpr(root, (e, p) => { if (p) parentOf.set(e, p); });
|
|
308
|
+
for (const root of exprRoots(node)) {
|
|
309
|
+
walkExpr(root, (e) => {
|
|
310
|
+
if (e.kind !== 'member') return;
|
|
311
|
+
const parent = parentOf.get(e);
|
|
312
|
+
if (parent && parent.kind === 'member' && parent.object === e) return; // not outermost
|
|
313
|
+
const p = accessPathOf(e);
|
|
314
|
+
if (!p || p.includes('*')) return;
|
|
315
|
+
const root = p.slice(0, p.indexOf('.'));
|
|
316
|
+
const a = aliases.get(root);
|
|
317
|
+
if (!a) return;
|
|
318
|
+
const fieldPath = a.basePath + p.slice(root.length);
|
|
319
|
+
if (fieldPath === a.seed.seedPath || minted.has(fieldPath)) return;
|
|
320
|
+
minted.add(fieldPath);
|
|
321
|
+
const site = fn.cfg.nodes[a.seed.nodeId];
|
|
322
|
+
mint({ nid: a.seed.nodeId, node: site ?? node, entry: { id: a.seed.entryId }, decision: a.seed, seedPath: fieldPath, seedKind: 'entry' });
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
222
328
|
// ── §3.5: seedEntryStateFactory — the driver.js opts.seedEntryState hook ──
|
|
223
329
|
|
|
224
330
|
/**
|
|
@@ -233,6 +339,7 @@ export function planSeeds(callGraph, { repository, maxSeeds, maxNodes, signal, d
|
|
|
233
339
|
export function seedEntryStateFactory(seeds) {
|
|
234
340
|
const byQid = new Map();
|
|
235
341
|
for (const s of seeds) {
|
|
342
|
+
if (s.seedKind === 'after-node') continue;
|
|
236
343
|
if (!byQid.has(s.qid)) byQid.set(s.qid, []);
|
|
237
344
|
byQid.get(s.qid).push(s);
|
|
238
345
|
}
|
|
@@ -244,3 +351,22 @@ export function seedEntryStateFactory(seeds) {
|
|
|
244
351
|
return st;
|
|
245
352
|
};
|
|
246
353
|
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Builds the `ctx.seedAfterNode(qid, nodeId) -> [{path, id}] | undefined`
|
|
357
|
+
* function `engine.js`'s worklist consults after every CFG node (GA-03.1,
|
|
358
|
+
* 0.195.24): the `after-node` seeds of that function, grouped by node.
|
|
359
|
+
* Keyed by `qid` rather than bound to one function so the same function
|
|
360
|
+
* works for a callee analysed through `createCallSummaryResolver`.
|
|
361
|
+
*/
|
|
362
|
+
export function seedAfterNodeFactory(seeds) {
|
|
363
|
+
const byKey = new Map();
|
|
364
|
+
for (const s of seeds) {
|
|
365
|
+
if (s.seedKind !== 'after-node') continue;
|
|
366
|
+
const k = `${s.qid}\u0000${s.nodeId}`;
|
|
367
|
+
if (!byKey.has(k)) byKey.set(k, []);
|
|
368
|
+
byKey.get(k).push({ path: s.seedPath, id: s.dataElementId });
|
|
369
|
+
}
|
|
370
|
+
if (!byKey.size) return undefined;
|
|
371
|
+
return (qid, nodeId) => byKey.get(`${qid}\u0000${nodeId}`);
|
|
372
|
+
}
|