@clear-capabilities/agentic-security-scanner 0.147.5 → 0.148.1
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 +144 -0
- package/bin/agentic-security.js +47 -15
- package/dist/agentic-security.mjs +2 -2
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/compliance-frameworks/nist-800-171-r3.json +1269 -0
- package/package.json +3 -3
- package/src/engine.js +37 -4
- package/src/posture/auditor-walkthrough.js +89 -6
- package/src/posture/compliance-frameworks/nist-800-171-r3.json +1269 -0
- package/src/posture/threat-model.js +20 -3
- package/src/sast/python-sinks.js +24 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clear-capabilities/agentic-security-scanner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.148.1",
|
|
4
4
|
"description": "Scanner engine for the agentic-security Claude Code plugin — SAST, SCA (function-level reachability + CISA KEV), secrets, IaC, prompt-injection, MCP/agent-tool audit, auth/authZ deep analysis, attack chains, PoC generation, business logic, toxic-combinations scoring, SBOM, pipeline integrity, compliance attestation, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -64,10 +64,10 @@
|
|
|
64
64
|
"test:smoke": "node --test test/smoke.test.js test/demo-app.test.js",
|
|
65
65
|
"test:glob": "node --test test/glob-compat.test.js",
|
|
66
66
|
"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/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/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/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",
|
|
67
|
-
"test:posture": "node --test 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/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/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/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/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",
|
|
67
|
+
"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/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/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/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/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",
|
|
68
68
|
"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-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",
|
|
69
69
|
"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",
|
|
70
|
-
"test:report": "node --test 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",
|
|
70
|
+
"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",
|
|
71
71
|
"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",
|
|
72
72
|
"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",
|
|
73
73
|
"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",
|
package/src/engine.js
CHANGED
|
@@ -1862,7 +1862,22 @@ function scanRoutes(fp,raw){const cleaned=stripNoise(raw,fp);const lines=raw.spl
|
|
|
1862
1862
|
|
|
1863
1863
|
const LOGIC_PATTERNS=[
|
|
1864
1864
|
{regex:/Math\.random\s*\(\s*\)/g,vuln:"Weak Randomness",severity:"medium",cwe:"CWE-330",stride:"Spoofing",fix:"Use crypto.randomBytes or crypto.randomUUID for security-sensitive values.",code:"// BEFORE\nconst token = Math.random().toString(36);\n\n// AFTER\nconst token = crypto.randomBytes(32).toString('hex');"},
|
|
1865
|
-
|
|
1865
|
+
// Real false positive (customer report): `[^'"]` matches newlines, so the
|
|
1866
|
+
// "value" capture could pair the CLOSING quote of an unrelated string with
|
|
1867
|
+
// some OTHER quote found anywhere later in the file, fabricating a fake
|
|
1868
|
+
// "secret" spanning arbitrary source text in between. Confirmed
|
|
1869
|
+
// reproduction: `HUGGINGFACE_TOKEN = getpass.getpass("Enter your Hugging
|
|
1870
|
+
// Face token: ")` — the trigger word "token" occurs inside the human-
|
|
1871
|
+
// readable PROMPT text, immediately followed by ": " (satisfying the
|
|
1872
|
+
// `[:=]` requirement) and then that string's own closing quote (accepted
|
|
1873
|
+
// as the regex's opening quote); `[^'"]{3,}` then consumed everything up
|
|
1874
|
+
// to the next unrelated quote anywhere below, including subsequent
|
|
1875
|
+
// function definitions. getpass() collects a credential interactively at
|
|
1876
|
+
// runtime — the only literal in the source is the prompt, not a secret.
|
|
1877
|
+
// Excluding `\n` from the captured-value class confines a match to a
|
|
1878
|
+
// single physical line, which is what "hardcoded" is supposed to mean:
|
|
1879
|
+
// a real inline literal, not two coincidental quote characters far apart.
|
|
1880
|
+
{regex:/(?:password|secret|api_?key|token|auth)\s*[:=]\s*['"][^'"\n]{3,}['"]/gi,vuln:"Hardcoded Secret",severity:"critical",cwe:"CWE-798",stride:"Information Disclosure",kind:"secret",fix:"Use environment variables or a secrets manager.",code:"// BEFORE\nconst apiKey = 'sk-abc123';\n\n// AFTER\nconst apiKey = process.env.API_KEY;"},
|
|
1866
1881
|
{regex:/===?\s*['"](?:admin|root|password|123456|test|default)['"]/gi,vuln:"Hardcoded Credential Check",severity:"high",cwe:"CWE-798",stride:"Spoofing",kind:"secret",fix:"Use hashed password verification, never hardcoded strings.",code:"// BEFORE\nif (password === 'admin') grant();\n\n// AFTER\nconst valid = await bcrypt.compare(password, user.hashedPassword);"},
|
|
1867
1882
|
{regex:/if\s*\(\s*(?:fs\.existsSync|fs\.access|stat)\s*\([^)]+\)\s*\)[^{]*(?:readFile|writeFile|unlink|rename)/g,vuln:"Race Condition (TOCTOU)",severity:"medium",cwe:"CWE-367",stride:"Tampering",fix:"Use atomic operations instead of check-then-act patterns.",code:"// BEFORE\nif (fs.existsSync(p)) fs.unlinkSync(p);\n\n// AFTER\ntry { fs.unlinkSync(p); } catch(e) { if(e.code!=='ENOENT') throw e; }"},
|
|
1868
1883
|
{regex:/\.(?:isAdmin|isRole|role)\s*(?:===?\s*(?:true|['"]admin['"])|\)\s*\{)/g,vuln:"Inline Privilege Check",severity:"medium",cwe:"CWE-863",stride:"Elevation of Privilege",fix:"Use middleware-based RBAC instead of inline role checks.",code:"// BEFORE\nif (user.isAdmin) deleteAll();\n\n// AFTER\nrouter.delete('/all', requireRole('admin'), handler);"},
|
|
@@ -1889,7 +1904,19 @@ const LOGIC_PATTERNS=[
|
|
|
1889
1904
|
// ── Missing Bounds on Financial/Quantity Fields ──────────────────────────────
|
|
1890
1905
|
{regex:/(?:req|request|ctx)\s*(?:\.\s*body|\[\s*['"]body['"]\s*\])\s*[.[\s]*(?:quantity|amount|price|units|count|qty)\b(?![^;]{0,200}(?:Number\.isInteger|isNaN|Math\.abs|>=\s*1|>\s*0|>0|>=1|max\s*:))/g,vuln:"Missing Positive-Integer Validation on Financial Field",severity:"medium",cwe:"CWE-20",stride:"Tampering",fix:"Validate that financial/quantity fields are positive integers before processing. Negative values can create credit or reverse transactions.",code:"// BEFORE\nawait Order.create({ quantity: req.body.quantity, price: product.price });\n\n// AFTER\nconst qty = req.body.quantity;\nif (!Number.isInteger(qty) || qty < 1 || qty > 10000)\n return res.status(400).json({ error: 'quantity must be 1-10000' });\nawait Order.create({ quantity: qty, price: product.price });"},
|
|
1891
1906
|
// ── #22: Missing timeout on outbound HTTP requests (DoS) ─────────────────────
|
|
1892
|
-
|
|
1907
|
+
// Real false positive (customer report): this rule names JS/Node APIs
|
|
1908
|
+
// (fetch/axios/http.get) but carried no langScope, so it ran on every
|
|
1909
|
+
// language and matched the literal text "fetch(" wherever it occurred —
|
|
1910
|
+
// including a Python method DEFINITION (`def fetch(self, ...)`) and its
|
|
1911
|
+
// interface declaration and every call site, none of which perform an
|
|
1912
|
+
// outbound HTTP request in that language at all. The remediation shown was
|
|
1913
|
+
// also unconditionally JS (fetch/axios/node http), which made no sense on
|
|
1914
|
+
// a Python finding. Two fixes: scope to the languages these API names
|
|
1915
|
+
// actually mean something in, and exclude a `function fetch(...)`
|
|
1916
|
+
// declaration from matching a call-shaped rule — the same declaration-vs-
|
|
1917
|
+
// call confusion that made the Python case fire, reproduced once more
|
|
1918
|
+
// inside JS/TS itself (a local polyfill/wrapper named `fetch`).
|
|
1919
|
+
{regex:/(?<!\bfunction\s)(?:await\s+)?\b(?:fetch|axios\.(?:get|post|put|patch|delete|request)|http\.(?:get|request)|https\.(?:get|request)|got)\s*\(/gi,vuln:"Missing Timeout on Outbound HTTP Request (DoS)",severity:"medium",cwe:"CWE-400",stride:"Denial of Service",appliesTo:["server"],langScope:/\.(?:js|jsx|ts|tsx|mjs|cjs)$/i,fix:"Set a timeout on all outbound requests to prevent event-loop starvation from stalled upstreams.",code:"// fetch (Node 18+)\nconst resp = await fetch(url, { signal: AbortSignal.timeout(5000) });\n\n// axios\nawait axios.get(url, { timeout: 5000 });\n\n// node http\nconst req = http.get(url, cb);\nreq.setTimeout(5000, () => req.destroy());"},
|
|
1893
1920
|
// ── #24: ORM collection queries without pagination limit (DoS) ───────────────
|
|
1894
1921
|
{regex:/\.\s*(?:findAll|findMany|findAndCountAll)\s*\(\s*\{[^}]{0,500}\}/g,vuln:"ORM Collection Query Without Pagination Limit (DoS)",severity:"medium",cwe:"CWE-400",stride:"Denial of Service",appliesTo:["server"],fix:"Always set limit/take on collection queries to bound memory and DB load.",code:"const items = await Model.findAll({\n where: { userId: req.user.id },\n limit: Math.min(Number(req.query.limit) || 50, 100),\n offset: Number(req.query.offset) || 0,\n});"},
|
|
1895
1922
|
// ── #27: Missing audit log on sensitive mutations (Repudiation) ──────────────
|
|
@@ -10153,7 +10180,13 @@ function _deterministicFileTimings(timings) {
|
|
|
10153
10180
|
await _runAnnotator("_v3.calibrationDrift", () => { _v3.calibrationDrift = computeCalibrationDrift(scanRoot); });
|
|
10154
10181
|
// v3 next-gen: why-fired provenance is captured LAST so it reflects the
|
|
10155
10182
|
// final state of each finding after every other annotator has run.
|
|
10156
|
-
|
|
10183
|
+
// Real customer-reported inconsistency: this call passed a hardcoded `{}`
|
|
10184
|
+
// context, so `ctx.rulesetVersion` inside why-fired.js was always
|
|
10185
|
+
// undefined and every finding's `whyFired.scanner.rulesetVersion` read
|
|
10186
|
+
// `null` — even though the top-level attestation, computed from the same
|
|
10187
|
+
// `_effectiveRulesetVersion(scanRoot)` this file already calls at two
|
|
10188
|
+
// other sites, correctly named the real ruleset version. Thread it through.
|
|
10189
|
+
await _runAnnotator("annotateWhyFired", () => { annotateWhyFired(finalFindings, { rulesetVersion: (_effectiveRulesetVersion(scanRoot) || {}).version || null }); });
|
|
10157
10190
|
// SCA-SAST correlation: link SAST findings to SCA vulnerable packages
|
|
10158
10191
|
try{for(const f of finalFindings){if(!f.chain||!f.chain.length)continue;const src=f.chain[0]?.label||'';for(const sc of supplyChain){if(sc.type!=='vulnerable_dep')continue;if(src.includes(sc.name)||f.vuln?.toLowerCase().includes(sc.name)){f.scaCorrelation={osvId:sc.osvId,package:sc.name,version:sc.version,confirmed:true};sc.sastConfirmed=true;break;}}}}catch(_){}
|
|
10159
10192
|
// Multi-sink chain detection: group findings by source variable
|
|
@@ -10691,7 +10724,7 @@ function _deterministicFileTimings(timings) {
|
|
|
10691
10724
|
compliance: _complianceReport ? { stale: _complianceReport.summary?.stale || 0 } : null,
|
|
10692
10725
|
});
|
|
10693
10726
|
} // end if (!skipAnnotators) — FR-PROV-029
|
|
10694
|
-
return{entrypointInventory:_entrypointInventory,rootCauseSweep:_rootCauseSweep,proofCoverage:_proofCoverage,kevCatalog:kevCatalogMeta(),routes:dd(aR,r=>`${r.method}:${r.path}:${r.file}:${r.line}`),findings:finalFindings,sources:aSrc,sinks:aSink,sanitizers:aSan,filesScanned:files.length,crossFileCount:cf.length,logicVulns:aLogic,supplyChain,components:annotatedComponents,secrets:aSecrets,ciphers:{atRest:aCiphersRest,inTransit:aCiphersTransit},pfr,fc,suppressions:_getSuppressions(),_v3,_scanMeta,_engineErrors:{cppDataflowParseErrors:_cppDataflowParseErrors.value},annotatorErrors:_annotatorErrors,detectorErrors:_detectorErrors,executionProof:_executionProofSummary,logicClaims:_logicClaims,vulnHistory:_vulnHistory,threatModel:_threatModel,privacyFramework:_privacyFramework,privacyIrBacked:_privacyIrBacked,privacyTaxonomyVersion:_privacyTaxonomyVersion,sbomDiff:_sbomDiff,complianceReport:_complianceReport,exploitBundles:_exploitBundles,pqcPlan:_pqcPlan,licenseGraph:_licenseGraph,attributions:_attributions,attackTaxonomy:_taxonomySummary,scanHealth:_scanHealth,coverageLedger:_coverageLedger,lineageGraph:_lineageGraph,lineageStatus:_lineageStatus};}
|
|
10727
|
+
return{entrypointInventory:_entrypointInventory,rootCauseSweep:_rootCauseSweep,proofCoverage:_proofCoverage,kevCatalog:kevCatalogMeta(),routes:dd(aR,r=>`${r.method}:${r.path}:${r.file}:${r.line}`),findings:finalFindings,sources:aSrc,sinks:aSink,sanitizers:aSan,filesScanned:files.length,linesScanned:Object.values(fc).reduce((_n,_c)=>_n+(typeof _c==='string'?_c.split("\n").length:0),0),crossFileCount:cf.length,logicVulns:aLogic,supplyChain,components:annotatedComponents,secrets:aSecrets,ciphers:{atRest:aCiphersRest,inTransit:aCiphersTransit},pfr,fc,suppressions:_getSuppressions(),_v3,_scanMeta,_engineErrors:{cppDataflowParseErrors:_cppDataflowParseErrors.value},annotatorErrors:_annotatorErrors,detectorErrors:_detectorErrors,executionProof:_executionProofSummary,logicClaims:_logicClaims,vulnHistory:_vulnHistory,threatModel:_threatModel,privacyFramework:_privacyFramework,privacyIrBacked:_privacyIrBacked,privacyTaxonomyVersion:_privacyTaxonomyVersion,sbomDiff:_sbomDiff,complianceReport:_complianceReport,exploitBundles:_exploitBundles,pqcPlan:_pqcPlan,licenseGraph:_licenseGraph,attributions:_attributions,attackTaxonomy:_taxonomySummary,scanHealth:_scanHealth,coverageLedger:_coverageLedger,lineageGraph:_lineageGraph,lineageStatus:_lineageStatus};}
|
|
10695
10728
|
|
|
10696
10729
|
// Post-aggregation classification: every source becomes "unsafe"|"safe"; every sink becomes "confirmed"|"safe".
|
|
10697
10730
|
// Orphans (no finding linkage) are bucketed by file-local heuristic so the UI shows binary states only.
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
// nist-ai-600-1 NIST AI Risk Management Framework, GenAI profile
|
|
11
11
|
// nist-privacy-1-1 NIST Privacy Framework 1.1 (see posture/privacy-framework.js
|
|
12
12
|
// for the assessment + remediation layer over it)
|
|
13
|
+
// nist-800-171-r3 NIST SP 800-171 Rev. 3 — Protecting CUI in Nonfederal
|
|
14
|
+
// Systems. FULL 97-requirement catalogue; 43 of those are
|
|
15
|
+
// organisational/physical and carry no automated signal at
|
|
16
|
+
// all, deliberately carried so the negative space is visible.
|
|
17
|
+
// Control text is generated from the CSV export by
|
|
18
|
+
// scripts/nist-800-171/build-catalog.py; the codeTestable
|
|
19
|
+
// rating is OURS, not NIST's (that export rates nothing for
|
|
20
|
+
// testability) and lives in nist-800-171/code-testability.json.
|
|
13
21
|
// owasp-asvs-5 OWASP Application Security Verification Standard 5.0
|
|
14
22
|
// owasp-llm-top-10 OWASP Top 10 for LLM Applications 2025
|
|
15
23
|
// eu-ai-act EU AI Act (Regulation 2024/1689)
|
|
@@ -345,6 +353,60 @@ export function evaluateFramework(scanRoot, fw, scan) {
|
|
|
345
353
|
families.get(k).push(f);
|
|
346
354
|
}
|
|
347
355
|
|
|
356
|
+
// VACUOUS SATISFACTION GUARD (shared-evaluator half).
|
|
357
|
+
//
|
|
358
|
+
// The `family:` branch below clears a mapping when no findings of that family
|
|
359
|
+
// are open — which is ALSO true of a scan that examined nothing at all. So
|
|
360
|
+
// pointing `--report <framework>` at an empty directory, a wrong path, or a
|
|
361
|
+
// scan that failed to read anything reported mapped controls as fully
|
|
362
|
+
// evidenced, on the strength of having looked at zero files.
|
|
363
|
+
//
|
|
364
|
+
// privacy-framework.js's `bucketOf` has carried this guard for its own module
|
|
365
|
+
// since it was found by that module's test, and its comment already named
|
|
366
|
+
// `evaluateFramework` as the place the hazard originates — but the guard was
|
|
367
|
+
// never applied HERE, so every framework reached through `--report` /
|
|
368
|
+
// `--walkthrough` / `--gap` / `--format oscal` still had it. Found while
|
|
369
|
+
// adding NIST SP 800-171 Rev. 3, whose 50 mapped controls made a
|
|
370
|
+
// zero-file scan report 32 controls `present`.
|
|
371
|
+
//
|
|
372
|
+
// Reads THREE count fields, not the two privacy-framework.js reads. Measured,
|
|
373
|
+
// not assumed: a real scan of a clean one-file project persists
|
|
374
|
+
// `scanned.files: 1` and `_scanMeta.filesScanned: 1` but NO top-level
|
|
375
|
+
// `filesScanned` — that key exists only on the in-memory object engine.js
|
|
376
|
+
// returns. privacy-framework.js reads `filesScanned ?? _scanMeta.filesScanned`
|
|
377
|
+
// and gets away with it because engine.js hands it the live in-memory object;
|
|
378
|
+
// this evaluator is reached from the CLI with the PERSISTED artifact, where
|
|
379
|
+
// that first key is absent. Mirroring privacy's exact field list here would
|
|
380
|
+
// therefore have read 0 for every real scan and degraded a genuinely clean
|
|
381
|
+
// project to "not assessed" — the opposite failure, and worse, because it
|
|
382
|
+
// punishes the operator who did everything right.
|
|
383
|
+
//
|
|
384
|
+
// ABSENT IS UNKNOWN, NOT ZERO. Degrading requires POSITIVE evidence that
|
|
385
|
+
// nothing was examined: at least one count field actually present AND every
|
|
386
|
+
// present one reading 0. A scan object carrying no count fields at all
|
|
387
|
+
// (synthetic fixtures, older artifacts, third-party callers) is treated as
|
|
388
|
+
// assessable and behaves exactly as before. Same precedent as relevance.js —
|
|
389
|
+
// never assert the negative without evidence, and `unknown` is not `false`.
|
|
390
|
+
// The findings/components/... fallbacks then rescue the remaining case: an
|
|
391
|
+
// artifact that records 0 files but plainly holds real results.
|
|
392
|
+
//
|
|
393
|
+
// An unassessable scan does not remove or fail a control: the mapping is
|
|
394
|
+
// disclosed as unverifiable, which caps the control at 'partial' via the same
|
|
395
|
+
// path a `rule:` mapping already uses. Recall-preserving, same precedent as
|
|
396
|
+
// falsification.js / proof-gate.js.
|
|
397
|
+
const _counts = [
|
|
398
|
+
scan && scan.filesScanned,
|
|
399
|
+
scan && scan._scanMeta && scan._scanMeta.filesScanned,
|
|
400
|
+
scan && scan.scanned && scan.scanned.files,
|
|
401
|
+
].filter((v) => typeof v === 'number' && Number.isFinite(v));
|
|
402
|
+
const _examinedNothing = _counts.length > 0 && _counts.every((v) => v === 0);
|
|
403
|
+
const assessable = !_examinedNothing
|
|
404
|
+
|| findings.length > 0
|
|
405
|
+
|| components.length > 0
|
|
406
|
+
|| secrets.length > 0
|
|
407
|
+
|| logicVulns.length > 0
|
|
408
|
+
|| supplyChain.length > 0;
|
|
409
|
+
|
|
348
410
|
const results = [];
|
|
349
411
|
for (const c of fw.controls || []) {
|
|
350
412
|
const obs = [];
|
|
@@ -447,6 +509,11 @@ export function evaluateFramework(scanRoot, fw, scan) {
|
|
|
447
509
|
allCleared = false;
|
|
448
510
|
contributingFindings.push(...open);
|
|
449
511
|
obs.push(`${open.length} open ${fam} finding(s) at ${minSeverity}+.`);
|
|
512
|
+
} else if (!assessable) {
|
|
513
|
+
// Empty bucket, but the scan examined nothing — see the guard above.
|
|
514
|
+
// Not evidence, and deliberately not a failure either.
|
|
515
|
+
obs.push(`⚠ ${fam}: the scan examined no files, so an empty finding bucket is not evidence (not assessed).`);
|
|
516
|
+
hasUnverifiableMapping = true;
|
|
450
517
|
} else {
|
|
451
518
|
obs.push(`✓ ${fam}: no open ${minSeverity}+ findings.`);
|
|
452
519
|
anyCleared = true;
|
|
@@ -465,7 +532,15 @@ export function evaluateFramework(scanRoot, fw, scan) {
|
|
|
465
532
|
'aibom': 'aibom.json',
|
|
466
533
|
'attack-taxonomy': 'last-scan.json',
|
|
467
534
|
'why-fired': 'last-scan.json',
|
|
468
|
-
|
|
535
|
+
// Two real spellings, both live in this codebase: security-trend.js and
|
|
536
|
+
// router.js read `scan-history.json` (a FILE), findings-memory.js uses
|
|
537
|
+
// `scan-history` (a DIRECTORY). Only the directory was listed here, so
|
|
538
|
+
// on a normal scan — which writes the .json — every control mapped to
|
|
539
|
+
// module:scan-history reported the artifact missing and could never
|
|
540
|
+
// clear. Five bundled frameworks were affected. An array means "any of
|
|
541
|
+
// these satisfies it", which is the honest reading: the control asks
|
|
542
|
+
// whether a scan history exists, not which shape it took.
|
|
543
|
+
'scan-history': ['scan-history.json', 'scan-history/'],
|
|
469
544
|
'integrity': 'last-scan.json.sig',
|
|
470
545
|
'watch-mode': 'watch-status.json',
|
|
471
546
|
'cve-alert-daemon': 'cve-alerts/',
|
|
@@ -480,17 +555,25 @@ export function evaluateFramework(scanRoot, fw, scan) {
|
|
|
480
555
|
'security-fixer': '.../agents/security-fixer.md',
|
|
481
556
|
'mcp-tools': '.../scanner/src/mcp/tools.js',
|
|
482
557
|
};
|
|
558
|
+
// A table entry is either one path or an array of acceptable ones. An
|
|
559
|
+
// array means the artifact has more than one real spelling in this
|
|
560
|
+
// codebase and any of them evidences the control; the FIRST is the
|
|
561
|
+
// canonical name used in the observation text when none is found, so
|
|
562
|
+
// the message still names something a reader can go create.
|
|
483
563
|
const target = ARTIFACT[mod];
|
|
564
|
+
const candidates = target == null ? [] : (Array.isArray(target) ? target : [target]);
|
|
484
565
|
// A '.../' sentinel marks a source-relative artifact (project source,
|
|
485
566
|
// e.g. a hook or agent file) — resolve it against the scan root itself.
|
|
486
567
|
// Everything else is a runtime artifact under the STATE dir. Without
|
|
487
568
|
// this, `statePath(scanRoot, '.../x')` never resolves and the
|
|
488
569
|
// control falsely reads "not present" for every project.
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
: statePath(scanRoot,
|
|
492
|
-
const
|
|
493
|
-
|
|
570
|
+
const _resolve = (t) => t.startsWith('.../')
|
|
571
|
+
? path.join(scanRoot, t.slice(4))
|
|
572
|
+
: statePath(scanRoot, t);
|
|
573
|
+
const found = candidates.find((t) => { try { return fs.existsSync(_resolve(t)); } catch { return false; } });
|
|
574
|
+
const resolved = found ? _resolve(found) : null;
|
|
575
|
+
const label = (found || candidates[0] || '(unmapped)').replace(/^\.\.\.\//, '');
|
|
576
|
+
if (resolved) {
|
|
494
577
|
obs.push(`✓ ${mod}: ${label} present.`);
|
|
495
578
|
anySignal = true;
|
|
496
579
|
anyCleared = true;
|