@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.
@@ -1 +1 @@
1
- c99c86a3f8172dcc3f6f1a23f6de0b955859ef9d8ea709e6d744e536f5676a15 agentic-security.mjs
1
+ 7a00dab21c958eb07e821a422efc871b2ceee9c84e4405ae9754390ec874c165 agentic-security.mjs
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clear-capabilities/agentic-security-scanner",
3
- "version": "0.195.23",
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.23",
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",
@@ -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 |
@@ -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
- const entryStateOf = (fn) => (opts.seedEntryState ? (opts.seedEntryState(fn) || emptyState()) : emptyState());
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
- const resolveCallSummary = createCallSummaryResolver(cache, lookupCallee);
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;
@@ -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
- const { state: out, returnFact } = step(node, incoming, widenings, stepCtx);
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
- const entry = matchSource(e, fn.file);
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
- const seedPath = seedPathFor(e, parentOf);
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
- const canonicalName = seedPath.slice(seedPath.lastIndexOf('.') + 1);
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
+ }