@clear-capabilities/agentic-security-scanner 0.195.25 → 0.195.27

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/bin/agentic-security.js +13 -3
  3. package/dist/396.index.js +936 -0
  4. package/dist/5752.index.js +15 -2
  5. package/dist/7838.index.js +22 -0
  6. package/dist/8218.index.js +19 -2
  7. package/dist/{809c900a90284017aaff.json → 90ebba38d8860ec77376.json} +4 -4
  8. package/dist/9382.index.js +315 -174
  9. package/dist/9560.index.js +19 -2
  10. package/dist/agentic-security.mjs +9 -9
  11. package/dist/agentic-security.mjs.sha256 +1 -1
  12. package/package.json +4 -4
  13. package/src/context-intelligence/CLAUDE.md +4 -0
  14. package/src/context-intelligence/entrypoints.js +141 -9
  15. package/src/context-intelligence/imports.js +1 -1
  16. package/src/context-intelligence/review.js +8 -3
  17. package/src/engine.js +10 -0
  18. package/src/lineage/CLAUDE.md +4 -0
  19. package/src/lineage/graph-builder.js +8 -0
  20. package/src/lineage/index.js +2 -0
  21. package/src/posture/CLAUDE.md +10 -2
  22. package/src/posture/custom-rules.js +7 -2
  23. package/src/posture/dag-commands.js +15 -2
  24. package/src/posture/provenance/coordinator.js +6 -2
  25. package/src/posture/provenance/dag-walk.js +7 -3
  26. package/src/posture/provenance/git-evidence.js +21 -0
  27. package/src/posture/provenance/origin-resolver.js +16 -2
  28. package/src/posture/provenance/providers/github.js +6 -3
  29. package/src/posture/provenance/providers/gitlab.js +5 -3
  30. package/src/posture/rule-compiler/cli.js +76 -21
  31. package/src/posture/rule-compiler/negative-corpus.js +39 -0
  32. package/src/posture/rule-compiler/promotion.js +21 -0
  33. package/src/posture/rule-compiler/regression.js +39 -9
  34. package/src/posture/rule-compiler/store.js +72 -6
  35. package/src/server/graph-loader.js +19 -2
@@ -1 +1 @@
1
- ea2e63293cf62b7a65de89db1da0f32c8490a8b41c5d9bbfd20fba1c3acb284f agentic-security.mjs
1
+ b1cf360cafbd8aeec59db25fb8e523f29104e005863aff5d6d00be22d3c1c58f agentic-security.mjs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clear-capabilities/agentic-security-scanner",
3
- "version": "0.195.25",
3
+ "version": "0.195.27",
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",
@@ -86,11 +86,11 @@
86
86
  "test:ollama-e2e": "AGENTIC_SECURITY_OLLAMA_E2E=1 node --test test/ollama-e2e.test.js",
87
87
  "test:glob": "node --test test/glob-compat.test.js",
88
88
  "test:sast": "node --test test/dag-precision.test.js test/cpp-hardcoded-secret.test.js test/sibling-guard.test.js test/comment-blindness.test.js test/todo-marker-words.test.js test/crypto-specialist.test.js test/llm.test.js test/llm-cost-advisor.test.js test/llm-owasp.test.js test/logic.test.js test/authz.test.js test/model-load.test.js test/prompt-template.test.js test/business-logic.test.js test/python-sinks.test.js test/phase1-detectors.test.js test/phase2-detectors.test.js test/phase3-v3.test.js test/phase7-extensions.test.js test/phase8-extensions.test.js test/new-cwe-detectors.test.js test/file-upload.test.js test/llmsecops-detectors.test.js test/db-taint.test.js test/dart-swift.test.js test/zip-slip-containment-guard.test.js test/zip-slip-go.test.js test/redos-anchoring.test.js test/path-guard-validator-call.test.js test/redos-nfa.test.js test/weak-randomness.test.js test/csharp-pipeline.test.js test/csharp-cross-file.test.js test/post-quantum-crypto.test.js test/web3-advanced.test.js test/cloud-iam-k8s.test.js test/crypto-protocol.test.js test/ml-supply-chain.test.js test/mcp-audit-scan.test.js test/wrong-context-sanitizer.test.js test/sanitizer-context.test.js test/frontend-hygiene.test.js test/csv-injection.test.js test/stored-taint.test.js test/tree-sitter-sinks.test.js test/kotlin-structural.test.js test/ruby-php-structural.test.js test/java-csharp-structural.test.js test/java-structural-cross-file.test.js test/guard-recognition.test.js test/guard-window-identifier-correlation.test.js test/js-python-framework-structural.test.js test/go-structural.test.js test/secret-concat.test.js test/secret-redaction.test.js test/xss-reflected-multilang.test.js test/code-injection-multilang.test.js test/xxe-multilang.test.js test/xpath-injection-multilang.test.js test/gapfill-batch13.test.js test/agent-untrusted-flow.test.js test/api-authz.test.js test/event-entrypoint.test.js test/iac-terraform.test.js test/cross-service.test.js test/rbac-consistency.test.js test/adversarial-robustness.test.js test/secret-entropy.test.js test/comment-strip.test.js test/juliet-path-filter-gate.test.js test/java-bench-shape-opt-in.test.js test/vendor-java-parser-current.test.js test/java-ast-folding.test.js test/java-bench-extras.test.js test/reachability-missing-callgraph-data.test.js test/rate-limit.test.js test/auth-signals.test.js test/convention-deviation.test.js test/resource-exhaustion.test.js test/resource-exhaustion-realworld.test.js test/redirect-toctou.test.js test/redirect-toctou-realworld.test.js test/codegen-sink.test.js test/ownership-authz.test.js test/ownership-authz-realworld.test.js test/t21-precision-audit.test.js test/secrets-coverage.test.js test/prompt-injection-payloads.test.js test/iac-cloud-templates.test.js test/ruby-path-join.test.js test/sast/manifest-line-tracking.test.js test/rust-sast.test.js test/java-deserialization-precision.test.js test/pipeline-env-secret.test.js test/mark-used-vuln-functions.test.js",
89
- "test:posture": "node --test test/contracts/root-cause-sweep.contract.test.js test/contracts/model-validation.contract.test.js test/contracts/agentic-discovery.contract.test.js test/contracts/local-models.contract.test.js test/contracts/dependency-finding.contract.test.js test/contracts/taint-flow.contract.test.js test/contracts/runtime-corroboration.contract.test.js test/compliance-coverage.test.js test/contracts/blast-radius.contract.test.js test/contracts/federation.contract.test.js test/contracts/eval-lab-report.contract.test.js test/contracts/fleet.contract.test.js test/contracts/rule-compiler.contract.test.js test/contracts/release-assurance.contract.test.js test/contracts/dnr.contract.test.js test/contracts/strategic-metrics.contract.test.js test/contracts/exploit-proof.contract.test.js test/contracts/artifact-provenance.contract.test.js test/dnr/artifact-join.test.js test/rule-pack-signing.test.js test/threat-model.test.js test/git-hardening.test.js test/privacy-framework.test.js test/material-change.test.js test/drift.test.js test/scorecard.test.js test/accuracy-scorecard.test.js test/scorecard-gate.test.js test/predeploy-gate.test.js test/release-check.test.js test/pre-push-gate.test.js test/dependency-currency.test.js test/package-contents-check.test.js test/gate-verdict-cache.test.js test/run-unit-tests.test.js test/temp-leak-guard.test.js test/release-workflow.test.js test/release-workflow-structure.test.js test/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-bundle.test.js test/obligation-evidence-pack.test.js test/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-transport.test.js test/local-preset-callers.test.js test/egress-policy-completeness.test.js test/egress-policy-integration.test.js test/router.test.js test/legacy-alias-redirect.test.js test/cache-economics.test.js test/coverage-report.test.js test/corpus-status.test.js test/provenance.test.js test/secret-history.test.js test/execution-proof.test.js test/fix-metrics.test.js test/corpus-enroll.test.js test/prove-findings.test.js test/corpus-match.test.js test/corpus-provenance.test.js test/learning-quorum.test.js test/llm-cache-integrity.test.js test/proof-artifact.test.js test/providers.test.js test/fleet.test.js test/autopilot.test.js test/autopilot-cli.test.js test/verifier-independence.test.js test/integrity-legacy-key.test.js test/suppression-visibility.test.js test/cost-ceiling.test.js test/local-endpoint.test.js test/ollama-provider.test.js test/ollama-capabilities.test.js test/ollama-offline-egress.test.js test/ollama-fix-proposal.test.js test/ollama-explain-proposal.test.js test/ollama-poc-proposal.test.js test/ollama-model-probe.test.js test/ollama-agent-tools.test.js test/ollama-agent-loop.test.js test/ollama-e2e.test.js test/ollama-oom-feedback.test.js test/ollama-doc-drift.test.js test/model-trust.test.js test/vuln-archaeology.test.js test/scan-checkpoint.test.js test/llm-redact.test.js test/world-class-batch2.test.js test/api-contract-scope.test.js test/world-class-modules.test.js test/privacy-ir-adapter.test.js test/privacy-taxonomy.test.js test/privacy-sink-policy.test.js test/privacy-governance.test.js test/privacy-inventory.test.js test/integration-end-to-end.test.js test/streak-cli-wiring.test.js test/stack-playbook.test.js test/machine-output-flag.test.js test/stdout-flush.test.js test/format-determinism.test.js test/only-flag-channels.test.js test/confidence.test.js test/security-trend.test.js test/calibration-drift.test.js test/validator-metrics.test.js test/clustering-ir-taint-line.test.js test/concurrency-cwe.test.js test/compliance-mapping-liveness.test.js test/module-artifact-liveness.test.js test/supply-chain-provenance-completeness.test.js test/compliance-severity-threshold.test.js test/compliance-severity-policy.test.js test/compliance-evidence-binding.test.js test/compliance-evidence-signing.test.js test/compliance-file-contains.test.js test/label-isolation.test.js test/evidence-grade-wording.test.js test/llm-model-status.test.js test/egress-policy-constraints.test.js test/egress-redact.test.js test/egress-audit.test.js test/egress-approved-providers.test.js test/family-registry.test.js test/coverage-strength.test.js test/framework-provenance.test.js test/framework-provenance-controlrefs.test.js test/third-party-verification.test.js test/proof-coverage.test.js test/calibration-holdout.test.js test/independent-population-gate.test.js test/catalog-freshness.test.js test/incremental-parity.test.js test/surface-smoke.test.js test/fix-coverage.test.js test/raw-source-carveout.test.js test/mlbom-conformance.test.js test/deploy-gate-replay.test.js test/sbom-conformance.test.js test/unscored-claims.test.js test/mcp-rug-pull.test.js test/agent-boundary-taint.test.js test/confinement-adversarial.test.js test/comment-strip-cost.test.js test/reachability-claim.test.js test/entrypoint-breadth.test.js test/dep-file-admission.test.js test/custom-rules-freshness.test.js test/posture/provenance-schema.test.js test/posture/provenance-cache.test.js test/helpers/build-git-fixture.test.js test/posture/provenance-git-evidence.test.js test/posture/provenance-dag-walk.test.js test/posture/provenance-confidence.test.js test/posture/provenance-predicate-replay.test.js test/posture/provenance-origin-resolver.test.js test/posture/provenance-ai-authorship.test.js test/posture/provenance-branch-entry.test.js test/posture/provenance-evidence-attribution.test.js test/posture/provenance-lifecycle.test.js test/posture/provenance-missing-control-resolver.test.js test/posture/provenance-missing-control-wiring.test.js test/posture/provenance-coordinator.test.js test/posture/provenance-sca-origin.test.js test/posture/provenance-transitive-sca.test.js test/posture/provenance-coordinator-sca.test.js test/posture/provenance-providers.test.js test/posture/provenance-evidence-bundle.test.js test/posture/provenance-repo-lineage.test.js test/posture/provenance-secrets-logic.test.js test/cli/provenance-flags.test.js test/cli/attest-provenance.test.js test/cli/attest-obligations.test.js test/cli/models.test.js test/cli/setup-llm.test.js test/cli/fix-retry.test.js test/cli/compliance-walkthrough-lineage.test.js test/cli/dataflow-export-privacy.test.js test/cli/dataflow-export-briefing.test.js test/cli/lineage-snapshot-persist.test.js test/cli/dataflow-diff.test.js test/cli/dataflow-watch.test.js test/cli/dataflow-recipients.test.js test/cli/dataflow-coverage.test.js test/security/provenance-safety.test.js test/security/provenance-injection.test.js test/posture/remediation-ledger.test.js test/posture/assurance-baseline.test.js test/posture/assurance-contracts.test.js test/posture/assurance-config.test.js test/evidence-issuer.test.js test/load-factor.test.js test/fleet/job-store.test.js test/fleet/enterprise-graph.test.js test/fleet/discover-edges.test.js test/fleet/code-boundaries.test.js test/fleet/job-runner.test.js test/fleet/accounting.test.js test/fleet/fleet-org-cli.test.js test/eval-lab/dataset-neutralize.test.js test/eval-lab/arms-score-stats.test.js test/eval-lab/run-report.test.js test/eval-lab/router.test.js test/eval-lab/arm-ollama.test.js test/eval-lab/import-independent.test.js test/cli/eval-lab-cli.test.js test/dnr/ir-and-outputs.test.js test/dnr/logs-and-hunt.test.js test/dnr/window-store-view.test.js test/dnr/exploit-correlation.test.js test/cli/dnr-cli.test.js test/proof-fabric/core.test.js test/proof-fabric/container.test.js test/proof-fabric/generate.test.js test/proof-fabric/siblings.test.js test/proof-fabric/generate-python.test.js test/proof-fabric/generate-haskell.test.js test/proof-fabric/generate-haskell-widened.test.js test/cli/proof-lifecycle-demo.test.js test/mcp-proof.test.js test/proof-fabric/fix-store-runner.test.js test/proof-fabric/fixtures.test.js test/cli/proof-fabric-cli.test.js test/rule-compiler/core.test.js test/rule-compiler/gates.test.js test/rule-compiler/synthesis.test.js test/rule-compiler/structure.test.js test/cli/rule-compiler-cli.test.js test/fleet/fleet-graph.test.js test/fleet/fleet-manifest.test.js test/fleet/fleet-cli.test.js test/artifact/safe-tar.test.js test/artifact/image-archive.test.js test/artifact/filesystem.test.js test/artifact/layer-secrets.test.js test/artifact/inventory.test.js test/artifact/vuln-correlate.test.js test/artifact/signature.test.js test/artifact/provenance-graph.test.js test/artifact/mismatch.test.js test/artifact/vex.test.js test/artifact/registry.test.js test/artifact/sources-cosign-evidence.test.js test/cli/artifact-sources-cli.test.js test/cli/artifact-provenance-cli.test.js test/witness-bundle.test.js",
89
+ "test:posture": "node --test test/contracts/finding-provenance.contract.test.js test/contracts/root-cause-sweep.contract.test.js test/contracts/model-validation.contract.test.js test/contracts/agentic-discovery.contract.test.js test/contracts/local-models.contract.test.js test/contracts/dependency-finding.contract.test.js test/contracts/taint-flow.contract.test.js test/contracts/runtime-corroboration.contract.test.js test/compliance-coverage.test.js test/contracts/blast-radius.contract.test.js test/contracts/federation.contract.test.js test/contracts/eval-lab-report.contract.test.js test/contracts/fleet.contract.test.js test/contracts/rule-compiler.contract.test.js test/contracts/release-assurance.contract.test.js test/contracts/dnr.contract.test.js test/contracts/strategic-metrics.contract.test.js test/contracts/exploit-proof.contract.test.js test/contracts/artifact-provenance.contract.test.js test/dnr/artifact-join.test.js test/rule-pack-signing.test.js test/threat-model.test.js test/git-hardening.test.js test/privacy-framework.test.js test/material-change.test.js test/drift.test.js test/scorecard.test.js test/accuracy-scorecard.test.js test/scorecard-gate.test.js test/predeploy-gate.test.js test/release-check.test.js test/pre-push-gate.test.js test/dependency-currency.test.js test/package-contents-check.test.js test/gate-verdict-cache.test.js test/run-unit-tests.test.js test/temp-leak-guard.test.js test/release-workflow.test.js test/release-workflow-structure.test.js test/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-bundle.test.js test/obligation-evidence-pack.test.js test/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-transport.test.js test/local-preset-callers.test.js test/egress-policy-completeness.test.js test/egress-policy-integration.test.js test/router.test.js test/legacy-alias-redirect.test.js test/cache-economics.test.js test/coverage-report.test.js test/corpus-status.test.js test/provenance.test.js test/secret-history.test.js test/execution-proof.test.js test/fix-metrics.test.js test/corpus-enroll.test.js test/prove-findings.test.js test/corpus-match.test.js test/corpus-provenance.test.js test/learning-quorum.test.js test/llm-cache-integrity.test.js test/proof-artifact.test.js test/providers.test.js test/fleet.test.js test/autopilot.test.js test/autopilot-cli.test.js test/verifier-independence.test.js test/integrity-legacy-key.test.js test/suppression-visibility.test.js test/cost-ceiling.test.js test/local-endpoint.test.js test/ollama-provider.test.js test/ollama-capabilities.test.js test/ollama-offline-egress.test.js test/ollama-fix-proposal.test.js test/ollama-explain-proposal.test.js test/ollama-poc-proposal.test.js test/ollama-model-probe.test.js test/ollama-agent-tools.test.js test/ollama-agent-loop.test.js test/ollama-e2e.test.js test/ollama-oom-feedback.test.js test/ollama-doc-drift.test.js test/model-trust.test.js test/vuln-archaeology.test.js test/scan-checkpoint.test.js test/llm-redact.test.js test/world-class-batch2.test.js test/api-contract-scope.test.js test/world-class-modules.test.js test/privacy-ir-adapter.test.js test/privacy-taxonomy.test.js test/privacy-sink-policy.test.js test/privacy-governance.test.js test/privacy-inventory.test.js test/integration-end-to-end.test.js test/streak-cli-wiring.test.js test/stack-playbook.test.js test/machine-output-flag.test.js test/stdout-flush.test.js test/format-determinism.test.js test/only-flag-channels.test.js test/confidence.test.js test/security-trend.test.js test/calibration-drift.test.js test/validator-metrics.test.js test/clustering-ir-taint-line.test.js test/concurrency-cwe.test.js test/compliance-mapping-liveness.test.js test/module-artifact-liveness.test.js test/supply-chain-provenance-completeness.test.js test/compliance-severity-threshold.test.js test/compliance-severity-policy.test.js test/compliance-evidence-binding.test.js test/compliance-evidence-signing.test.js test/compliance-file-contains.test.js test/label-isolation.test.js test/evidence-grade-wording.test.js test/llm-model-status.test.js test/egress-policy-constraints.test.js test/egress-redact.test.js test/egress-audit.test.js test/egress-approved-providers.test.js test/family-registry.test.js test/coverage-strength.test.js test/framework-provenance.test.js test/framework-provenance-controlrefs.test.js test/third-party-verification.test.js test/proof-coverage.test.js test/calibration-holdout.test.js test/independent-population-gate.test.js test/catalog-freshness.test.js test/incremental-parity.test.js test/surface-smoke.test.js test/fix-coverage.test.js test/raw-source-carveout.test.js test/mlbom-conformance.test.js test/deploy-gate-replay.test.js test/sbom-conformance.test.js test/unscored-claims.test.js test/mcp-rug-pull.test.js test/agent-boundary-taint.test.js test/confinement-adversarial.test.js test/comment-strip-cost.test.js test/reachability-claim.test.js test/entrypoint-breadth.test.js test/dep-file-admission.test.js test/custom-rules-freshness.test.js test/posture/provenance-schema.test.js test/posture/provenance-cache.test.js test/helpers/build-git-fixture.test.js test/posture/provenance-git-evidence.test.js test/posture/provenance-dag-walk.test.js test/posture/provenance-confidence.test.js test/posture/provenance-predicate-replay.test.js test/posture/provenance-origin-resolver.test.js test/posture/provenance-ai-authorship.test.js test/posture/provenance-branch-entry.test.js test/posture/provenance-evidence-attribution.test.js test/posture/provenance-lifecycle.test.js test/posture/provenance-missing-control-resolver.test.js test/posture/provenance-missing-control-wiring.test.js test/posture/provenance-coordinator.test.js test/posture/provenance-sca-origin.test.js test/posture/provenance-transitive-sca.test.js test/posture/provenance-coordinator-sca.test.js test/posture/provenance-providers.test.js test/posture/provenance-evidence-bundle.test.js test/posture/provenance-repo-lineage.test.js test/posture/provenance-secrets-logic.test.js test/cli/provenance-flags.test.js test/cli/attest-provenance.test.js test/cli/attest-obligations.test.js test/cli/models.test.js test/cli/setup-llm.test.js test/cli/fix-retry.test.js test/cli/compliance-walkthrough-lineage.test.js test/cli/dataflow-export-privacy.test.js test/cli/dataflow-export-briefing.test.js test/cli/lineage-snapshot-persist.test.js test/cli/dataflow-diff.test.js test/cli/dataflow-watch.test.js test/cli/dataflow-recipients.test.js test/cli/dataflow-coverage.test.js test/security/provenance-safety.test.js test/security/provenance-injection.test.js test/posture/remediation-ledger.test.js test/posture/assurance-baseline.test.js test/posture/assurance-contracts.test.js test/posture/assurance-config.test.js test/evidence-issuer.test.js test/load-factor.test.js test/fleet/job-store.test.js test/fleet/enterprise-graph.test.js test/fleet/discover-edges.test.js test/fleet/code-boundaries.test.js test/fleet/job-runner.test.js test/fleet/accounting.test.js test/fleet/fleet-org-cli.test.js test/eval-lab/dataset-neutralize.test.js test/eval-lab/arms-score-stats.test.js test/eval-lab/run-report.test.js test/eval-lab/router.test.js test/eval-lab/arm-ollama.test.js test/eval-lab/import-independent.test.js test/cli/eval-lab-cli.test.js test/dnr/ir-and-outputs.test.js test/dnr/logs-and-hunt.test.js test/dnr/window-store-view.test.js test/dnr/exploit-correlation.test.js test/cli/dnr-cli.test.js test/proof-fabric/core.test.js test/proof-fabric/container.test.js test/proof-fabric/generate.test.js test/proof-fabric/siblings.test.js test/proof-fabric/generate-python.test.js test/proof-fabric/generate-haskell.test.js test/proof-fabric/generate-haskell-widened.test.js test/cli/proof-lifecycle-demo.test.js test/mcp-proof.test.js test/proof-fabric/fix-store-runner.test.js test/proof-fabric/fixtures.test.js test/cli/proof-fabric-cli.test.js test/rule-compiler/core.test.js test/rule-compiler/gates.test.js test/rule-compiler/ci-regression.test.js test/rule-compiler/synthesis.test.js test/rule-compiler/structure.test.js test/cli/rule-compiler-cli.test.js test/fleet/fleet-graph.test.js test/fleet/fleet-manifest.test.js test/fleet/fleet-cli.test.js test/artifact/safe-tar.test.js test/artifact/image-archive.test.js test/artifact/filesystem.test.js test/artifact/layer-secrets.test.js test/artifact/inventory.test.js test/artifact/vuln-correlate.test.js test/artifact/signature.test.js test/artifact/provenance-graph.test.js test/artifact/mismatch.test.js test/artifact/vex.test.js test/artifact/registry.test.js test/artifact/sources-cosign-evidence.test.js test/cli/artifact-sources-cli.test.js test/cli/artifact-provenance-cli.test.js test/witness-bundle.test.js",
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/seeding-across-calls.test.js test/lineage/boundary-join.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/boundary-join.test.js test/contracts/code-boundaries.contract.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",
@@ -133,7 +133,7 @@
133
133
  "verification:replay-example": "node ../scripts/verification-replay-example.mjs",
134
134
  "test:lifecycle": "node --test test/ci-hang-protection.test.js test/gate-select.test.js test/ci-test-shards.test.js test/pre-bash-guard.test.js test/lsp-protocol-smoke.test.js test/ide-surfaces.test.js test/detector-liveness.test.js test/bench-watchdog.test.js test/ci-parity.test.js test/dead-code.test.js test/no-dead-modules.test.js test/no-orphan-scripts.test.js test/no-stray-state.test.js test/tree-integrity.test.js test/stop-hook.test.js test/plugin-self-check.test.js test/skills-registry.test.js test/bodyguard.test.js test/cache-invalidator-guard.test.js test/dispatch-pre-tool.test.js test/dispatch-user-prompt.test.js test/session-start-model-capture.test.js test/sandbox.test.js test/sandbox-escape.test.js test/trust-boundary.test.js test/ci-templates.test.js test/agent-mcp-tools.test.js test/triage-command.test.js test/no-orphan-tests.test.js test/supply-command.test.js test/posture-command.test.js test/labs-command.test.js test/compliance-command.test.js test/check-doc-drift.test.js test/gate-warm.test.js test/docs-positioning.test.js test/dist-chunks-tracked.test.js test/ga-program.test.js test/workflow-confinement.test.js test/check-bundle-source.test.js test/ship.test.js test/release-prepare.test.js && node ../scripts/lint-command-descriptions.mjs",
135
135
  "test:eval": "node --test test/independent-eval.test.js test/realworld-recall.test.js",
136
- "test:discovery": "node --test test/discovery-spec-synthesis.test.js test/discovery-partition.test.js test/discovery-lenses.test.js test/discovery-hunter.test.js test/discovery-confirm.test.js test/discovery-disprove.test.js test/discovery-judge.test.js test/discovery-run.test.js test/discovery-wiring.test.js test/discovery-llm-invoke.test.js test/local-only-mode.test.js test/context-intelligence/build-js.test.js test/context-intelligence/build-python-haskell.test.js test/context-intelligence/hie-adapter.test.js test/context-intelligence/store.test.js test/context-intelligence/review-queue.test.js test/context-intelligence/packer-egress.test.js test/context-intelligence/evaluation.test.js test/context-intelligence/queue-diff.test.js test/context-intelligence/cli.test.js test/context-intelligence/resilience.test.js test/context-intelligence/frameworks.test.js test/context-intelligence/skew-git-rls.test.js test/context-intelligence/premortem.test.js test/context-intelligence/git-diff-candidates.test.js",
136
+ "test:discovery": "node --test test/discovery-spec-synthesis.test.js test/discovery-partition.test.js test/discovery-lenses.test.js test/discovery-hunter.test.js test/discovery-confirm.test.js test/discovery-disprove.test.js test/discovery-judge.test.js test/discovery-run.test.js test/discovery-wiring.test.js test/discovery-llm-invoke.test.js test/local-only-mode.test.js test/context-intelligence/build-js.test.js test/context-intelligence/build-python-haskell.test.js test/context-intelligence/hie-adapter.test.js test/context-intelligence/store.test.js test/context-intelligence/review-queue.test.js test/context-intelligence/route-mounts.test.js test/context-intelligence/packer-egress.test.js test/context-intelligence/evaluation.test.js test/context-intelligence/queue-diff.test.js test/context-intelligence/cli.test.js test/context-intelligence/resilience.test.js test/context-intelligence/frameworks.test.js test/context-intelligence/skew-git-rls.test.js test/context-intelligence/premortem.test.js test/context-intelligence/git-diff-candidates.test.js",
137
137
  "eval:independent": "node ../bench/independent-eval/runner.mjs",
138
138
  "eval:independent:gate": "node ../bench/independent-eval/runner.mjs --gate default",
139
139
  "smoke": "node bin/agentic-security.js scan test/fixtures/vulnerable-js",
@@ -23,3 +23,7 @@ See the module table in the architecture doc. Tests: `test/context-intelligence/
23
23
  ## Reuse, do not fork
24
24
 
25
25
  Model calls go through `discovery/llm-invoke.js` and the egress policy. Confirmation, refutation and judging are `discovery/{confirm,disprove,judge}.js`. Grouped intervals come from `posture/evaluation/interval.js`. State paths come from `posture/state-dir.js`.
26
+
27
+ ## Router mounts and stale citations (GA-03.2, 0.195.26)
28
+
29
+ `entrypoints.js` composes router mounts: `router.get('/:id')` with `app.use('/api', auth, router)` is the entry point `/api/:id` (`pathTemplate`), with `registeredPath` keeping `/:id` and `mount` recording the chain (root first) and the number of alternatives. A mount is found in the same file, through the import map (`import users from './users.js'`, `const users = require('./users')`) or an inline `require('./users')`, when the target module's `module.exports` / `export default` receiver registers routes or mounts; mounts compose through intermediate routers (depth 4, at most 8 paths); a router mounted at several prefixes is one entry point per prefix; the mount statement's other identifier arguments are inherited middleware scoped to that mount (`viaMount: true`), per chain. A dynamic prefix (`app.use(prefixFor(), router)`) mounts nothing and the route keeps `router-mount-not-resolved`. `review.js`'s `resolveCitation` refuses `stale-context` when the cited lines sit in a segment whose current text no longer hashes to the segment's `spanHash`. Tests: `test/context-intelligence/route-mounts.test.js`.
@@ -16,6 +16,7 @@ import { hashOf, safeLabel } from './canonical.js';
16
16
  import { callStatement, inlineBlockSpan, splitArgs } from './spans.js';
17
17
  import { dependencyParams, signatureText } from './controls.js';
18
18
  import { buildEntrypointInventory } from '../posture/entrypoint-inventory.js';
19
+ import { buildImportMap, resolveViaImports, resolveJsModule } from './imports.js';
19
20
 
20
21
  export const DISPOSITIONS = Object.freeze(['resolved', 'partially_resolved', 'unresolved', 'excluded_by_policy']);
21
22
  export const ENTRYPOINT_KINDS = Object.freeze(['http', 'webhook', 'queue', 'cron', 'upload', 'cli']);
@@ -88,8 +89,11 @@ function collectUses(fileContents) {
88
89
  let rest = args;
89
90
  const q = args[0] && /^(['"`])(.*)\1$/.exec(args[0]);
90
91
  if (q) { prefix = q[2]; rest = args.slice(1); }
92
+ // GA-03.2: a first argument that is a call, a template with interpolation or a concatenation is a prefix
93
+ // the scan cannot read; the use still records its middleware, but nothing is mounted under a guessed '/'.
94
+ const prefixDynamic = !q && !!args[0] && (/^[A-Za-z_$][\w$.]*\s*\(/.test(args[0]) && !/^require\s*\(/.test(args[0]) || /\$\{|\+/.test(args[0]));
91
95
  const names = rest.map(a => { const id = IDENT_CHAIN.exec(a.replace(/^\[|\]$/g, '')); return id ? { name: id[0], text: a } : { name: null, text: a }; });
92
- uses.push({ file, line: i + 1, receiver: m[1], prefix, args: names });
96
+ uses.push({ file, line: i + 1, receiver: m[1], prefix, prefixDynamic, args: names });
93
97
  }
94
98
  }
95
99
  return uses;
@@ -212,6 +216,107 @@ function resolveHandler(route, ctx) {
212
216
  return out;
213
217
  }
214
218
 
219
+ // ── Router mounts (GA-03.2, 0.195.26) ──────────────────────────────────────
220
+ //
221
+ // `router.get('/:id', h)` is reachable at `/users/:id` only through `app.use('/users', router)`, and that mount
222
+ // can sit in another file (`app.use('/users', require('./users'))`, `import users from './users.js'`). Before
223
+ // this, every route on a router-looking receiver was `partially_resolved` with `router-mount-not-resolved` and
224
+ // its path was the local one. Now a mount is found when the mounted argument is (a) a receiver with routes in
225
+ // the same file, (b) a name imported from a file whose `module.exports` / `export default` is that file's
226
+ // router receiver, or (c) an inline `require('./x')` of such a file. Mounts compose: `app.use('/api', v1)` and
227
+ // `v1.use('/users', users)` make `/api/users/:id`. A router mounted at several prefixes is reachable at each,
228
+ // so it yields one entry point per prefix (sorted; at most MAX_MOUNT_PATHS). Depth is bounded; a cycle stops.
229
+ // A mount's other identifier arguments (`app.use('/api', auth, router)`) are middleware every mounted route
230
+ // inherits, recorded with the mount's prefix as their scope. What stays unresolved: a router passed through a
231
+ // variable the import map cannot follow, a mount whose prefix is not a string literal, a dynamic
232
+ // `app.use(prefixVar, router)`, and anything registered outside the scanned files.
233
+ const MAX_MOUNT_DEPTH = 4;
234
+ const MAX_MOUNT_PATHS = 8;
235
+ const ROUTE_RECEIVER_RE = /([A-Za-z_$][\w$]*)\.(?:get|post|put|patch|delete|all|options|head|route)\s*\(/;
236
+ const ROUTER_NAME_RE = /^(?:router|routes?|r|api|\w*Router)$/i;
237
+
238
+ function routeReceiver(fileContents, route) {
239
+ if (!JS_FILE.test(route.file)) return null;
240
+ const line = (fileContents[route.file] || '').split('\n')[route.line - 1] || '';
241
+ const m = ROUTE_RECEIVER_RE.exec(line);
242
+ return m ? m[1] : null;
243
+ }
244
+
245
+ /** The receiver a file exports as its module (`module.exports = router`, `export default router`), or null. */
246
+ function exportedReceiver(text) {
247
+ const t = String(text || '');
248
+ const m = /\bmodule\.exports\s*=\s*([A-Za-z_$][\w$]*)\s*;?/.exec(t) || /\bexport\s+default\s+([A-Za-z_$][\w$]*)\s*;?/.exec(t);
249
+ return m ? m[1] : null;
250
+ }
251
+
252
+ function joinMountPath(prefix, routePath) {
253
+ const p = String(prefix || '/').replace(/\/+$/, '');
254
+ const r = String(routePath || '');
255
+ if (!p) return r.startsWith('/') ? r : '/' + r;
256
+ if (!r || r === '/') return p;
257
+ return p + (r.startsWith('/') ? r : '/' + r);
258
+ }
259
+
260
+ /**
261
+ * Every mount of a router: key `${file}|${receiver}` -> [{ prefix, file, line, receiver, middleware }], where
262
+ * `receiver` is the receiver the router was mounted ON (so a chain can be followed upward).
263
+ */
264
+ function buildMountIndex(fileContents, uses, routes) {
265
+ const files = new Set(Object.keys(fileContents));
266
+ const imports = buildImportMap(fileContents);
267
+ const receiversByFile = new Map();
268
+ for (const r of routes) {
269
+ const recv = routeReceiver(fileContents, r);
270
+ if (!recv) continue;
271
+ if (!receiversByFile.has(r.file)) receiversByFile.set(r.file, new Set());
272
+ receiversByFile.get(r.file).add(recv);
273
+ }
274
+ // a receiver that mounts something is a router too (an intermediate `v1.use('/users', users)`)
275
+ for (const u of uses) { if (!receiversByFile.has(u.file)) receiversByFile.set(u.file, new Set()); receiversByFile.get(u.file).add(u.receiver); }
276
+ const index = new Map();
277
+ const add = (file, receiver, mount) => { const k = `${file}|${receiver}`; if (!index.has(k)) index.set(k, []); index.get(k).push(mount); };
278
+ // a module is a router module only when its exported receiver registers routes or mounts in that file
279
+ const isRouter = (file, receiver) => !!receiver && (receiversByFile.get(file) || new Set()).has(receiver);
280
+ for (const u of uses) {
281
+ if (u.prefixDynamic) continue;
282
+ const middleware = [];
283
+ for (const a of u.args) {
284
+ let target = null;
285
+ const req = /^require\(\s*(['"])([^'"]+)\1\s*\)$/.exec(String(a.text || '').trim());
286
+ if (req) {
287
+ const res = resolveJsModule(u.file, req[2], files);
288
+ const recv = res && res.target && files.has(res.target) ? exportedReceiver(fileContents[res.target]) : null;
289
+ if (recv && isRouter(res.target, recv)) target = { file: res.target, receiver: recv };
290
+ } else if (a.name && a.name !== u.receiver && isRouter(u.file, a.name)) {
291
+ target = { file: u.file, receiver: a.name };
292
+ } else if (a.name) {
293
+ const via = resolveViaImports(imports, u.file, a.name);
294
+ const recv = via && via.kind === 'exact-import' && via.targetFile && files.has(via.targetFile) ? exportedReceiver(fileContents[via.targetFile]) : null;
295
+ if (recv && isRouter(via.targetFile, recv)) target = { file: via.targetFile, receiver: recv };
296
+ }
297
+ if (target) add(target.file, target.receiver, { prefix: u.prefix, file: u.file, line: u.line, receiver: u.receiver, middleware: [...middleware] });
298
+ else if (a.name && !req) middleware.push(a.name);
299
+ }
300
+ }
301
+ return index;
302
+ }
303
+
304
+ /** The mount chains (root first) through which (file, receiver) is reachable; [] when it is mounted nowhere. */
305
+ function mountChains(index, file, receiver, depth = 0, seen = new Set()) {
306
+ const key = `${file}|${receiver}`;
307
+ const mounts = index.get(key) || [];
308
+ if (!mounts.length || depth >= MAX_MOUNT_DEPTH || seen.has(key)) return [];
309
+ const out = [];
310
+ const next = new Set(seen); next.add(key);
311
+ for (const m of mounts) {
312
+ const parents = mountChains(index, m.file, m.receiver, depth + 1, next);
313
+ if (!parents.length) out.push([m]);
314
+ else for (const pc of parents) out.push([...pc, m]);
315
+ if (out.length >= MAX_MOUNT_PATHS) break;
316
+ }
317
+ return out.slice(0, MAX_MOUNT_PATHS);
318
+ }
319
+
215
320
  function pathMatchesPrefix(routePath, prefix) {
216
321
  if (prefix === '/' || prefix === '') return true;
217
322
  const p = prefix.replace(/\/+$/, '');
@@ -226,23 +331,24 @@ export async function collectEntrypoints({ fileContents, graph, config = {} }) {
226
331
  const kinds = new Set(config.entrypointKinds && config.entrypointKinds.length ? config.entrypointKinds : ['http']);
227
332
  const idx = symbolIndexes(graph);
228
333
  const uses = collectUses(fileContents);
334
+ const routes = (kinds.has('http') || kinds.has('webhook')) ? await collectRoutes(fileContents) : [];
335
+ const mountIndex = buildMountIndex(fileContents, uses, routes);
229
336
  const exclude = (config.exclude || []).map(globToRe);
230
337
  const cap = Number.isInteger(config.maxEntrypoints) && config.maxEntrypoints > 0 ? config.maxEntrypoints : 5000;
231
338
  const ctx = { fileContents, idx, graph };
232
339
  const records = [];
233
340
 
234
341
  if (kinds.has('http') || kinds.has('webhook')) {
235
- for (const route of await collectRoutes(fileContents)) {
342
+ for (const route of routes) {
236
343
  const isWebhook = /webhook/i.test(route.path || '');
237
344
  if (isWebhook && !kinds.has('webhook') && !kinds.has('http')) continue;
238
345
  const h = resolveHandler(route, ctx);
239
346
  const reasons = [...h.reasons];
240
347
  const middleware = [...h.middleware];
348
+ const receiver = routeReceiver(fileContents, route);
349
+ const chains = receiver ? mountChains(mountIndex, route.file, receiver) : [];
241
350
  // inherited middleware registered with `X.use(...)` on the same receiver, earlier in the same file
242
351
  if (JS_FILE.test(route.file)) {
243
- const lines = fileContents[route.file].split('\n');
244
- const recvM = /([A-Za-z_$][\w$]*)\.(?:get|post|put|patch|delete|all|options|head|route)\s*\(/.exec(lines[route.line - 1] || '');
245
- const receiver = recvM ? recvM[1] : null;
246
352
  for (const u of uses) {
247
353
  if (u.file !== route.file || u.line >= route.line || u.receiver !== receiver) continue;
248
354
  if (!pathMatchesPrefix(String(route.path || ''), u.prefix)) continue;
@@ -253,17 +359,35 @@ export async function collectEntrypoints({ fileContents, graph, config = {} }) {
253
359
  if (!r.symbol) reasons.push(`inherited-middleware-${r.resolution}:${a.name}`);
254
360
  }
255
361
  }
256
- if (receiver && /^(?:router|routes?|r|api|\w*Router)$/i.test(receiver) && !uses.some(u => u.receiver !== receiver && u.args.some(a => a.name === receiver))) {
257
- reasons.push('router-mount-not-resolved');
258
- }
362
+ if (receiver && !chains.length && ROUTER_NAME_RE.test(receiver)) reasons.push('router-mount-not-resolved');
259
363
  }
364
+ // middleware the mount statements apply to everything mounted under them (`app.use('/api', auth, router)`):
365
+ // per chain, so a router mounted twice inherits each mount's own middleware and not the other's
366
+ const chainMiddleware = (chain) => {
367
+ const mw = []; const why = [];
368
+ for (const hop of chain) {
369
+ for (const name of hop.middleware) {
370
+ const r = resolveName(name, hop.file, idx);
371
+ mw.push({ name, symbolId: r.symbol ? r.symbol.id : null, resolution: r.resolution, line: hop.line, scope: hop.prefix, inherited: true, viaMount: true });
372
+ if (!r.symbol) why.push(`inherited-middleware-${r.resolution}:${name}`);
373
+ }
374
+ }
375
+ return { mw, why };
376
+ };
260
377
  const authScheme = route.webModel
261
378
  ? { status: route.webModel.auth.status, kind: route.webModel.auth.kind || null, basis: 'haskell-web-model', role: route.webModel.role.status, ownership: route.webModel.ownership || null, idLookups: route.webModel.idLookups || [], csrf: !!route.webModel.csrf }
262
379
  : { status: route.hasAuth ? 'authenticated' : 'none', basis: 'route-scan-heuristic', note: 'authentication seen near the registration; says nothing about ownership' };
380
+ const mountsOf = chains.map(chain => ({
381
+ prefix: chain.reduce((acc, hop) => joinMountPath(acc, hop.prefix), '/'),
382
+ chain: chain.map(hop => ({ file: hop.file, line: hop.line, receiver: hop.receiver, prefix: hop.prefix })),
383
+ ...chainMiddleware(chain),
384
+ })).sort((a, b) => (a.prefix < b.prefix ? -1 : a.prefix > b.prefix ? 1 : 0));
263
385
  const base = {
264
386
  kind: isWebhook ? 'webhook' : 'http',
265
387
  method: String(route.method || 'GET'),
266
388
  pathTemplate: String(route.path || ''),
389
+ registeredPath: String(route.path || ''),
390
+ mount: null,
267
391
  framework: route.framework || null,
268
392
  language: route.language === 'haskell' ? 'haskell' : (JS_FILE.test(route.file) ? 'javascript' : (route.file.split('.').pop())),
269
393
  registrationSite: { file: route.file, line: route.line },
@@ -276,7 +400,15 @@ export async function collectEntrypoints({ fileContents, graph, config = {} }) {
276
400
  externalDependencies: [],
277
401
  resolutionReasons: [...new Set(reasons)].sort(),
278
402
  };
279
- records.push(base);
403
+ if (!mountsOf.length) { records.push(base); continue; }
404
+ for (const m of mountsOf) {
405
+ const { mw, why, ...mount } = m;
406
+ records.push({
407
+ ...base, pathTemplate: joinMountPath(mount.prefix, base.registeredPath), mount: { ...mount, alternatives: mountsOf.length },
408
+ middleware: [...base.middleware.map(x => ({ ...x })), ...mw],
409
+ resolutionReasons: [...new Set([...base.resolutionReasons, ...why])].sort(),
410
+ });
411
+ }
280
412
  }
281
413
  }
282
414
 
@@ -17,7 +17,7 @@ import * as path from 'node:path';
17
17
 
18
18
  const JS_EXTS = ['', '.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx', '/index.js', '/index.mjs', '/index.ts', '/index.tsx'];
19
19
 
20
- function resolveJsModule(fromFile, source, files) {
20
+ export function resolveJsModule(fromFile, source, files) {
21
21
  if (!source.startsWith('.')) return { kind: 'third-party', target: null };
22
22
  const base = path.posix.normalize(path.posix.join(path.posix.dirname(fromFile), source));
23
23
  for (const ext of JS_EXTS) if (files.has(base + ext)) return { kind: 'repo', target: base + ext };
@@ -20,7 +20,7 @@ import { redactPayload } from '../egress/redact.js';
20
20
  import { evaluateEgress } from '../egress/policy.js';
21
21
  import { resolveProvider } from '../llm-validator/providers.js';
22
22
  import { resolveLlmInvokeWithDecision } from '../discovery/llm-invoke.js';
23
- import { hashOf, safeLabel, safeRelPath, sha256 } from './canonical.js';
23
+ import { hashOf, safeLabel, safeRelPath, sha256, sliceLines } from './canonical.js';
24
24
  import { estimateTokens } from './packer.js';
25
25
  import { REVIEW_SCHEMA_VERSION, packRef } from './prompts/packs.js';
26
26
 
@@ -249,8 +249,13 @@ export function resolveCitation(c, segments, fileContents) {
249
249
  if (typeof text !== 'string') return 'file-not-in-snapshot';
250
250
  const fileLines = text.split('\n');
251
251
  if (c.endLine > fileLines.length) return 'beyond-end-of-file';
252
- const inside = segments.some(s => s.file === c.file && c.startLine >= s.startLine && c.endLine <= s.endLine);
253
- if (!inside) return 'outside-provided-context';
252
+ const inside = segments.filter(s => s.file === c.file && c.startLine >= s.startLine && c.endLine <= s.endLine);
253
+ if (!inside.length) return 'outside-provided-context';
254
+ // GA-03.2 (0.195.26): the citation is bound to the exact lines the bundle was built from. A segment carries the
255
+ // hash of its span at build time; if the snapshot's lines no longer hash to it, the model reviewed text that is
256
+ // not what is on disk now, and the citation is stale rather than evidence. A segment with no recorded hash
257
+ // (a hand-built one) cannot be judged and is not refused for that reason.
258
+ if (inside.every(s => typeof s.spanHash === 'string' && sha256(sliceLines(text, s.startLine, s.endLine)) !== s.spanHash)) return 'stale-context';
254
259
  return claimGrounded(c.claim, fileLines.slice(c.startLine - 1, c.endLine).join('\n')) ? null : 'claim-not-grounded-in-cited-lines';
255
260
  }
256
261
 
package/src/engine.js CHANGED
@@ -222,6 +222,7 @@ import { createStringMemo } from './util/string-memo.js';
222
222
  import { collectIrStats, irStatsTarget, writeIrStats } from './ir/ir-stats.js';
223
223
  import { runDeepAnalysis } from './dataflow/index.js';
224
224
  import { buildLineageGraph } from './lineage/index.js';
225
+ import { getRepoState, worktreeDirtyExcluding } from './posture/provenance/git-evidence.js';
225
226
  import { buildLineageGraphOwned, resolveLineageDeadlineMs } from './lineage/owned-build.js';
226
227
  import { completionOfGraph, summarizeForHealth as summarizeCompletionForHealth } from './lineage/completion.js';
227
228
  // v3 next-gen — Pillars 1, 4, 5, 6, 8, 9.
@@ -9747,6 +9748,12 @@ function _deterministicFileTimings(timings) {
9747
9748
  const _lineageDeadlineMs = resolveLineageDeadlineMs();
9748
9749
  const { perFile, callGraph } = _lineageDeadlineMs ? {} : (_sharedIR || (_sharedIR = await _buildIR()));
9749
9750
  const _lineageT0 = Date.now();
9751
+ let _lineageRepo = null;
9752
+ try {
9753
+ _lineageRepo = scanRoot ? getRepoState(scanRoot) : null;
9754
+ // The scanner's own state directory must not make an unchanged tree read as dirty between two scans.
9755
+ if (_lineageRepo) _lineageRepo = { ..._lineageRepo, dirty: worktreeDirtyExcluding(scanRoot) };
9756
+ } catch { _lineageRepo = null; }
9750
9757
  setProgress({ phase: 'Lineage graph', current: 0, total: 0, file: `starting (budget ${Math.round(_lineageBudgetMs / 1000)}s)` });
9751
9758
  const _lineageOpts = {
9752
9759
  // Live per-function progress from the field-identity driver's own
@@ -9777,6 +9784,9 @@ function _deterministicFileTimings(timings) {
9777
9784
  // GA-03.3 (0.195.25): cross-service handoff joins in the lineage
9778
9785
  // graph, opt-in. Off, the graph is the one 0.195.24 built.
9779
9786
  ...(process.env.AGENTIC_SECURITY_LINEAGE_BOUNDARIES === '1' ? { boundaries: { enabled: true } } : {}),
9787
+ // GA-03.7 (0.195.26): the revision the graph is built from, so a
9788
+ // consumer can refuse a graph from another one. Null outside git.
9789
+ ...(_lineageRepo && _lineageRepo.head ? { commit: _lineageRepo.head, worktreeDirty: _lineageRepo.dirty } : {}),
9780
9790
  };
9781
9791
  // DAG-07 R07.7: with AGENTIC_SECURITY_LINEAGE_DEADLINE_MS set, the build runs in an
9782
9792
  // owned worker with a watchdog. Unset: the in-process call, exactly as before.
@@ -1353,3 +1353,7 @@ A value leaving one function through a queue publish or a store write and re-ent
1353
1353
  | `index.js` / `engine.js` (scan) | `AGENTIC_SECURITY_LINEAGE_BOUNDARIES=1` sets `boundaries.enabled`; `index.js` reads `.agentic-security/boundary-contracts.json` (the file `dataflow contracts` reads) once, existence-gated, a malformed file printed and treated as no contracts. `bench/dag/lib/scan-unit.mjs` forces the variable on, as it does deep mode. |
1354
1354
 
1355
1355
  Measured on the multi-service fixture, scan path (`node bench/dag/run.mjs --only=fixture`): flow labels 6 of 6 positive and 3 of 3 negative, where 0.195.24 found 3 of 6; the two site labels (stored value into HTML) stay FN, they need a storage-read source in the taint engine; one reported flow matches no label (`qty` into `orders.total`, a derived numeric write the label set does not list; left unlabelled under the no-relabelling rule). Precision of the join rests on three things stated above: a literal identity on both sides, the ids that really reached the producer site, and outermost-path field mapping. What it does not do: a queue consumed through a callback (`consumer.on('message', cb)`), a store read through an ORM model, a batch send's per-entry bodies, an identity that is a template string or a concatenation, and a producer in another repository (that is `cross-repo-link.js`'s declared edge, unchanged).
1356
+
1357
+ ## Frozen artifact contracts and the scanned revision (GA-03.7, 0.195.26)
1358
+
1359
+ `docs/reference/code-boundaries-contract.md` freezes the shape of the signed lineage graph, witness bundles, boundary contracts, context bundles, sink query keys and cut plans, and `test/contracts/code-boundaries.contract.test.js` holds every table in both directions and exercises each rule with a real tamper, a real wrong revision and a real partial analysis (it is the `contractTest` of the `code-boundaries`, `graph-precision` and `context-intelligence` registry rows, so `check-ga-contracts` runs it). Two things changed in code for it: `graph-builder.js` records `graph.scope.revision = { commit, worktreeDirty }` when the caller passes `commit` (the scan passes the repository head from `getRepoState`; a build without one is byte-identical to before), and `server/graph-loader.js`'s `loadSignedGraph(root, { expectRevision })` refuses with `wrong-revision` a graph that records another commit, or none (`dataflow view --expect-revision <sha|HEAD>`). The incremental 3x end-to-end target (GA-03.6) was measured, not met, and the measured reason is under "Open items" in `docs/implementation-status/dag-improvements.md`.
@@ -602,6 +602,14 @@ export function buildDataFlowGraph(callGraph, opts = {}) {
602
602
  // module ever passed `undefined`/omitted the key entirely).
603
603
  generatedAt: opts.generatedAt ?? '1970-01-01T00:00:00.000Z',
604
604
  });
605
+ // GA-03.7 (0.195.26): the revision the graph was built from, when the
606
+ // caller knows it (a scan passes the repository head). A consumer can then
607
+ // refuse a graph built from another revision (`loadSignedGraph`'s
608
+ // `expectRevision`). Absent when no commit was given, so a build without
609
+ // one is byte-identical to before.
610
+ if (typeof opts.commit === 'string' && opts.commit) {
611
+ graph.scope.revision = { commit: opts.commit, worktreeDirty: typeof opts.worktreeDirty === 'boolean' ? opts.worktreeDirty : null };
612
+ }
605
613
 
606
614
  const nodesById = new Map();
607
615
  const edgesById = new Map();
@@ -301,6 +301,8 @@ export function buildLineageGraph(callGraph, opts = {}) {
301
301
  const built = buildGraphWithCoverage(lineageCallGraph, {
302
302
  repository: opts.repository,
303
303
  ...(boundaries ? { boundaries } : {}),
304
+ // GA-03.7: the scanned revision, recorded in `graph.scope.revision` and in the graph id.
305
+ ...(typeof opts.commit === 'string' && opts.commit ? { commit: opts.commit, worktreeDirty: opts.worktreeDirty } : {}),
304
306
  generatedAt: opts.deterministic ? undefined : new Date().toISOString(),
305
307
  perFile: opts.perFile,
306
308
  parseFailures: opts.parseFailures,
@@ -906,7 +906,7 @@ are both pre-existing unrelated fields.
906
906
  | `coordinator.js` | the integration point — budget, cache, per-finding dispatch, the terminal-status guarantee |
907
907
  | `git-evidence.js` | the only Git wrapper (`getRepoState`, `blameLine`, `candidateCommitsForLine`, `getBlobAtCommit`, `commitMeta`) |
908
908
  | `origin-resolver.js` | which commit introduced a SAST finding |
909
- | `dag-walk.js` | (M3 §3.1) non-first-parent DAG walk + revert/cherry-pick detection for `--provenance deep` |
909
+ | `dag-walk.js` | (M3 §3.1) non-first-parent DAG walk + revert/cherry-pick detection for `--provenance deep`. Since 0.195.27 (GA-05) `origin-resolver.js` applies the revert and cherry-pick annotation to EVERY origin deep mode resolves, not only the ones the retry loop found, and a `Reapply "..."` subject (git's name for a revert of a revert since 2.43) counts as a revert; the diff inversion still decides. The corpus `bench/provenance-accuracy` holds ten adverse histories (revert, cherry-pick, squash, amend, `git mv`, dirty file, first-parent merge, conflict resolution, transitive lockfile, unreachable provider) and the contract is `docs/reference/finding-provenance-contract.md` |
910
910
  | `predicate-replay.js` | was this finding's condition true at commit X (calls `runFullScan` on that commit's blobs) |
911
911
  | `sca-origin.js` | which commit moved a directly-declared dependency version into an advisory's vulnerable range |
912
912
  | `transitive-sca.js` | (M3 §3.2) the same question for a TRANSITIVE dependency, re-deriving lockfile ancestry per historical commit |
@@ -918,7 +918,7 @@ are both pre-existing unrelated fields.
918
918
  | `schema.js` | the status/method/role/confidence enums, `emptyProvenance`, `redactFindingProvenance`, `isProvenanceHealthy` |
919
919
  | `validate.js` | shape assertion for tests |
920
920
  | `missing-control-resolver.js` | (M3 §3.3, FR-PROV-017) when a previously-observed safeguard disappeared — **wired into `coordinator.js`**: `resolveMissingControlOrigin` calls `resolveMissingControl` for any finding with `missingControlCandidate:true` (today, `sast/rate-limit.js`'s findings) |
921
- | `providers/config.js`, `providers/github.js`, `providers/gitlab.js` | (M3 §3.4, FR-PROV-022) GitHub/GitLab PR-metadata + CODEOWNERS fetch, config resolved from `.agentic-security/provenance-providers.yml` / token env vars — **wired into `coordinator.js`**: `resolveProviderConfig` is resolved once per scan in `annotateGitProvenance`, and `fetchPRMetadata`/`fetchCodeowners` are called per `complete`-status finding (capped, see `MAX_PROVIDER_ENRICHMENTS_PER_SCAN`), landing on `findingProvenance.providerEnrichment` |
921
+ | `providers/config.js`, `providers/github.js`, `providers/gitlab.js` | (M3 §3.4, FR-PROV-022) GitHub/GitLab PR-metadata + CODEOWNERS fetch, config resolved from `.agentic-security/provenance-providers.yml` / token env vars — **wired into `coordinator.js`**: `resolveProviderConfig` is resolved once per scan in `annotateGitProvenance`, and `fetchPRMetadata`/`fetchCodeowners` are called per `complete`-status finding (capped, see `MAX_PROVIDER_ENRICHMENTS_PER_SCAN`), landing on `findingProvenance.providerEnrichment`. Since 0.195.27 a provider that errs or does not answer returns `{unavailable, reason}` instead of `null`, and the coordinator records a limitation naming the provider and the reason; `null` means "no pull request names this commit" and nothing else |
922
922
  | `repo-lineage.js` | (M4 §4.2) loads + fully verifies an operator-declared `.agentic-security/repo-lineage.json` cross-repo link (local clones only, no remote fetch) — used by `origin-resolver.js`'s root-commit case, not a standalone-unwired module |
923
923
  | `ai-authorship.js` | (M4 §4.3) extensible AI-authorship verifier registry (`registerAIAuthorshipVerifier`/`resolveAIAuthorship`), defaults to `{status:'unknown', verifier:null}` with nothing registered (today's real state) — wired into `origin-resolver.js`'s `originFrom`, so every SAST `findingOrigin` carries `aiAuthorship`; scoped to SAST only, not direct/transitive SCA origins |
924
924
 
@@ -1014,6 +1014,14 @@ Turns a PROVEN finding into a validated detection, hunts local historical logs f
1014
1014
  * **Redaction before persistence or display** (`redact.js`), on top of the proof store's secret scrubber. Records live under `.agentic-security/dnr/` (registered in `artifact-registry.js`, evidence class, encrypted by policy, tamper-evident).
1015
1015
  * **Names:** a detection is not a finding rule. `rule-compiler/` promotes DETECTION rules for the scanner; `dnr/` produces RESPONSE rules for a SIEM, a WAF or a log search. Do not merge them.
1016
1016
 
1017
+ ## Finding-to-rule compiler: `rule-compiler/` (PRD 3, GA-07)
1018
+
1019
+ `cli.js` (`rules synthesize|evaluate|promote|status|rollback|deprecate|cost|regress`), `ir.js`, `corpus.js`, `mutation.js`, `promotion.js`, `proposal.js`, `lifecycle.js`, `store.js`, `regression.js` and `eligibility.js`; contract `docs/reference/rule-compiler-contract.md`, guide `docs/guides/rule-compiler.md`, tests `test/rule-compiler/` and `test/cli/rule-compiler-cli.test.js` (`test:posture`). Since 0.195.27 (GA-07 §2 and §3):
1020
+
1021
+ * **`negative-corpus.js`** is the shared negative corpus shipped with the scanner: safe snippets as strings in a module (no build step, identical everywhere) that `regression.js` puts in front of the shared false-positive corpus, so every evaluation, promotion and regression is held to it. Keep every entry free of the unsafe form; a vulnerable snippet here fails a correct rule. Directories named in `AGENTIC_SECURITY_RULES_FP_CORPUS` are held to as well.
1022
+ * **The ledger is portable.** `store.js` `saveLedger(root, ledger, { authorKey })` writes `ledger.json.author.sig` (Ed25519, the key `promote --sign-key` takes) beside the per-install HMAC signature, and `loadLedger` accepts a ledger whose chain verifies and whose author signature verifies against `AGENTIC_SECURITY_RULES_PUBLIC_KEY` or the rule-pack trust store when the HMAC does not (another install, a CI runner). A project-local key file alone is not trust. The ledger object carries `verifiedBy` (`install-key`, `author-key`, `unsigned-accepted`), stripped before it is written.
1023
+ * **Promotion records what was reviewed.** `--scope` is required; the ledger entry and `promotionEvent.evidence` carry digests of the evaluation, the provenance and both corpus halves (`regression.js` `corpusHash`), and `regress` reports a rule whose corpus changed after the review. `promotion.js` `dependentExamples` refuses an independent example that equals the original, its patch or a generated mutation after whitespace is collapsed.
1024
+
1017
1025
  ## Eval Lab and the policy router: `evaluation/lab/`, `routing/policy-router.js` (PRD 6)
1018
1026
 
1019
1027
  Measures a deterministic baseline and model arms, and routes tasks from the measurements. Entry points `evaluation/lab/cli.js` (`cmdEval`, `cmdModelsLab`) and `routing/policy-router.js`; guide `docs/guides/eval-lab.md`; tests `test/eval-lab/`, `test/cli/eval-lab-cli.test.js`.
@@ -436,6 +436,7 @@ export async function runRuleTests(scanRoot, fixtureGlob) {
436
436
  const files = await globFiles(fixtureGlob);
437
437
  console.log(`Loaded ${rules.length} rule(s); testing against ${files.length} file(s).\n`);
438
438
  let fired = 0;
439
+ let failures = 0;
439
440
  for (const fp of files) {
440
441
  let content = '';
441
442
  try { content = fs.readFileSync(fp, 'utf8'); } catch { continue; }
@@ -446,12 +447,16 @@ export async function runRuleTests(scanRoot, fixtureGlob) {
446
447
  if (findings.length) {
447
448
  fired++;
448
449
  const verdict = expectClean ? 'FAIL (false positive)' : 'PASS';
450
+ if (expectClean) failures++;
449
451
  console.log(` [${verdict}] ${r.id} → ${fp}:${findings[0].line}`);
450
452
  } else if (expectFire) {
453
+ failures++;
451
454
  console.log(` [FAIL (missed)] ${r.id} → ${fp}`);
452
455
  }
453
456
  }
454
457
  }
455
- console.log(`\n${fired} match(es) across ${files.length} file(s).`);
456
- return { ok: true, rules: rules.length, fired };
458
+ // GA-07 §3 (0.195.27): `ok` reports the verdicts, not merely that the run finished. It used to be `true` whatever the fixtures
459
+ // said, so `rule test` could never fail a build; a missed vulnerable fixture or a hit on a clean one is now a failure.
460
+ console.log(`\n${fired} match(es) across ${files.length} file(s)${failures ? `, ${failures} FAILED` : ''}.`);
461
+ return { ok: failures === 0, rules: rules.length, fired, failures };
457
462
  }
@@ -79,7 +79,7 @@ async function loadProgram(root) {
79
79
  // ---------------------------------------------------------------------------------------------------------------------
80
80
 
81
81
  /**
82
- * agentic-security dataflow view [path] [--node <id> [--direction in|out|both] [--relations a,b] [--origins a,b]] [--output <file>]
82
+ * agentic-security dataflow view [path] [--node <id> [--direction in|out|both] [--relations a,b] [--origins a,b]] [--expect-revision <sha|HEAD>] [--output <file>]
83
83
  * Read-only over the signed lineage graph. Without --node it reports the view identity, counts, completion and the SCC
84
84
  * condensation of the data-flow relation. With --node it lists the neighbours the filter allows.
85
85
  */
@@ -95,7 +95,18 @@ export async function cmdDataflowView(args) {
95
95
  if (f.node === undefined && (f.direction || f.relations || f.origins)) { err(`${who}: --direction, --relations and --origins apply to --node.`); return 2; }
96
96
 
97
97
  const { loadSignedGraph } = await import('../server/graph-loader.js');
98
- const loaded = loadSignedGraph(root);
98
+ // GA-03.7: `--expect-revision <sha|HEAD>` refuses a graph built from another revision (or from none).
99
+ let expectRevision = null;
100
+ if (f['expect-revision'] !== undefined) {
101
+ if (typeof f['expect-revision'] !== 'string' || !f['expect-revision']) { err(`${who}: --expect-revision needs a commit or HEAD.`); return 2; }
102
+ if (f['expect-revision'] === 'HEAD') {
103
+ const { getRepoState } = await import('./provenance/git-evidence.js');
104
+ const st = getRepoState(root);
105
+ if (!st || !st.head) { err(`${who}: --expect-revision HEAD needs a git repository at ${root}.`); return 2; }
106
+ expectRevision = st.head;
107
+ } else expectRevision = f['expect-revision'];
108
+ }
109
+ const loaded = loadSignedGraph(root, { expectRevision });
99
110
  if (!loaded.ok) { err(`${who}: ${loaded.message}`); return 1; }
100
111
  const { dataflowView, GraphViewError } = await import('../graph/index.js');
101
112
  const { computeScc, verifyScc } = await import('../graph/scc.js');
@@ -107,6 +118,8 @@ export async function cmdDataflowView(args) {
107
118
  schema: 'agentic-security/graph-view-report@1',
108
119
  maturity: 'preview',
109
120
  identity: view.identity,
121
+ revision: loaded.graph.scope && loaded.graph.scope.revision ? loaded.graph.scope.revision : null,
122
+ expectedRevision: expectRevision,
110
123
  metadata: view.metadata(),
111
124
  viewCompletion: view.completion(),
112
125
  note: 'A view is a read-only lens over the signed graph. An absent edge means the model has no such edge, not that the software has none.',
@@ -101,7 +101,7 @@ const MAX_CONCURRENCY = 4;
101
101
  // quotient: a 200-finding scan under a 60s global budget divides to 300ms,
102
102
  // which is less than a single `git blame`'s own 2s timeout and would starve
103
103
  // every finding equally instead of a few. 2s is one blame's worth of work.
104
- const MIN_PER_FINDING_BUDGET_MS = 2000;
104
+ export const MIN_PER_FINDING_BUDGET_MS = 2000;
105
105
 
106
106
  // FR-PROV-022: an 8s-timeout-capped network call per finding is not
107
107
  // automatically bounded by the scan's own deadlineAt -- N findings x up to
@@ -563,7 +563,11 @@ async function resolveAndCache(finding, ctx, cacheKey, isSca, isTransitiveSca, i
563
563
  const fetchPRFn = ctx.providerName === 'github' ? fetchPRMetadataGithub : fetchPRMetadataGitlab;
564
564
  const fetchCodeownersFn = ctx.providerName === 'github' ? fetchCodeownersGithub : fetchCodeownersGitlab;
565
565
  const pr = await fetchPRFn(scanRoot, originResult.findingOrigin.commit, ctx.remoteUrl, ctx.providerConfig);
566
- if (pr) {
566
+ if (pr && pr.unavailable) {
567
+ // GA-05 (0.195.27): forge metadata that could not be fetched is recorded, never silently absent. The origin,
568
+ // status and confidence above are unaffected: they come from local history, which the provider cannot change.
569
+ provenance.limitations.push(`provider metadata unavailable (${ctx.providerName}: ${pr.reason}); the origin commit is unaffected, only the pull request and code owner enrichment is missing`);
570
+ } else if (pr) {
567
571
  const codeowners = await fetchCodeownersFn(scanRoot, ctx.remoteUrl, ctx.providerConfig);
568
572
  provenance.providerEnrichment = {
569
573
  provider: ctx.providerName,