@clear-capabilities/agentic-security-scanner 0.160.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 +38 -0
- package/bin/agentic-security.js +11 -2
- package/dist/1919.index.js +1 -1
- package/dist/2432.index.js +10 -1
- package/dist/4113.index.js +60 -9
- package/dist/5613.index.js +500 -0
- package/dist/6239.index.js +469 -0
- package/dist/7033.index.js +4 -1
- package/dist/7838.index.js +39 -3
- package/dist/8714.index.js +1 -1
- package/dist/8798.index.js +2 -2
- package/dist/9207.index.js +5 -0
- package/dist/9560.index.js +205 -62
- package/dist/{1905.index.js → 9999.index.js} +265 -8
- package/dist/agentic-security.mjs +12 -12
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +14 -9
- package/src/dataflow/engine.js +20 -4
- package/src/discovery/CLAUDE.md +9 -0
- package/src/discovery/index.js +28 -0
- package/src/discovery/memory.js +7 -5
- package/src/engine.js +29 -8
- package/src/fix/apply-fix-service.js +33 -3
- package/src/language/CLAUDE.md +6 -1
- package/src/language/aibom.js +4 -1
- package/src/language/fix-lifecycle.js +9 -0
- package/src/language/haskell-ir.js +47 -7
- package/src/language/haskell-sca.js +87 -7
- package/src/language/haskell-supply.js +175 -6
- package/src/language/haskell-web.js +200 -18
- package/src/language/nix-fix.js +1 -1
- package/src/language/resolved-pass.js +4 -1
- package/src/language/witness.js +1 -1
- package/src/mcp/CLAUDE.md +43 -4
- package/src/mcp/tools.js +185 -43
- 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/blast-radius.js +15 -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/remediation-ledger.js +10 -1
- 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
- package/src/sast/pipeline.js +51 -4
|
@@ -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,28 +67,32 @@
|
|
|
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
|
-
"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",
|
|
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/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",
|
|
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",
|
|
78
|
-
"test:mcp": "node --test test/mcp.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",
|
|
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 test/posture/assurance-baseline.test.js test/posture/assurance-contracts.test.js test/posture/assurance-config.test.js test/evidence-issuer.test.js",
|
|
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
|
+
"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",
|
|
80
80
|
"test:lineage": "node --test test/lineage/schema.test.js test/lineage/ids.test.js test/lineage/protection.test.js test/lineage/classification.test.js test/lineage/validate.test.js test/lineage/json-schema-parity.test.js test/lineage/flagship-fixture.test.js test/lineage/flagship-fixture-semantics.test.js test/lineage/field-identity.test.js test/lineage/engine-expr-resolver.test.js test/lineage/engine-walker.test.js test/lineage/engine-integration.test.js test/lineage/engine-receiver-identity.test.js test/lineage/summaries.test.js test/lineage/driver.test.js test/lineage/engine-provenance.test.js test/lineage/engine-provenance-interprocedural.test.js test/lineage/path-store.test.js test/lineage/path-query.test.js test/lineage/flow-grade.test.js test/lineage/source-registry.test.js test/lineage/sink-registry.test.js test/lineage/transform-catalog.test.js test/lineage/handling-analyzer.test.js test/lineage/registry-real-code.test.js test/lineage/source-seeding.test.js test/lineage/graph-builder.test.js test/lineage/coverage.test.js test/lineage/resolve-destination.test.js test/lineage/queue-detail.test.js test/lineage/index.test.js test/lineage/obligation-mapping.test.js test/lineage/obligation-predicates.test.js test/lineage/obligation-predicates-walkthrough.test.js test/lineage/transit-protection.test.js test/lineage/at-rest-protection.test.js test/lineage/protection-summary.test.js test/lineage/ac01-multi-sink.test.js test/lineage/policy-verdict.test.js test/lineage/governance-refs.test.js test/lineage/export-privacy.test.js test/lineage/export-json.test.js test/lineage/export-csv.test.js test/lineage/decision-story.test.js test/lineage/export-briefing.test.js test/lineage/graph-snapshot.test.js test/lineage/graph-diff.test.js test/lineage/drift-policy.test.js test/lineage/recipient-profile.test.js test/lineage/recipient-registry.test.js test/lineage/recipient-wiring.test.js test/lineage/redact-graph.test.js test/lineage-scan-wiring.test.js test/lineage-artifact-write.test.js test/bench-data-lineage-runner.test.js test/bundle-frontend.test.js test/bundle-frontend-golden.test.js test/generate-html-report.test.js test/export-image.test.js test/lineage/scenario.test.js test/lineage/scenario-engine.test.js test/lineage/scenario-diff.test.js test/lineage/scenario-no-obligation-wiring.test.js test/lineage/impact-assessment.test.js test/lineage/impact-engine.test.js test/lineage/impact-no-obligation-wiring.test.js test/lineage/governance-edit.test.js test/lineage/remediation.test.js test/lineage/runtime-observation.test.js test/lineage/observation-correlation.test.js test/lineage/observation-adapters.test.js test/lineage/observation-store.test.js test/lineage/runtime-corroboration-wiring.test.js test/lineage/cross-repo-link.test.js test/lineage/federation-loader.test.js test/lineage/cross-repo-link-wiring.test.js",
|
|
81
81
|
"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",
|
|
82
82
|
"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",
|
|
83
|
-
"test:haskell": "node --test 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-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",
|
|
84
|
-
"test:nix": "node --test test/nix/nix-build-trust.test.js test/nix/nix-eval-isolation.test.js test/nix/nix-input-inventory.test.js test/nix/nix-parser-ir.test.js test/nix/nix-remediation.test.js test/nix/nix-resolved-closure.test.js test/nix/nix-sca-patches.test.js test/nix/nix-sca-severity.test.js test/nix/nix-haskell-closure-names.test.js test/nix/nix-script-taint.test.js test/nix/nix-secrets-store.test.js test/nix/nixos-hardening.test.js test/nix/nixos-module-resolution.test.js test/nix/nix-fix-multifile.test.js test/nix/nixos-evaluator-forms.test.js test/nix/nix-advisory-feed.test.js",
|
|
83
|
+
"test:haskell": "node --test 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-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",
|
|
84
|
+
"test:nix": "node --test test/nix/nix-build-trust.test.js test/nix/nix-eval-isolation.test.js test/nix/nix-input-inventory.test.js test/nix/nix-parser-ir.test.js test/nix/nix-remediation.test.js test/nix/nix-resolved-closure.test.js test/nix/nix-sca-patches.test.js test/nix/nix-sca-severity.test.js test/nix/nix-haskell-closure-names.test.js test/nix/nix-script-taint.test.js test/nix/nix-secrets-store.test.js test/nix/nixos-hardening.test.js test/nix/nixos-module-resolution.test.js test/nix/nix-fix-multifile.test.js test/nix/nixos-evaluator-forms.test.js test/nix/nix-advisory-feed.test.js test/nix/nix-fix-prewrite.test.js",
|
|
85
85
|
"test:language": "node --test test/language/capability-ledger.test.js test/language/language-accuracy-regression.test.js test/language/language-aibom.test.js test/language/language-assurance.test.js test/language/language-bom.test.js test/language/language-compliance.test.js test/language/language-contracts.test.js test/language/language-corpora-integrity.test.js test/language/language-discovery.test.js test/language/language-doc-coverage.test.js test/language/language-evidence-posture.test.js test/language/language-explorer.test.js test/language/language-gap-fixes.test.js test/language/language-gap-fixes-2.test.js test/language/language-governance.test.js test/language/language-integrations.test.js test/language/language-llm-agent.test.js test/language/language-local-ai.test.js test/language/language-polyglot-bridges.test.js test/language/language-privacy-lineage.test.js test/language/language-proof-remediation.test.js test/language/language-report-formats.test.js test/language/language-secrets.test.js test/language/language-trust-boundary.test.js",
|
|
86
86
|
"test:language-stress": "node --test --test-concurrency=1 test/language/language-stress-offline.test.js",
|
|
87
87
|
"test:language-tools": "node --test test/haskell/haskell-web-auth.test.js test/haskell/haskell-support-gate.test.js",
|
|
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/dataflow/engine.js
CHANGED
|
@@ -329,14 +329,14 @@ function _resolveCalleeForSummary(calleeExpr, callContext) {
|
|
|
329
329
|
// call's OWN arguments, silently losing the callee's return-taint. Mirrors
|
|
330
330
|
// the same resolve -> get-or-compute -> merge sequence step()'s two existing
|
|
331
331
|
// call sites use, via the Task 3/4 shared _resolveCalleeForSummary.
|
|
332
|
-
function _nestedCallReturnTainted(calleeExpr, argExprs, state, callContext) {
|
|
332
|
+
function _nestedCallReturnTainted(calleeExpr, argExprs, state, callContext, argTaint) {
|
|
333
333
|
if (!callContext || !callContext._summaryCache) return false;
|
|
334
334
|
const target = _resolveCalleeForSummary(calleeExpr, callContext);
|
|
335
335
|
if (!target) return false;
|
|
336
336
|
const { qid, fn } = target;
|
|
337
337
|
const paramNames = (fn && Array.isArray(fn.params)) ? fn.params : [];
|
|
338
338
|
const entry = paramNames.length
|
|
339
|
-
? entryStateFromCall(paramNames, argExprs || [], state, (a) => exprTaint(a, state, callContext))
|
|
339
|
+
? entryStateFromCall(paramNames, argExprs || [], state, argTaint || ((a) => exprTaint(a, state, callContext)))
|
|
340
340
|
: new Set();
|
|
341
341
|
let sum = callContext._summaryCache.get(qid, entry);
|
|
342
342
|
if (!sum && fn && fn.cfg) {
|
|
@@ -470,7 +470,11 @@ function _calleeGetterFieldTainted(calleeExpr, state, callContext) {
|
|
|
470
470
|
return !!(receiverPath && isCoveredBy(state, `${receiverPath}.${field}`));
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
+
// Work counter for the scaling guard in test/dataflow-expr-taint-scaling.test.js (a count, not a clock).
|
|
474
|
+
export const _perf = { exprTaintCalls: 0 };
|
|
475
|
+
|
|
473
476
|
function exprTaint(expr, state, callContext) {
|
|
477
|
+
_perf.exprTaintCalls += 1;
|
|
474
478
|
if (expr && (expr.kind === 'member' || expr.kind === 'call' || expr.kind === 'ident') && exprIsSource(expr, callContext)) return true;
|
|
475
479
|
if (!expr) return false;
|
|
476
480
|
// P1.1 — field-sensitive access path: if the expression is a pure
|
|
@@ -518,8 +522,20 @@ function exprTaint(expr, state, callContext) {
|
|
|
518
522
|
// direction) so the merge always runs when a call expression is
|
|
519
523
|
// visited; _resolveCalleeForSummary + SummaryCache make repeat
|
|
520
524
|
// resolution/computation for the same (qid, entry-state) cheap.
|
|
521
|
-
|
|
522
|
-
|
|
525
|
+
// Each argument's taint is computed at most once per visit of this call. The argument loop below and the
|
|
526
|
+
// callee's entry state (built inside _nestedCallReturnTainted) both ask for it, and with the second ask
|
|
527
|
+
// re-walking the whole argument subtree the work doubled at every level of call nesting: a chain of N nested
|
|
528
|
+
// calls cost 2^N exprTaint visits (a deep-mode scan of a large Haskell project did not finish). The value asked
|
|
529
|
+
// for is the same pure function of (argument, state) both times, so reusing it changes no verdict.
|
|
530
|
+
const _argMemo = new Map();
|
|
531
|
+
const _argTaint = (a) => {
|
|
532
|
+
if (_argMemo.has(a)) return _argMemo.get(a);
|
|
533
|
+
const r = exprTaint(a, state, callContext);
|
|
534
|
+
_argMemo.set(a, r);
|
|
535
|
+
return r;
|
|
536
|
+
};
|
|
537
|
+
const argsTainted = (expr.args || []).some((a, i) => !_isSprintfSafeArg(expr, i) && _argTaint(a));
|
|
538
|
+
const nestedTainted = _nestedCallReturnTainted(expr.callee, expr.args, state, callContext, _argTaint);
|
|
523
539
|
// Taint-recall PRD (80%): a call's RECEIVER can itself be tainted
|
|
524
540
|
// independent of its arguments — `tainted.toString()`, `tainted.trim()`,
|
|
525
541
|
// `it.getBytes()` — and neither argsTainted (there are no/unrelated
|
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
|
}
|
package/src/engine.js
CHANGED
|
@@ -6684,17 +6684,32 @@ function _semverSatisfies(ver,range){
|
|
|
6684
6684
|
function markUsedVulnFunctions(supplyChain,fc){
|
|
6685
6685
|
const used={};
|
|
6686
6686
|
const perFile={};
|
|
6687
|
+
// The per-(package, function) expressions are compiled once, not once per file, and a file is split into lines only when
|
|
6688
|
+
// it contains the text a match needs: `\b(?:pkg|_)\.fn\b` can only match a line holding the substring `.fn`, and the Rust
|
|
6689
|
+
// bare-call expression one holding `fn`. Both prefilters are exact for a plain identifier; any other name skips the
|
|
6690
|
+
// prefilter and is judged line by line, as before. Match order (file, package, function, line) is unchanged.
|
|
6691
|
+
const _plainIdent=/^[A-Za-z_]\w*$/;
|
|
6692
|
+
const _hintRes=new Map();
|
|
6693
|
+
const _hintRe=(pkg,fn)=>{const k=pkg+'\0'+fn;let r=_hintRes.get(k);if(!r){r=new RegExp(`\\b(?:${pkg.replace(/\W/g,'\\$&')}|_)\\.${fn}\\b`,'g');_hintRes.set(k,r);}return r;};
|
|
6694
|
+
const _bareRes=new Map();
|
|
6695
|
+
const _bareRe=(fn)=>{let r=_bareRes.get(fn);if(!r){r=new RegExp(`\\b${fn.replace(/\W/g,'\\$&')}\\s*[(<]`,'g');_bareRes.set(fn,r);}return r;};
|
|
6696
|
+
const _hintEntries=Object.entries(VULN_FUNCTION_HINTS);
|
|
6697
|
+
for(const pkg of Object.keys(VULN_FUNCTION_HINTS))if(!perFile[pkg])perFile[pkg]=[];
|
|
6687
6698
|
for(const[fp,content] of Object.entries(fc)){
|
|
6688
|
-
|
|
6699
|
+
let lines=null;
|
|
6689
6700
|
// Rust import-aware matching: build import map for .rs files
|
|
6690
6701
|
let _rustImports=null;
|
|
6691
6702
|
if(/\.rs$/i.test(fp)){try{_rustImports=extractRustImportMap(content);}catch(_){}}
|
|
6692
|
-
for(const[pkg,fns] of
|
|
6693
|
-
if(!perFile[pkg])perFile[pkg]=[];
|
|
6703
|
+
for(const[pkg,fns] of _hintEntries){
|
|
6694
6704
|
for(const fn of fns){
|
|
6695
|
-
const
|
|
6705
|
+
const plain=_plainIdent.test(fn);
|
|
6706
|
+
const dotPossible=!plain||content.includes('.'+fn);
|
|
6707
|
+
const barePossible=_rustImports&&(!plain||content.includes(fn));
|
|
6708
|
+
if(!dotPossible&&!barePossible)continue;
|
|
6709
|
+
const re=_hintRe(pkg,fn);
|
|
6696
6710
|
// Rust: also match bare function calls if import map traces them to this package
|
|
6697
|
-
const rustBareRe=_rustImports?
|
|
6711
|
+
const rustBareRe=_rustImports?_bareRe(fn):null;
|
|
6712
|
+
if(lines===null)lines=content.split('\n');
|
|
6698
6713
|
for(let li=0;li<lines.length;li++){
|
|
6699
6714
|
let matched=re.test(lines[li]);
|
|
6700
6715
|
re.lastIndex=0;
|
|
@@ -6733,9 +6748,12 @@ function markUsedVulnFunctions(supplyChain,fc){
|
|
|
6733
6748
|
// Search codebase for these functions (if not already searched via VULN_FUNCTION_HINTS)
|
|
6734
6749
|
if(osvFns.length&&!hardcoded.length){
|
|
6735
6750
|
for(const[fp,content] of Object.entries(fc)){
|
|
6736
|
-
|
|
6751
|
+
let lines=null;
|
|
6737
6752
|
for(const fn of osvFns){
|
|
6738
6753
|
const shortFn=fn.lastIndexOf('.')>0?fn.slice(fn.lastIndexOf('.')+1):fn;
|
|
6754
|
+
// the expression escapes every non-word character, so a match needs the literal text on its line
|
|
6755
|
+
if(!content.includes(shortFn))continue;
|
|
6756
|
+
if(lines===null)lines=content.split('\n');
|
|
6739
6757
|
const re=new RegExp(`\\b${shortFn.replace(/\W/g,'\\$&')}\\b`,'g');
|
|
6740
6758
|
for(let li=0;li<lines.length;li++){
|
|
6741
6759
|
if(re.test(lines[li])){
|
|
@@ -9086,6 +9104,11 @@ function _deterministicFileTimings(timings) {
|
|
|
9086
9104
|
setProgress({current:i,total:files.length,file:"Config file cross-ref...",phase:"Linking"});aLogic.push(...(runDetector(_detectorErrors,'<project>','scanConfigFiles',()=>scanConfigFiles(fc))||[]));
|
|
9087
9105
|
setProgress({current:i,total:files.length,file:"OSV vulnerability database...",phase:"SCA"});
|
|
9088
9106
|
const allFileContents={...fc, ...depFileContents};
|
|
9107
|
+
// The opt-in live advisory feeds are fetched HERE, as soon as the manifests are in hand, not after the expensive passes: a scan that is
|
|
9108
|
+
// killed or times out later still has its feed data on disk (the lookup used to run ~1447 s into a 1460 s scan, so an early kill looked like
|
|
9109
|
+
// "feed unavailable"). The inputs are the file contents only, so the result is the same as when this ran at the end.
|
|
9110
|
+
try{if(process.env.AGENTIC_SECURITY_HACKAGE_ADVISORIES_LIVE==='1'){const{prefetchHackageFeed}=await import('./language/haskell-supply.js');const{resolvedHackageComponents,nixHaskellPackageNames}=await import('./language/resolved-pass.js');let _rc=[],_nx=[];try{_rc=resolvedHackageComponents(allFileContents).components||[];}catch(_){}try{_nx=nixHaskellPackageNames(allFileContents);}catch(_){}await prefetchHackageFeed(allFileContents,{resolvedComponents:_rc,extraNames:_nx});}}catch(_){/* the live feed is optional: a failure leaves the scan on whatever snapshot exists */}
|
|
9111
|
+
try{if(process.env.AGENTIC_SECURITY_NIX_ADVISORIES_LIVE==='1'){const{prefetchNixAdvisoryFeed}=await import('./language/resolved-pass.js');await prefetchNixAdvisoryFeed(allFileContents);}}catch(_){/* the live feed is optional: a failure leaves the scan on whatever snapshot exists */}
|
|
9089
9112
|
// PRD F11.4 — malicious install hooks, scanned where package.json actually
|
|
9090
9113
|
// lives. `scanInstallScripts` was wired into the per-file SAST dispatch, but
|
|
9091
9114
|
// that loop iterates `files`, which comes from `fileContents` — and
|
|
@@ -9231,8 +9254,6 @@ function _deterministicFileTimings(timings) {
|
|
|
9231
9254
|
const annotatedComponents=components.map(c=>{const key=`${c.ecosystem}:${c.name}:${c.version}`;const vulns=vulnsByKey[key]||[];const riKey=c.ecosystem==='maven'&&c.group?`maven:${c.group}/${c.name}`:`${c.ecosystem}:${c.name}`;const ri=registryInfo.get(riKey)||{};const latestVersion=ri.latestVersion||'';const vd=(ri.versions||{})[c.version]||{};const isDeprecated=typeof vd.deprecated==='string'&&vd.deprecated.length>0;const deprecationMessage=isDeprecated?vd.deprecated:'';const isOutdated=!isDeprecated&&typeof vd.outdated==='string'&&vd.outdated.length>0;const outdatedMessage=isOutdated?vd.outdated:'';const license=ri.license||vd.license||'';return{...c,vulns,hasVulns:vulns.length>0,hasAttackPath:attackResult.flagged.has(key),attackPaths:attackResult.pathsByKey.get(key)||[],latestVersion,isDeprecated,deprecationMessage,isOutdated,outdatedMessage,license};});
|
|
9232
9255
|
// X-010: Hackage and Nix components, dependency edges and target provenance. Kept beside (not inside) the ordinary
|
|
9233
9256
|
// component list so the existing consumers are untouched; the BOM emitters and the SBOM diff read it explicitly.
|
|
9234
|
-
try{if(process.env.AGENTIC_SECURITY_HACKAGE_ADVISORIES_LIVE==='1'){const{prefetchHackageFeed}=await import('./language/haskell-supply.js');const{resolvedHackageComponents,nixHaskellPackageNames}=await import('./language/resolved-pass.js');let _rc=[],_nx=[];try{_rc=resolvedHackageComponents(allFileContents).components||[];}catch(_){}try{_nx=nixHaskellPackageNames(allFileContents);}catch(_){}await prefetchHackageFeed(allFileContents,{resolvedComponents:_rc,extraNames:_nx});}}catch(_){/* the live feed is optional: a failure leaves the scan on whatever snapshot exists */}
|
|
9235
|
-
try{if(process.env.AGENTIC_SECURITY_NIX_ADVISORIES_LIVE==='1'){const{prefetchNixAdvisoryFeed}=await import('./language/resolved-pass.js');await prefetchNixAdvisoryFeed(allFileContents);}}catch(_){/* the live feed is optional: a failure leaves the scan on whatever snapshot exists */}
|
|
9236
9257
|
let _languageBom=null;try{if(Object.keys(allFileContents).some(f=>/\.(?:l?hs|nix)$|\.cabal$|(?:^|\/)(?:cabal\.project(?:\.freeze)?|package\.yaml|stack\.yaml(?:\.lock)?|flake\.lock)$/i.test(f))){const{languageBom}=await import('./language/bom.js');const{resolvedHaskellGraph,nixClosureOf}=await import('./language/resolved-pass.js');const _rg=resolvedHaskellGraph(allFileContents);const _nc=nixClosureOf(allFileContents);_languageBom=languageBom(allFileContents,{...(_rg?{resolved:_rg.graph}:{}),...(_nc&&!_nc.refused.length?{closure:_nc.closure}:{})});}}catch(_){_languageBom=null;}
|
|
9237
9258
|
aF.push(...(runDetector(_detectorErrors,'<project>','scanDbTaintCrossFile',()=>scanDbTaintCrossFile(fc))||[]));
|
|
9238
9259
|
aF.push(...(runDetector(_detectorErrors,'<project>','scanCsharpCrossFile',()=>scanCsharpCrossFile(fc))||[]));
|
|
@@ -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/language/CLAUDE.md
CHANGED
|
@@ -31,6 +31,9 @@ or a capability.** Product-facing guides: `docs/guides/haskell.md`, `docs/guides
|
|
|
31
31
|
queried (`covered`); a package it did not cover is `feed-incomplete` and a stale one `feed-stale`, in BOTH matchers
|
|
32
32
|
(`evaluateComponents` and `nix-sca.js`). Any new consumer of an `AdvisoryDb` must check `db.coverage(name)` before saying "no advisory".
|
|
33
33
|
The snapshot lives in the operator configuration directory, never the project (`trusted-inputs.js`).
|
|
34
|
+
- **Auth guards must stop the handler.** In `haskell-web.js` a Scotty `status 40x` only sets the code, so both the named-guard path (`guardSummary`) and the inline path (`handlerFacts`) need a halting call (`finish`, `raise`, `raiseStatus`, `redirect`; `finish` is an IR identifier with no line of its own). WAI, Servant and Yesod inline guards cannot be ordered by line (the IR reports a tail-position call at the enclosing line), so after the IR finds a candidate (credential read plus a rejection that ends the handler), `inlineOrder` decides the ORDER from the handler's syntax tree: do-block statements in sequence, a scrutinee/condition before its branches, `when`/`unless`/`if`/`case` as forks (a branch that always rejects after a read guards its siblings and everything after the fork). Leaves are classified by lowering them alone and reusing `credentialReadsOf`/`isRejection`/`stopsHandler`/`isSensitiveCall`, so there is no second definition of those. Verdicts: `before` (authenticated), `after` (read, write, then reject: `late`, CWE-306 "runs after"), `unknown` (order not provable: `late` with `evidence[0].reason`, never authenticated). `throwError`/`permissionDenied`/`notAuthenticated` stop the handler anywhere; a WAI `respond`/`return` of a 401 stops it only in TAIL position (a mid-block `respond` is followed by the next statement). Remaining limits, all failing toward `late`/missing and never toward authenticated: a rejection and a sensitive call inside one opaque expression (`maybe`/`either` with both), a handler whose source structure is unavailable, and a sensitive call the walk cannot find; also unchanged, the guarding condition is not required to mention the credential. Pinned both ways in `test/haskell/haskell-web-guards.test.js`. A WAI `responseLBS` rejection counts only when handed back (`respond`/`return`).
|
|
35
|
+
- **Declared uses are never collapsed to the first one, and project bounds are disclosed.** `hackageComponents` returns `components` (one per package, scope and resolved version, for inventories) AND `uses` (every distinct declared use, identical ones merged with all declaring components in `carriers`); the evaluator runs over `uses` and `collapseToWorst` reports ONE finding per package, scope and advisory carrying the WORST status, `carriedBy` naming the components that carry it and `otherUses` the milder ones. cabal.project `constraints:` are intersected with a declared range and `allow-newer`/`allow-older` widen it (`effectiveDeclared`), only for a use with no resolved version and only from a project file whose `packages:` governs the manifest; a constraint under an `if` is not applied and is disclosed as a gap. Widening is the safe direction: never narrow on a relaxation you cannot match.
|
|
36
|
+
- **Compiler-provided packages are one finding per advisory.** GHC boot-library findings are grouped (`grouped: 'compiler'`, `members`, `memberCount`) and counted in `counts.compilerAdvisories`, not `counts.dependencyFindings`; every use keeps its `ghc-component:*` status row. Which advisory applies is decided against `projectCompiler` (`with-compiler`, Stack compiler, a fresh plan's compiler-id, or a `tested-with` naming exactly one version; conflicting or several versions is `unknown compiler`, never a guess). `base` takes the base series of a known GHC release (`GHC_BASE_SERIES`, series only, so the result is a range); a plan's boot unit gives the exact version. A series not in the table is not guessed.
|
|
34
37
|
- **Auth guards must stop the handler.** In `haskell-web.js` a Scotty `status 40x` only sets the code, so both the named-guard path (`guardSummary`) and the inline path (`handlerFacts`) need a halting call (`finish`, `raise`, `raiseStatus`, `redirect`; `finish` is an IR identifier with no line of its own). WAI, Servant and Yesod inline guards order the credential READ against the first sensitive call, because a tail-position rejection reports the enclosing line (a disclosed gap, pinned in `test/haskell/haskell-web-guards.test.js`). A WAI `responseLBS` rejection counts only when handed back (`respond`/`return`).
|
|
35
38
|
- **Severity is the advisory's own** (`cvss.js`): CVSS v3.x base score, else a named `database_specific.severity`, else `medium` with the basis stated. Never guess from an unparseable or v4 vector.
|
|
36
39
|
- **Option fixes work on the effective configuration, across files.** `nix-fix.js` edits EVERY contributing definition of an option
|
|
@@ -41,7 +44,8 @@ or a capability.** Product-facing guides: `docs/guides/haskell.md`, `docs/guides
|
|
|
41
44
|
first write, rolls back on a failed write) and records one history entry per file sharing `languageGroupId`; `undoFix` and
|
|
42
45
|
`posture/fix-history.js` `undoLast`/`revertEntryById` restore a group as a unit. A winner outside the root or not a single
|
|
43
46
|
literal is refused with an `override` suggestion (`overrideSuggestion`), never an automatic edit. Any new consumer of a fix
|
|
44
|
-
plan (LSP, MCP, CLI) must honour `plan.edits
|
|
47
|
+
plan (LSP, MCP, CLI) must honour `plan.edits`. MCP `apply_fix` does so through `plan_digest` (the server recomputes the plan and
|
|
48
|
+
compares a digest; see `mcp/CLAUDE.md`), using the lifecycle's optional `preWrite` hook, which runs after every gate and before the first write.
|
|
45
49
|
- **Boundaries are narrowed only by sound, closed rules.** `haskell-cpp.js` decides a CPP conditional only from the file, a project-stated
|
|
46
50
|
compiler (`with-compiler` pin; `tested-with` only when every item is an exact version and all agree, recorded as an assumption) or the hull
|
|
47
51
|
of cabal `build-depends` bounds; an unknown macro is undecided, never 0, and an undecided conditional keeps every branch. `haskell-boundaries.js`
|
|
@@ -57,6 +61,7 @@ or a capability.** Product-facing guides: `docs/guides/haskell.md`, `docs/guides
|
|
|
57
61
|
will use; metadata comes only from the operator (`AGENTIC_SECURITY_NIX_META`). NVD is rate limited (5/30 s keyless): never raise
|
|
58
62
|
the request caps or lower the spacing in `nix-advisory-feed.js` without re-reading the NVD limits.
|
|
59
63
|
- **Pragma** (`pragma.js`): comment-aware, line-scoped, exact rule-id match, logged. A line-less finding cannot be suppressed.
|
|
64
|
+
- **Scale: no per-use scan of the project.** Pandoc and cabal-install (hundreds to thousands of modules) did not finish a scan because of four superlinear costs, now fixed; do not reintroduce the shapes. (1) `haskell-ir.js`: anything a lowering step needs "from every module" (a constructor by name, the record types, a definition by qid, a name's resolution) is an index on `Project` / a per-module memo (`findCon`, `recordTypes`, `defByQid`, `RESOLVE_MEMO`), built once, never a loop over `proj.modules` per use. Only a top-level resolution (depth 0) is memoised: a resolution inside a re-export chain depends on the path taken. (2) `aibom.js` looks a file's imports up in a map, not `Object.values(perFile).find`. (3) `sast/pipeline.js`: the workflow-wide `env:` secret rule is a one-pass matcher (`scanEnvSecrets`), because the single regular expression it replaced backtracked catastrophically on a comment-blanked workflow (blanking leaves runs of blank lines). A detector expression that runs on comment-blanked text must not let `\s*`, `\n` and a lazy line loop claim the same blank lines. (4) `dataflow/engine.js` `exprTaint`: a call's argument taint is computed once per visit (`_argTaint`); computing it twice re-walked the subtree and cost 2^N for N nested calls. Guards: `test/pipeline-env-secret.test.js`, `test/dataflow-expr-taint-scaling.test.js`, `test/haskell/haskell-ir-scaling.test.js`, `test/mark-used-vuln-functions.test.js` compare work counters between two sizes, never a clock.
|
|
60
65
|
|
|
61
66
|
## Module map
|
|
62
67
|
|
package/src/language/aibom.js
CHANGED
|
@@ -80,7 +80,10 @@ export function extractHaskellAI(files) {
|
|
|
80
80
|
const declared = new Map(comps.filter((c) => HS_AI_PACKAGES[c.name]).map((c) => [c.name, c]));
|
|
81
81
|
let ir = null;
|
|
82
82
|
try { ir = buildHaskellIR(Object.fromEntries(sources)); } catch (e) { gaps.push({ kind: 'haskell-ir-failed', detail: e.message }); }
|
|
83
|
-
|
|
83
|
+
// file -> its imports, first entry wins; built once (a per-call scan of every file made this quadratic in project size)
|
|
84
|
+
const importsByFile = new Map();
|
|
85
|
+
if (ir && ir.perFile) for (const x of Object.values(ir.perFile)) if (!importsByFile.has(x.file)) importsByFile.set(x.file, x.imports || []);
|
|
86
|
+
const importsOf = (f) => importsByFile.get(f) || [];
|
|
84
87
|
|
|
85
88
|
for (const [file, text] of sources) {
|
|
86
89
|
const toks = tokenizeHaskell(text);
|
|
@@ -191,6 +191,15 @@ export async function runFixLifecycle(o) {
|
|
|
191
191
|
const res = { status: 'verified', applied: false, plan, gates, label: plan.label, verified, preview: edits.map((e) => unifiedDiff(e.file, e.before, e.after)).join('\n'), files: edits.map((e) => e.file) };
|
|
192
192
|
if (o.apply) {
|
|
193
193
|
if (!o.root) return blocked('apply requires a root directory');
|
|
194
|
+
// Optional last check, after every gate has passed and before the first byte is written, so a caller that bound this plan
|
|
195
|
+
// to something outside the lifecycle (a digest, a confinement policy) refuses atomically with the write it guards. A hook
|
|
196
|
+
// that throws or answers ok:false blocks the apply; nothing has been written at this point.
|
|
197
|
+
if (o.preWrite) {
|
|
198
|
+
let pre;
|
|
199
|
+
try { pre = o.preWrite(plan, edits); } catch (err) { pre = { ok: false, detail: `pre-write check failed: ${err.message}` }; }
|
|
200
|
+
gates.preWrite = pre || { ok: false, detail: 'no pre-write result' };
|
|
201
|
+
if (gates.preWrite.ok !== true) return blocked(`pre-write: ${gates.preWrite.detail || 'refused'} (no file was written)`);
|
|
202
|
+
}
|
|
194
203
|
const base = { scanRoot: o.root, findingId: finding.id || `${finding.file}:${finding.line}`, ruleId: finding.rule || null, vuln: finding.vuln || null, stableId: finding.stableId || null, fixLabel: plan.label || null, verification: verified, findingProvenance: finding.findingProvenance || null };
|
|
195
204
|
if (edits.length === 1) {
|
|
196
205
|
res.backup = writeWithBackup(o.root, plan.file, plan.before, plan.after);
|