@clear-capabilities/agentic-security-scanner 0.161.0 → 0.162.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/bin/agentic-security.js +11 -2
- package/dist/4113.index.js +59 -8
- package/dist/5613.index.js +500 -0
- package/dist/6239.index.js +469 -0
- package/dist/7838.index.js +38 -2
- package/dist/8798.index.js +1 -1
- package/dist/9207.index.js +5 -0
- package/dist/9560.index.js +36 -22
- package/dist/{1905.index.js → 9999.index.js} +265 -8
- package/dist/agentic-security.mjs +2 -2
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +9 -4
- package/src/discovery/CLAUDE.md +9 -0
- package/src/discovery/index.js +28 -0
- package/src/discovery/memory.js +7 -5
- package/src/fix/apply-fix-service.js +33 -3
- package/src/mcp/CLAUDE.md +9 -1
- package/src/mcp/tools.js +17 -4
- package/src/posture/CLAUDE.md +174 -0
- package/src/posture/assurance/baseline-inventory.js +218 -0
- package/src/posture/assurance/baseline.js +322 -0
- package/src/posture/assurance/bounded-io.js +145 -0
- package/src/posture/assurance/config.js +299 -0
- package/src/posture/assurance/contracts.js +315 -0
- package/src/posture/assurance/identity.js +62 -0
- package/src/posture/assurance/migrations.js +259 -0
- package/src/posture/assurance/schema-kit.js +111 -0
- package/src/posture/assurance/verification-record.js +258 -0
- package/src/posture/attestation.js +5 -0
- package/src/posture/autopilot.js +28 -1
- package/src/posture/evidence-bundle.js +93 -7
- package/src/posture/execution-proof.js +11 -1
- package/src/posture/fix-verify-loop.js +20 -6
- package/src/posture/fix-verify.js +37 -2
- package/src/posture/oracles/adapters.js +360 -0
- package/src/posture/oracles/conformance.js +275 -0
- package/src/posture/oracles/oracle.js +433 -0
- package/src/posture/oracles/registry.js +76 -0
- package/src/posture/oracles/scenario-classes.js +223 -0
- package/src/posture/replay/replay.js +249 -0
- package/src/posture/verification/advisory-state.js +71 -0
- package/src/posture/verification/emit.js +257 -0
- package/src/posture/verification/hypothesis-promotion.js +211 -0
- package/src/posture/verification/patch-negative.js +214 -0
- package/src/posture/verification/patch-promotion.js +105 -0
- package/src/posture/verification/projection.js +183 -0
- package/src/posture/verification/repair-records.js +124 -0
- package/src/posture/verifier.js +16 -0
- package/src/report/index.js +49 -0
- package/src/sandbox/CLAUDE.md +41 -0
- package/src/sandbox/backend-namespace.js +7 -0
- package/src/sandbox/backend-userspace.js +68 -29
- package/src/sandbox/control-probes.js +215 -0
- package/src/sandbox/supervise.js +201 -0
- package/src/sandbox/trust-boundary.js +113 -0
- package/src/sandbox/trust-domains.js +171 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
e8b2c157e5948dbc9edd255a29b5b96f77378e8310baac39fc5f27a171cb2ec8 agentic-security.mjs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clear-capabilities/agentic-security-scanner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.162.0",
|
|
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",
|
|
@@ -67,13 +67,13 @@
|
|
|
67
67
|
"test": "node ../scripts/run-unit-tests.mjs",
|
|
68
68
|
"test:extras": "AGENTIC_SECURITY_CPP_DATAFLOW=1 node --test test/cpp-dataflow.test.js && node --experimental-test-module-mocks --test test/fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection-k8s-admission.test.js && node --experimental-test-module-mocks --test test/lineage-fault-injection.test.js && npm run test:python",
|
|
69
69
|
"loop": "node ../scripts/loop-engineering/run.mjs",
|
|
70
|
-
"test:loop": "node --test --test-concurrency=1 ../scripts/loop-engineering/test/loop-manifest.test.js ../scripts/loop-engineering/test/loop-watchdog.test.js ../scripts/loop-engineering/test/loop-background.test.js ../scripts/loop-engineering/test/loop-dashboard.test.js ../scripts/loop-engineering/test/loop-evidence.test.js ../scripts/loop-engineering/test/loop-recovery.test.js ../scripts/loop-engineering/test/loop-remote.test.js ../scripts/loop-engineering/test/loop-runbook.test.js",
|
|
70
|
+
"test:loop": "node --test --test-concurrency=1 ../scripts/loop-engineering/test/loop-manifest.test.js ../scripts/loop-engineering/test/loop-watchdog.test.js ../scripts/loop-engineering/test/loop-background.test.js ../scripts/loop-engineering/test/loop-dashboard.test.js ../scripts/loop-engineering/test/loop-evidence.test.js ../scripts/loop-engineering/test/loop-recovery.test.js ../scripts/loop-engineering/test/loop-remote.test.js ../scripts/loop-engineering/test/loop-runbook.test.js ../scripts/loop-engineering/test/loop-assurance-profile.test.js",
|
|
71
71
|
"test:python": "node ../scripts/run-python-tests.mjs",
|
|
72
72
|
"test:smoke": "node --test test/smoke.test.js test/demo-app.test.js",
|
|
73
73
|
"test:ollama-e2e": "AGENTIC_SECURITY_OLLAMA_E2E=1 node --test test/ollama-e2e.test.js",
|
|
74
74
|
"test:glob": "node --test test/glob-compat.test.js",
|
|
75
75
|
"test:sast": "node --test test/cpp-hardcoded-secret.test.js test/sibling-guard.test.js test/comment-blindness.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/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/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",
|
|
76
|
-
"test:posture": "node --test 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-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/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",
|
|
76
|
+
"test:posture": "node --test 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-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/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",
|
|
77
77
|
"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",
|
|
78
78
|
"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",
|
|
79
79
|
"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/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",
|
|
@@ -88,7 +88,11 @@
|
|
|
88
88
|
"bench:language-support:check": "node ../bench/language-support/check.mjs",
|
|
89
89
|
"bench:live-feed": "node ../bench/live-feed/run.mjs",
|
|
90
90
|
"bench:language-support:perf": "node ../bench/language-support/perf.mjs",
|
|
91
|
-
"test:
|
|
91
|
+
"test:verification": "node --test test/verification/verification-record.test.js test/verification/oracle-adapters.test.js test/verification/replay-manifest.test.js test/verification/patch-negative.test.js test/verification/non-taint-classes.test.js test/verification/interface-equivalence.test.js test/verification/advisory-gating.test.js test/verification/oracle-conformance.test.js",
|
|
92
|
+
"verification:conformance:check": "node ../scripts/verification-conformance-check.mjs",
|
|
93
|
+
"verification:conformance:static": "node ../scripts/verification-conformance-check.mjs --static",
|
|
94
|
+
"verification:replay-example": "node ../scripts/verification-replay-example.mjs",
|
|
95
|
+
"test:lifecycle": "node --test test/ci-hang-protection.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/dist-chunks-tracked.test.js && node ../scripts/lint-command-descriptions.mjs test/workflow-confinement.test.js test/check-bundle-source.test.js test/ship.test.js",
|
|
92
96
|
"test:eval": "node --test test/independent-eval.test.js test/realworld-recall.test.js",
|
|
93
97
|
"test:discovery": "node --test 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",
|
|
94
98
|
"eval:independent": "node ../bench/independent-eval/runner.mjs",
|
|
@@ -133,6 +137,7 @@
|
|
|
133
137
|
"bench:self-scan:check": "node ../bench/self-scan/check.mjs",
|
|
134
138
|
"bench:nist-800-171-labels:check": "node ../bench/nist-800-171-labels/check.mjs",
|
|
135
139
|
"bench:mutation:check": "node ../bench/mutation/runner.mjs",
|
|
140
|
+
"baseline:capture": "node ../scripts/baseline-capture.mjs",
|
|
136
141
|
"bench:protection-verdict:check": "node ../bench/protection-verdict/runner.mjs",
|
|
137
142
|
"bench:fix-correctness": "node ../bench/fix-correctness/runner.mjs",
|
|
138
143
|
"bench:iac-coverage": "node ../bench/iac-coverage/runner.mjs",
|
package/src/discovery/CLAUDE.md
CHANGED
|
@@ -52,3 +52,12 @@ panel · `judge.js` shapes findings and dedupes against the prior scan ·
|
|
|
52
52
|
`partition.js` `partitionNixFiles` groups `.nix` files by their import structure (a configuration and the modules it imports are
|
|
53
53
|
one area), merged areas are capped, and hunter prompts for Haskell and Nix are redacted (split literals joined first) before they
|
|
54
54
|
leave the process. `hunt` includes `.hs`, `.lhs` and `.nix`. With no endpoint every run is degraded and says so.
|
|
55
|
+
|
|
56
|
+
## Advisory/gating separation (X-207)
|
|
57
|
+
|
|
58
|
+
Hunt output stays advisory in code, not just by convention. `memory.js` writes only through
|
|
59
|
+
`posture/verification/advisory-state.js` (`writeAdvisoryState`: one allowlisted file, atomic rename, never through a link), and
|
|
60
|
+
`report/index.js` excludes every hunt hypothesis from findings and exit codes. A hypothesis crosses into a finding only through
|
|
61
|
+
`promoteHypothesis` (`posture/verification/hypothesis-promotion.js`), reached from `runDiscovery` only with
|
|
62
|
+
`opts.promote = { policy, verify }`: a verifier receipt the promotion code verifies, an explicit policy, and an audit record. The
|
|
63
|
+
panel's votes, a confidence score and the confirmation tier are never inputs to that decision.
|
package/src/discovery/index.js
CHANGED
|
@@ -14,6 +14,8 @@ import { confirmAll } from './confirm.js';
|
|
|
14
14
|
import { disprovePanel } from './disprove.js';
|
|
15
15
|
import { judgeCandidates } from './judge.js';
|
|
16
16
|
import { loadMemory, saveMemory, rememberRun, previouslyRefuted, nextWavePlan } from './memory.js';
|
|
17
|
+
import { emitVerification, headCommit } from '../posture/verification/emit.js';
|
|
18
|
+
import { promoteHypothesis } from '../posture/verification/hypothesis-promotion.js';
|
|
17
19
|
|
|
18
20
|
// Bridge a candidate to the deterministic layer. A taint finding at or within
|
|
19
21
|
// two lines of the candidate corroborates it; a modelled sink on the line
|
|
@@ -283,11 +285,37 @@ export async function runDiscovery(ctx = {}, opts = {}) {
|
|
|
283
285
|
}));
|
|
284
286
|
}
|
|
285
287
|
|
|
288
|
+
// X-201: one version-1 verification record per fresh hypothesis, ADDITIVE and advisory (hunt output never enters
|
|
289
|
+
// last-scan.json). A taint-corroborated candidate is `inconclusive` (a static probe is corroboration, not
|
|
290
|
+
// execution) and an uncorroborated one is `not-run`; a model refutation panel is recorded as inference only.
|
|
291
|
+
const hypothesisCommit = headCommit(opts.scanRoot);
|
|
292
|
+
const verificationRecords = [];
|
|
293
|
+
for (const f of fresh) {
|
|
294
|
+
const e = emitVerification('hunt', f, { finding: f, commit: hypothesisCommit });
|
|
295
|
+
if (e.ok) verificationRecords.push(e.record);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// X-207: opt-in promotion. A hypothesis crosses into a finding ONLY through `promoteHypothesis` (trusted verifier receipt,
|
|
299
|
+
// explicit policy, audit record). `fresh` is unchanged; promoted findings and every decision's audit record are reported
|
|
300
|
+
// separately, and nothing here writes `last-scan.json`. `opts.promote = { policy, verify(hypothesis) -> runOracle result }`.
|
|
301
|
+
let promotions = null;
|
|
302
|
+
if (opts.promote && typeof opts.promote.verify === 'function') {
|
|
303
|
+
promotions = [];
|
|
304
|
+
for (const f of fresh) {
|
|
305
|
+
let result = null;
|
|
306
|
+
try { result = await opts.promote.verify(f); } catch { result = null; }
|
|
307
|
+
const p = promoteHypothesis({ hypothesis: f, result, policy: opts.promote.policy });
|
|
308
|
+
promotions.push({ hypothesisId: p.audit.sourceHypothesis.id, decision: p.decision, audit: p.audit, finding: p.finding });
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
286
312
|
return {
|
|
287
313
|
schema: 'agentic-security/discovery@1',
|
|
288
314
|
focusAreas: areas.map(a => ({ id: a.id, label: a.label, files: a.files.length, size: a.size })),
|
|
289
315
|
runs,
|
|
290
316
|
fresh,
|
|
317
|
+
verificationRecords,
|
|
318
|
+
...(promotions ? { promotions } : {}),
|
|
291
319
|
duplicates,
|
|
292
320
|
suppressed,
|
|
293
321
|
// `refutedCandidates` holds RAW candidates straight from `disprovePanel`,
|
package/src/discovery/memory.js
CHANGED
|
@@ -40,6 +40,7 @@ import * as fs from 'node:fs';
|
|
|
40
40
|
import * as path from 'node:path';
|
|
41
41
|
|
|
42
42
|
import { stateWritesEnabled } from '../posture/state-dir.js';
|
|
43
|
+
import { writeAdvisoryState } from '../posture/verification/advisory-state.js';
|
|
43
44
|
const MEMORY_SCHEMA = 'agentic-security/discovery-memory@1';
|
|
44
45
|
export const MEMORY_FILE = path.join('.agentic-security', 'discovery-memory.json');
|
|
45
46
|
|
|
@@ -73,14 +74,15 @@ export function loadMemory(scanRoot) {
|
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* Persist. Failure is non-fatal -- the run still produced its report. X-207: the write goes through the advisory-state writer
|
|
79
|
+
* (closed file-name allowlist, atomic rename, never through a link), so hunt output cannot reach `last-scan.json` or its
|
|
80
|
+
* signature even when a hostile tree planted a link at the memory file.
|
|
81
|
+
*/
|
|
77
82
|
export function saveMemory(scanRoot, memory) {
|
|
78
83
|
try {
|
|
79
|
-
const p = path.join(scanRoot, MEMORY_FILE);
|
|
80
84
|
if (!stateWritesEnabled()) return;
|
|
81
|
-
|
|
82
|
-
fs.writeFileSync(p, JSON.stringify(memory, null, 2) + '\n');
|
|
83
|
-
return true;
|
|
85
|
+
return writeAdvisoryState(scanRoot, path.basename(MEMORY_FILE), JSON.stringify(memory, null, 2) + '\n').ok;
|
|
84
86
|
} catch {
|
|
85
87
|
return false;
|
|
86
88
|
}
|
|
@@ -42,6 +42,7 @@ import { verifyLastScan } from '../posture/integrity.js';
|
|
|
42
42
|
import { stateDir } from '../posture/state-dir.js';
|
|
43
43
|
import { applyFix as applyFixHistory, revertEntryById as revertFixEntry } from '../posture/fix-history.js';
|
|
44
44
|
import { classifyFixMaterialRisk } from '../posture/material-change.js';
|
|
45
|
+
import { appendRepairRecord } from '../posture/verification/repair-records.js';
|
|
45
46
|
import { loadApproverRegistry, verifyApprover, requiredRolesFor, checkSeparationOfDuties } from './approver-registry.js';
|
|
46
47
|
|
|
47
48
|
const RESERVED_WRITE_PREFIXES = [
|
|
@@ -191,6 +192,12 @@ async function getVerifyFixCore() {
|
|
|
191
192
|
* .agentic-security/authorized-approvers.json is configured, `approvedBy` is ALSO checked
|
|
192
193
|
* against it (fix/approver-registry.js) — an approvedBy the registry doesn't recognize, or one
|
|
193
194
|
* missing a role a touched category requires, is refused. A no-op with no registry configured.
|
|
195
|
+
* @param {object} [opts.patchNegative] - X-204: `{ original: {files, entry, oracleId, inputs}, functional: {inputs}, commit?, ... }`,
|
|
196
|
+
* the exploit scenario and functional cases the patch must satisfy. Honoured only when the `patch-negative-verification`
|
|
197
|
+
* feature (and `verification-oracles`) is enabled; otherwise ignored and this function behaves exactly as before. When it
|
|
198
|
+
* runs, a patch is written only if it is `verified-fix`, and the result carries `patchNegative` plus `repairRecords`: SEPARATE
|
|
199
|
+
* records for the proposal, its promotion or rejection, its application and any rollback.
|
|
200
|
+
* @param {object} [opts.assuranceConfig] - resolved assurance config (defaults to the process environment)
|
|
194
201
|
* @param {boolean} [opts.dryRun]
|
|
195
202
|
* @param {boolean} [opts.skipVerification] - escape hatch for callers that have ALREADY verified
|
|
196
203
|
* (none currently use this — present so a future caller cannot be forced into double verification
|
|
@@ -202,7 +209,7 @@ async function getVerifyFixCore() {
|
|
|
202
209
|
* configured, tests when a runner is detected — genuinely ran and passed, not silently skipped.
|
|
203
210
|
* `verified:true, verifiedFull:false` is a real, honest state: applied, but on a degraded pass.
|
|
204
211
|
*/
|
|
205
|
-
export async function applyVerifiedFix({ scanRoot, finding, files, fixMeta = null, dryRun = false, skipVerification = false } = {}) {
|
|
212
|
+
export async function applyVerifiedFix({ scanRoot, finding, files, fixMeta = null, dryRun = false, skipVerification = false, patchNegative = null, assuranceConfig } = {}) {
|
|
206
213
|
if (!scanRoot) return { ok: false, applied: false, reason: 'scanRoot required' };
|
|
207
214
|
if (!finding) return { ok: false, applied: false, reason: 'finding required' };
|
|
208
215
|
if (!files || typeof files !== 'object' || !Object.keys(files).length) {
|
|
@@ -316,6 +323,9 @@ export async function applyVerifiedFix({ scanRoot, finding, files, fixMeta = nul
|
|
|
316
323
|
}
|
|
317
324
|
|
|
318
325
|
let verify = null;
|
|
326
|
+
let pn = null;
|
|
327
|
+
let repairRecords = [];
|
|
328
|
+
const pnView = (r) => ({ status: r.status, verifiedFix: r.verifiedFix, incompleteStep: r.incompleteStep, failureCode: r.failureCode, reason: r.reason, steps: r.steps, patchDigest: r.patchDigest, summary: r.summary, verificationRecord: r.verificationRecord });
|
|
319
329
|
if (!skipVerification) {
|
|
320
330
|
if (!finding.stableId) {
|
|
321
331
|
return { ok: false, applied: false, reason: 'finding has no stableId — cannot verify a candidate fix against it' };
|
|
@@ -328,22 +338,31 @@ export async function applyVerifiedFix({ scanRoot, finding, files, fixMeta = nul
|
|
|
328
338
|
originalFindingStableId: finding.stableId,
|
|
329
339
|
files: Object.fromEntries(Object.entries(confined).map(([rel, v]) => [rel, v.content])),
|
|
330
340
|
fixMeta,
|
|
341
|
+
...(patchNegative ? { patchNegative, assuranceConfig } : {}),
|
|
331
342
|
});
|
|
332
343
|
} catch (e) {
|
|
333
344
|
return { ok: false, applied: false, reason: `verification failed: ${e.message}` };
|
|
334
345
|
}
|
|
335
346
|
verify = verdict;
|
|
347
|
+
// X-204: present only when the caller asked and the feature is on. `pn` stays null on every legacy path.
|
|
348
|
+
const pnRun = verdict.patchNegative && verdict.patchNegative.status !== 'disabled' ? verdict.patchNegative : null;
|
|
349
|
+
if (pnRun) { pn = pnRun; repairRecords = pnRun.repairRecords || []; }
|
|
336
350
|
if (!verdict.ok) {
|
|
351
|
+
// the patch-negative leg may have passed while another leg did not: the rejection is still its own record
|
|
352
|
+
if (pn && pn.verifiedFix) {
|
|
353
|
+
repairRecords = appendRepairRecord(repairRecords, { kind: 'rejected', hypothesisId: finding.stableId, revision: pn.promotion?.revision ?? null, diffDigest: pn.patchDigest, step: 'other-verification-legs', reason: `rejected by verifier: ${String(verdict.summary || 'did not verify').slice(0, 200)}` }).records;
|
|
354
|
+
}
|
|
337
355
|
return {
|
|
338
356
|
ok: false, applied: false,
|
|
339
357
|
reason: `rejected by verifier: ${verdict.summary || verdict.rescan?.reason || 'did not verify'}`,
|
|
340
358
|
verify: { rescan: verdict.rescan, lint: verdict.lint ? { runner: verdict.lint.runner, ok: verdict.lint.ok } : null, honesty: verdict.honesty || null },
|
|
359
|
+
...(pn ? { patchNegative: pnView(pn), repairRecords } : {}),
|
|
341
360
|
};
|
|
342
361
|
}
|
|
343
362
|
}
|
|
344
363
|
|
|
345
364
|
if (dryRun) {
|
|
346
|
-
return { ok: true, applied: false, dryRun: true, verified: !skipVerification, files: Object.keys(confined), verify, materialClassification };
|
|
365
|
+
return { ok: true, applied: false, dryRun: true, verified: !skipVerification, files: Object.keys(confined), verify, materialClassification, ...(pn ? { patchNegative: pnView(pn), repairRecords } : {}) };
|
|
347
366
|
}
|
|
348
367
|
|
|
349
368
|
// FR-304: re-check every file against its pre-verification baseline hash,
|
|
@@ -381,11 +400,21 @@ export async function applyVerifiedFix({ scanRoot, finding, files, fixMeta = nul
|
|
|
381
400
|
for (const w of written) {
|
|
382
401
|
try { await revertFixEntry(scanRoot, w.historyId); } catch { /* best-effort; original error still propagates below */ }
|
|
383
402
|
}
|
|
403
|
+
// X-204: the rollback is its own record, after the promotion it undoes (a write that failed part-way)
|
|
404
|
+
if (pn) {
|
|
405
|
+
const rolled = appendRepairRecord(repairRecords, { kind: 'rolled-back', hypothesisId: finding.stableId, revision: pn.promotion?.revision ?? null, diffDigest: pn.patchDigest, reason: `the write failed and every file written in this batch was restored: ${String(e?.message || e).slice(0, 160)}` });
|
|
406
|
+
repairRecords = rolled.records;
|
|
407
|
+
}
|
|
384
408
|
if (e && e.name === 'FixAttemptBudgetExceededError') {
|
|
385
|
-
return { ok: false, applied: false, reason: `budget-exceeded: ${e.message}`, budgetExceeded: true, attempts: e.attempts, maxAttempts: e.max, key: e.key };
|
|
409
|
+
return { ok: false, applied: false, reason: `budget-exceeded: ${e.message}`, budgetExceeded: true, attempts: e.attempts, maxAttempts: e.max, key: e.key, ...(pn ? { patchNegative: pnView(pn), repairRecords } : {}) };
|
|
386
410
|
}
|
|
411
|
+
if (pn && e && typeof e === 'object') e.repairRecords = repairRecords;
|
|
387
412
|
throw e;
|
|
388
413
|
}
|
|
414
|
+
// X-204: application is a separate record from the promotion that allowed it
|
|
415
|
+
if (pn) {
|
|
416
|
+
repairRecords = appendRepairRecord(repairRecords, { kind: 'applied', hypothesisId: finding.stableId, revision: pn.promotion?.revision ?? null, diffDigest: pn.patchDigest, reason: `written to ${written.length} file(s) after verified-fix promotion` }).records;
|
|
417
|
+
}
|
|
389
418
|
|
|
390
419
|
// FR-305: `verified: true` only ever meant "verification was attempted",
|
|
391
420
|
// not "every required leg genuinely ran and passed" — a caller that
|
|
@@ -400,5 +429,6 @@ export async function applyVerifiedFix({ scanRoot, finding, files, fixMeta = nul
|
|
|
400
429
|
written,
|
|
401
430
|
verify: verify ? { summary: verify.summary, verifiedFull: verify.verifiedFull, degradedLegs: verify.degradedLegs || [] } : null,
|
|
402
431
|
materialClassification,
|
|
432
|
+
...(pn ? { patchNegative: pnView(pn), repairRecords } : {}),
|
|
403
433
|
};
|
|
404
434
|
}
|
package/src/mcp/CLAUDE.md
CHANGED
|
@@ -12,7 +12,7 @@ MCP server. JSON-RPC 2.0 over NDJSON on stdin/stdout. Bin entry `../../bin/agent
|
|
|
12
12
|
| `find_rule_module` | ✓ | reads `scanner/src/{sast,posture}/` to answer "which file detects CWE-X / family Y" |
|
|
13
13
|
| `lookup_cve` | ✓ | reads local OSV / KEV / EPSS cache; staleness-tiered |
|
|
14
14
|
| `synthesize_fix` | ✓ | reads last-scan; returns the patch text |
|
|
15
|
-
| `verify_fix` | ✗ | re-scans patched files in memory, runs lint + the project test suite + the fix-honesty gate + PoC re-check; does not touch the target project's own files, but appends a record to `.agentic-security/fix-metrics.jsonl` per attempt |
|
|
15
|
+
| `verify_fix` | ✗ | re-scans patched files in memory, runs lint + the project test suite + the fix-honesty gate + PoC re-check; does not touch the target project's own files, but appends a record to `.agentic-security/fix-metrics.jsonl` per attempt. Also returns `verificationRecord`, the shared version-1 verification record (X-201; additive, `apply_fix` returns it too): a static re-scan alone is `not-run`, never confirmed |
|
|
16
16
|
| `apply_fix` | ✗ | writes via `posture/fix-history.js` (with backup). With `plan_digest` it applies a MULTI-FILE NixOS option fix (see "Multi-file apply_fix" below): all files or none, one backup per file, one history group |
|
|
17
17
|
| `append_scratchpad` | ✗ | writes under `.agentic-security/agent-scratchpad/<agent>/<session>/` only |
|
|
18
18
|
| `read_scratchpad` | ✓ | paginated read of scratchpad files |
|
|
@@ -103,3 +103,11 @@ the relock path is not a finding fix), no fix-attempt-budget enforcement (the la
|
|
|
103
103
|
CLI, does not enforce a cap), and no `fixMeta` honesty gate (that gate judges a caller's residual/verdict claims, which this
|
|
104
104
|
path does not take). The `preWrite` digest re-check is defence in depth over a deterministic planner and has no independent
|
|
105
105
|
test that fails when it is removed (the earlier digest comparison is the pinned control).
|
|
106
|
+
|
|
107
|
+
## Verification view (X-206)
|
|
108
|
+
|
|
109
|
+
`explain_finding` (when the finding in `last-scan.json` carries a `verificationRecord`), `verify_fix` and `apply_fix` add
|
|
110
|
+
`verificationView`, the shared projection from `posture/verification/projection.js`, beside the existing `verificationRecord`.
|
|
111
|
+
It is the same object the JSON report, the text reports and the autopilot response carry for the same finding. Additive: a finding
|
|
112
|
+
without a record returns exactly what it did. The key is `verificationView`, not `verification` (that name belongs to the
|
|
113
|
+
producer/verifier separation record). See `docs/guides/verification-schema-migration.md`.
|
package/src/mcp/tools.js
CHANGED
|
@@ -26,6 +26,8 @@ import { withStateWritesDisabled } from '../posture/state-dir.js';
|
|
|
26
26
|
import { analyzeTranscript, formatCacheReport, renderCacheStatusLine } from '../posture/cache-economics.js';
|
|
27
27
|
import { redactString, redactFinding } from './redact.js';
|
|
28
28
|
import { _remediationOf, normalizeFindings } from '../report/index.js';
|
|
29
|
+
// X-206: the one projection of the verification record, shared with the JSON report, the text report and the autopilot response.
|
|
30
|
+
import { verificationFields } from '../posture/verification/projection.js';
|
|
29
31
|
// Git-origin provenance (Finding Provenance M0/M1). Distinct from
|
|
30
32
|
// `finding.provenance` (AI-authorship) and from an SCA entry's `provenance`
|
|
31
33
|
// (Sigstore/SLSA attestation) — see report/index.js's import comment.
|
|
@@ -599,6 +601,10 @@ export const explain_finding = {
|
|
|
599
601
|
findingProvenance: f.findingProvenance ? redactFindingProvenance(f.findingProvenance, {
|
|
600
602
|
pseudonymize: process.env.AGENTIC_SECURITY_PSEUDONYMIZE_AUTHORS === '1',
|
|
601
603
|
}) : null,
|
|
604
|
+
// X-206: the same verification projection the JSON and text reports carry; absent when the finding has no record.
|
|
605
|
+
...(f.verificationRecord && typeof f.verificationRecord === 'object'
|
|
606
|
+
? { verificationRecord: f.verificationRecord, ...verificationFields(f.verificationRecord, { replay: f.verificationReplay }) }
|
|
607
|
+
: {}),
|
|
602
608
|
};
|
|
603
609
|
},
|
|
604
610
|
};
|
|
@@ -875,7 +881,7 @@ export const apply_fix = {
|
|
|
875
881
|
// default. Normalized to the scan+lint verdict shape used below.
|
|
876
882
|
const { verifyFixWithTests } = await import('../posture/fix-verify-loop.js');
|
|
877
883
|
const t = await verifyFixWithTests({ scanRoot: ctx.sessionRoot, originalFindingStableId: f.stableId, files: _files });
|
|
878
|
-
verdict = { ok: t.ok, summary: t.summary, rescan: t.legs?.scan?.detail, lint: t.legs?.lint?.detail, tests: t.legs?.tests, testVerdict: t.verdict };
|
|
884
|
+
verdict = { ok: t.ok, summary: t.summary, rescan: t.legs?.scan?.detail, lint: t.legs?.lint?.detail, tests: t.legs?.tests, testVerdict: t.verdict, verificationRecord: t.verificationRecord ?? null };
|
|
879
885
|
} else {
|
|
880
886
|
const verifyFixCore = await getVerifyFixCore();
|
|
881
887
|
verdict = await verifyFixCore({
|
|
@@ -893,6 +899,9 @@ export const apply_fix = {
|
|
|
893
899
|
_meta: META, applied: false,
|
|
894
900
|
reason: `patch rejected by verifier: ${verdict.summary || verdict.rescan?.reason || 'did not verify'}`,
|
|
895
901
|
verify: { rescan: verdict.rescan, lint: { runner: verdict.lint?.runner, ok: verdict.lint?.ok }, honesty: verdict.honesty || null },
|
|
902
|
+
// X-201: the same version-1 verification record every surface emits (additive; null when none could be formed).
|
|
903
|
+
verificationRecord: verdict.verificationRecord ?? null,
|
|
904
|
+
...verificationFields(verdict.verificationRecord),
|
|
896
905
|
};
|
|
897
906
|
}
|
|
898
907
|
// FR-307/FR-1002/D-0024: this caller-supplied-patch branch writes via
|
|
@@ -913,7 +922,7 @@ export const apply_fix = {
|
|
|
913
922
|
}
|
|
914
923
|
const materialClassification = classifyFixMaterialRisk(filesForMaterialClassification);
|
|
915
924
|
if (dry_run) {
|
|
916
|
-
return { _meta: META, applied: false, dryRun: true, verified: true, files: Object.keys(confinedAbs), summary: verdict.summary, materialClassification };
|
|
925
|
+
return { _meta: META, applied: false, dryRun: true, verified: true, files: Object.keys(confinedAbs), summary: verdict.summary, verificationRecord: verdict.verificationRecord ?? null, ...verificationFields(verdict.verificationRecord), materialClassification };
|
|
917
926
|
}
|
|
918
927
|
const approvalRefusal = _highImpactApprovalRefusal(ctx, materialClassification, fixMeta);
|
|
919
928
|
if (approvalRefusal) return approvalRefusal;
|
|
@@ -944,7 +953,7 @@ export const apply_fix = {
|
|
|
944
953
|
}
|
|
945
954
|
let acceptance = null;
|
|
946
955
|
try { acceptance = fixAcceptanceRate(ctx.sessionRoot); } catch { /* best-effort */ }
|
|
947
|
-
return { _meta: META, applied: true, verified: true, patched: written, integrity: status, verify: { summary: verdict.summary }, acceptance, materialClassification };
|
|
956
|
+
return { _meta: META, applied: true, verified: true, patched: written, integrity: status, verify: { summary: verdict.summary, verificationRecord: verdict.verificationRecord ?? null, ...verificationFields(verdict.verificationRecord) }, acceptance, materialClassification };
|
|
948
957
|
}
|
|
949
958
|
|
|
950
959
|
if (typeof f.fix?.replacement !== 'string') {
|
|
@@ -1033,7 +1042,7 @@ export const apply_fix = {
|
|
|
1033
1042
|
// proceed with apply_fix.
|
|
1034
1043
|
export const verify_fix = {
|
|
1035
1044
|
name: 'verify_fix',
|
|
1036
|
-
description: 'Verify a proposed patch before applying. Re-scans the patched files in memory, runs the project linter, runs the project test suite, checks fix honesty (FULL/MITIGATION/WORKAROUND) when fixMeta is supplied, and re-runs the PoC when one exists. Returns { ok, rescan, lint, tests, honesty, poc, summary }. Does not write to the target project’s own files, but DOES append one record per attempt to .agentic-security/fix-metrics.jsonl for the measured fix-loop.',
|
|
1045
|
+
description: 'Verify a proposed patch before applying. Re-scans the patched files in memory, runs the project linter, runs the project test suite, checks fix honesty (FULL/MITIGATION/WORKAROUND) when fixMeta is supplied, and re-runs the PoC when one exists. Returns { ok, rescan, lint, tests, honesty, poc, summary, verificationRecord } (verificationRecord is the shared version-1 record: outcome is one of not-run, unsupported, inconclusive, error, refuted, confirmed, and a static re-scan alone is never reported as confirmed; verificationView is the shared projection every interface shows for it, with state, scope, evidence ids, replay prerequisites and what was and was not verified). Does not write to the target project’s own files, but DOES append one record per attempt to .agentic-security/fix-metrics.jsonl for the measured fix-loop.',
|
|
1037
1046
|
inputSchema: {
|
|
1038
1047
|
type: 'object',
|
|
1039
1048
|
additionalProperties: false,
|
|
@@ -1134,6 +1143,10 @@ export const verify_fix = {
|
|
|
1134
1143
|
honesty: r.honesty,
|
|
1135
1144
|
poc: r.poc ? { ...r.poc, reason: r.poc.reason ? redactString(r.poc.reason) : r.poc.reason } : r.poc,
|
|
1136
1145
|
summary: r.summary,
|
|
1146
|
+
// X-201: the one version-1 verification record (additive). It states what was actually established, e.g.
|
|
1147
|
+
// `not-run` when no exploit oracle executed against the patch, never a bare pass/fail.
|
|
1148
|
+
verificationRecord: r.verificationRecord ?? null,
|
|
1149
|
+
...verificationFields(r.verificationRecord),
|
|
1137
1150
|
};
|
|
1138
1151
|
} catch (e) {
|
|
1139
1152
|
return { _meta: META, ok: false, reason: `verify_fix failed: ${e.message}` };
|