@clear-capabilities/agentic-security-scanner 0.186.2 → 0.188.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/1602.index.js +275 -19
- package/dist/{4d06c8944ae121d7085c.json → 92f78fd3e2dbe30fb58d.json} +5 -5
- package/dist/9560.index.js +61 -4
- package/dist/agentic-security.mjs +2 -2
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +5 -5
- package/src/capabilities/decide.js +7 -1
- package/src/dataflow/CLAUDE.md +15 -0
- package/src/dataflow/catalog-haskell.js +7 -1
- package/src/dataflow/catalog.js +14 -0
- package/src/dataflow/engine.js +147 -10
- package/src/dataflow/privacy-catalog-haskell.js +2 -1
- package/src/engine.js +11 -3
- package/src/language/CLAUDE.md +26 -0
- package/src/language/compliance-map.js +6 -0
- package/src/language/context.js +20 -0
- package/src/language/haskell-comments.js +65 -0
- package/src/language/haskell-disclosure.js +16 -4
- package/src/language/haskell-findings.js +2 -1
- package/src/language/haskell-fix.js +118 -2
- package/src/language/haskell-guards.js +132 -5
- package/src/language/haskell-ir.js +228 -13
- package/src/language/haskell-models.js +108 -8
- package/src/language/haskell-security-rules.js +223 -41
- package/src/language/haskell-syntax.js +10 -1
- package/src/language/haskell-trust.js +268 -0
- package/src/language/haskell-web.js +46 -9
- package/src/language/support-registry.js +3 -2
- package/src/mcp/tools.js +54 -3
- package/src/posture/attack-taxonomy.js +10 -0
- package/src/posture/entrypoint-inventory.js +6 -2
- package/src/posture/proof-fabric/generate-haskell.js +266 -17
- package/src/posture/proof-fabric/generate.js +10 -3
- package/src/posture/proof-fabric/oracles.js +21 -0
- package/src/posture/stable-id.js +2 -1
- package/src/report/index.js +3 -0
- package/src/sast/cloud-iam.js +3 -1
- package/dist/2066.index.js +0 -851
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
f70bb09f8b0030636bb4ba27a72de9beb7087bcedadb2d27127bad3cfb960f01 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.188.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",
|
|
@@ -86,16 +86,16 @@
|
|
|
86
86
|
"test:ollama-e2e": "AGENTIC_SECURITY_OLLAMA_E2E=1 node --test test/ollama-e2e.test.js",
|
|
87
87
|
"test:glob": "node --test test/glob-compat.test.js",
|
|
88
88
|
"test:sast": "node --test test/dag-precision.test.js test/cpp-hardcoded-secret.test.js test/sibling-guard.test.js test/comment-blindness.test.js test/crypto-specialist.test.js test/llm.test.js test/llm-cost-advisor.test.js test/llm-owasp.test.js test/logic.test.js test/authz.test.js test/model-load.test.js test/prompt-template.test.js test/business-logic.test.js test/python-sinks.test.js test/phase1-detectors.test.js test/phase2-detectors.test.js test/phase3-v3.test.js test/phase7-extensions.test.js test/phase8-extensions.test.js test/new-cwe-detectors.test.js test/file-upload.test.js test/llmsecops-detectors.test.js test/db-taint.test.js test/dart-swift.test.js test/zip-slip-containment-guard.test.js test/zip-slip-go.test.js test/redos-anchoring.test.js test/path-guard-validator-call.test.js test/redos-nfa.test.js test/weak-randomness.test.js test/csharp-pipeline.test.js test/csharp-cross-file.test.js test/post-quantum-crypto.test.js test/web3-advanced.test.js test/cloud-iam-k8s.test.js test/crypto-protocol.test.js test/ml-supply-chain.test.js test/mcp-audit-scan.test.js test/wrong-context-sanitizer.test.js test/sanitizer-context.test.js test/frontend-hygiene.test.js test/csv-injection.test.js test/stored-taint.test.js test/tree-sitter-sinks.test.js test/kotlin-structural.test.js test/ruby-php-structural.test.js test/java-csharp-structural.test.js test/java-structural-cross-file.test.js test/guard-recognition.test.js test/guard-window-identifier-correlation.test.js test/js-python-framework-structural.test.js test/go-structural.test.js test/secret-concat.test.js test/secret-redaction.test.js test/xss-reflected-multilang.test.js test/code-injection-multilang.test.js test/xxe-multilang.test.js test/xpath-injection-multilang.test.js test/gapfill-batch13.test.js test/agent-untrusted-flow.test.js test/api-authz.test.js test/event-entrypoint.test.js test/iac-terraform.test.js test/cross-service.test.js test/rbac-consistency.test.js test/adversarial-robustness.test.js test/secret-entropy.test.js test/comment-strip.test.js test/juliet-path-filter-gate.test.js test/java-bench-shape-opt-in.test.js test/java-ast-folding.test.js test/java-bench-extras.test.js test/reachability-missing-callgraph-data.test.js test/rate-limit.test.js test/auth-signals.test.js test/convention-deviation.test.js test/resource-exhaustion.test.js test/resource-exhaustion-realworld.test.js test/redirect-toctou.test.js test/redirect-toctou-realworld.test.js test/codegen-sink.test.js test/ownership-authz.test.js test/ownership-authz-realworld.test.js test/t21-precision-audit.test.js test/secrets-coverage.test.js test/prompt-injection-payloads.test.js test/iac-cloud-templates.test.js test/ruby-path-join.test.js test/sast/manifest-line-tracking.test.js test/rust-sast.test.js test/java-deserialization-precision.test.js test/pipeline-env-secret.test.js test/mark-used-vuln-functions.test.js",
|
|
89
|
-
"test:posture": "node --test test/contracts/root-cause-sweep.contract.test.js test/contracts/model-validation.contract.test.js test/contracts/agentic-discovery.contract.test.js test/contracts/local-models.contract.test.js test/contracts/dependency-finding.contract.test.js test/contracts/taint-flow.contract.test.js test/contracts/runtime-corroboration.contract.test.js test/compliance-coverage.test.js test/contracts/blast-radius.contract.test.js test/contracts/federation.contract.test.js test/contracts/eval-lab-report.contract.test.js test/contracts/fleet.contract.test.js test/contracts/rule-compiler.contract.test.js test/contracts/release-assurance.contract.test.js test/contracts/dnr.contract.test.js test/contracts/strategic-metrics.contract.test.js test/contracts/exploit-proof.contract.test.js test/contracts/artifact-provenance.contract.test.js test/dnr/artifact-join.test.js test/rule-pack-signing.test.js test/threat-model.test.js test/git-hardening.test.js test/privacy-framework.test.js test/material-change.test.js test/drift.test.js test/scorecard.test.js test/accuracy-scorecard.test.js test/scorecard-gate.test.js test/predeploy-gate.test.js test/release-check.test.js test/pre-push-gate.test.js test/dependency-currency.test.js test/package-contents-check.test.js test/gate-verdict-cache.test.js test/run-unit-tests.test.js test/temp-leak-guard.test.js test/release-workflow.test.js test/release-workflow-structure.test.js test/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-bundle.test.js test/obligation-evidence-pack.test.js test/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-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 test/load-factor.test.js test/fleet/job-store.test.js test/fleet/enterprise-graph.test.js test/fleet/discover-edges.test.js test/fleet/code-boundaries.test.js test/fleet/job-runner.test.js test/fleet/fleet-org-cli.test.js test/eval-lab/dataset-neutralize.test.js test/eval-lab/arms-score-stats.test.js test/eval-lab/run-report.test.js test/eval-lab/router.test.js test/eval-lab/arm-ollama.test.js test/eval-lab/import-independent.test.js test/cli/eval-lab-cli.test.js test/dnr/ir-and-outputs.test.js test/dnr/logs-and-hunt.test.js test/dnr/window-store-view.test.js test/dnr/exploit-correlation.test.js test/cli/dnr-cli.test.js test/proof-fabric/core.test.js test/proof-fabric/container.test.js test/proof-fabric/generate.test.js test/proof-fabric/siblings.test.js test/proof-fabric/generate-python.test.js test/proof-fabric/generate-haskell.test.js test/cli/proof-lifecycle-demo.test.js test/mcp-proof.test.js test/proof-fabric/fix-store-runner.test.js test/proof-fabric/fixtures.test.js test/cli/proof-fabric-cli.test.js test/rule-compiler/core.test.js test/rule-compiler/synthesis.test.js test/rule-compiler/structure.test.js test/cli/rule-compiler-cli.test.js test/fleet/fleet-graph.test.js test/fleet/fleet-manifest.test.js test/fleet/fleet-cli.test.js test/artifact/safe-tar.test.js test/artifact/image-archive.test.js test/artifact/filesystem.test.js test/artifact/layer-secrets.test.js test/artifact/inventory.test.js test/artifact/vuln-correlate.test.js test/artifact/signature.test.js test/artifact/provenance-graph.test.js test/artifact/mismatch.test.js test/artifact/vex.test.js test/artifact/registry.test.js test/artifact/sources-cosign-evidence.test.js test/cli/artifact-sources-cli.test.js test/cli/artifact-provenance-cli.test.js test/witness-bundle.test.js",
|
|
90
|
-
"test:dataflow": "node --test test/privacy-catalog.test.js test/privacy-deep-walker.test.js test/privacy-deep-e2e.test.js test/container-taint.test.js test/stored-taint-families.test.js test/py-annotation-sources.test.js test/fn-reach.test.js test/deep-taint.test.js test/calibration.test.js test/holdout-eval.test.js test/cross-lang-meta.test.js test/cross-lang-queues.test.js test/phase5-xlang.test.js test/phase5-coverage.test.js test/phase6-taint.test.js test/llm-validator-consistency.test.js test/llm-validator-default-on.test.js test/llm-validator-preset.test.js test/parser-py-cst.test.js test/chrome-probe.test.js test/parser-cs-kt.test.js test/parser-cs-annotations.test.js test/parser-cs-control-flow.test.js test/parser-kt-control-flow.test.js test/parser-js-annotations.test.js test/parser-java-annotations.test.js test/parser-java-control-flow.test.js test/parser-java-calls.test.js test/parser-go.test.js test/parser-rust.test.js test/rust-taint-flow.test.js test/parser-php-rb.test.js test/parser-php-control-flow.test.js test/interproc-k2.test.js test/proven-clean.test.js test/backward-default.test.js test/incremental-cache.test.js test/string-regex-lattice.test.js test/closure-capture.test.js test/points-to.test.js test/type-stubs.test.js test/soft-taint.test.js test/ifds.test.js test/symbolic-exec-proof.test.js test/ifds-summary-edges.test.js test/stub-aware-filter.test.js test/cross-repo.test.js test/proof-gate.test.js test/layer-recall.test.js test/java-taint-flow.test.js test/kt-taint-flow.test.js test/sanitizer-typed-flow.test.js test/sanitizer-gate-unit.test.js test/proof-safe.test.js test/collection-taint.test.js test/kcfa-context.test.js test/kcfa-callstring.test.js test/flow-parity.test.js test/callgraph-resolve.test.js test/import-reachability.test.js test/ir-stats.test.js test/parser-cpp.test.js test/parser-js-decorators.test.js test/parser-js-if-else-cfg.test.js test/cpp-integration.test.js test/engine-reconnect.test.js test/phase2-scoping.test.js test/engine-recall.test.js test/catalog-annotation-source.test.js test/catalog-cs-p1.test.js test/catalog-expanded.test.js test/builtin-summaries.test.js test/interproc-mutation-crash.test.js test/mutated-param-taint.test.js test/return-sink-check.test.js test/higher-order-inline-callback.test.js test/interproc-findings-discard.test.js test/runscan-deep-option.test.js test/summary-cache-convergence.test.js test/parser-rb-calls.test.js test/parser-rb-module-level.test.js test/balanced-call.test.js test/destructuring-taint.test.js test/computed-member-taint.test.js test/string-interpolation-taint.test.js test/dataflow-deadcode-severity.test.js test/catalog-dotted-callee-lookup.test.js test/builtin-summary-family-blind-kill.test.js test/deep-mode-annotator-pipeline.test.js test/receiver-type-and-nested-calls.test.js test/member-write-and-loop-taint.test.js test/parser-py-module-level.test.js test/parser-php-module-level.test.js test/r14b-module-level-e2e.test.js test/annotation-taint-engine.test.js test/interproc-nested-call-taint.test.js test/catalog-xpath-injection.test.js test/catalog-ldap-injection.test.js test/catalog-xxe.test.js test/catalog-response-splitting.test.js test/catalog-code-injection.test.js test/parser-rb-control-flow.test.js test/catalog-ruby-p1.test.js test/callee-receiver-taint.test.js test/catalog-command-injection-p4.test.js test/catalog-xss-p4.test.js test/catalog-path-ssrf-p3.test.js test/catalog-ai-model-provider-precision.test.js test/catalog-orm-write.test.js test/dataflow/provenance-pipeline-integration.test.js test/dataflow-progress.test.js test/scan-progress.test.js test/sard-leakage-pipeline.test.js test/sard-split-scoring.test.js test/sard-llm-isolation.test.js test/sard-mutation-adversarial.test.js test/sard-baseline-adaptive-tolerance.test.js test/sard-independent-holdout.test.js test/sard-flow-aware-scoring.test.js test/sard-macro-score-support-floor.test.js test/sard-php-split.test.js test/parser-java-nary-concat.test.js test/sard-naive-baseline.test.js test/field-taint-cross-method.test.js test/field-taint-engine-unit.test.js test/coercion-sanitizer-kill.test.js test/constant-propagation-path-sensitivity.test.js test/parser-java-assignments.test.js test/catalog-java-sard.test.js test/parser-cs-juliet-shapes.test.js test/catalog-cs-sard-p2.test.js test/sard-cwe-key-merge.test.js test/parser-php-backtick.test.js test/parser-php-include.test.js test/php-include-merge.test.js test/parser-php-array-field-taint.test.js test/parser-php-cond-assign.test.js test/parser-rust-redos.test.js test/multi-sink-taint-chain.test.js test/static-field-cross-class-call-string.test.js test/regex-guard-narrowing.test.js test/object-instance-field-taint.test.js test/param-type-inference.test.js test/smt-path-feasibility.test.js test/cross-service-taint.test.js test/dataflow-expr-taint-scaling.test.js test/evaluation/guard-dominance.test.js test/evaluation/engine-mechanisms.test.js test/rx-exec-receiver.test.js test/incremental-dependency.test.js test/incremental-integrity.test.js test/demand-query.test.js test/ir-cache.test.js test/canonicalize-parity.test.js",
|
|
89
|
+
"test:posture": "node --test test/contracts/root-cause-sweep.contract.test.js test/contracts/model-validation.contract.test.js test/contracts/agentic-discovery.contract.test.js test/contracts/local-models.contract.test.js test/contracts/dependency-finding.contract.test.js test/contracts/taint-flow.contract.test.js test/contracts/runtime-corroboration.contract.test.js test/compliance-coverage.test.js test/contracts/blast-radius.contract.test.js test/contracts/federation.contract.test.js test/contracts/eval-lab-report.contract.test.js test/contracts/fleet.contract.test.js test/contracts/rule-compiler.contract.test.js test/contracts/release-assurance.contract.test.js test/contracts/dnr.contract.test.js test/contracts/strategic-metrics.contract.test.js test/contracts/exploit-proof.contract.test.js test/contracts/artifact-provenance.contract.test.js test/dnr/artifact-join.test.js test/rule-pack-signing.test.js test/threat-model.test.js test/git-hardening.test.js test/privacy-framework.test.js test/material-change.test.js test/drift.test.js test/scorecard.test.js test/accuracy-scorecard.test.js test/scorecard-gate.test.js test/predeploy-gate.test.js test/release-check.test.js test/pre-push-gate.test.js test/dependency-currency.test.js test/package-contents-check.test.js test/gate-verdict-cache.test.js test/run-unit-tests.test.js test/temp-leak-guard.test.js test/release-workflow.test.js test/release-workflow-structure.test.js test/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-bundle.test.js test/obligation-evidence-pack.test.js test/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-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 test/load-factor.test.js test/fleet/job-store.test.js test/fleet/enterprise-graph.test.js test/fleet/discover-edges.test.js test/fleet/code-boundaries.test.js test/fleet/job-runner.test.js test/fleet/fleet-org-cli.test.js test/eval-lab/dataset-neutralize.test.js test/eval-lab/arms-score-stats.test.js test/eval-lab/run-report.test.js test/eval-lab/router.test.js test/eval-lab/arm-ollama.test.js test/eval-lab/import-independent.test.js test/cli/eval-lab-cli.test.js test/dnr/ir-and-outputs.test.js test/dnr/logs-and-hunt.test.js test/dnr/window-store-view.test.js test/dnr/exploit-correlation.test.js test/cli/dnr-cli.test.js test/proof-fabric/core.test.js test/proof-fabric/container.test.js test/proof-fabric/generate.test.js test/proof-fabric/siblings.test.js test/proof-fabric/generate-python.test.js test/proof-fabric/generate-haskell.test.js test/proof-fabric/generate-haskell-widened.test.js test/cli/proof-lifecycle-demo.test.js test/mcp-proof.test.js test/proof-fabric/fix-store-runner.test.js test/proof-fabric/fixtures.test.js test/cli/proof-fabric-cli.test.js test/rule-compiler/core.test.js test/rule-compiler/synthesis.test.js test/rule-compiler/structure.test.js test/cli/rule-compiler-cli.test.js test/fleet/fleet-graph.test.js test/fleet/fleet-manifest.test.js test/fleet/fleet-cli.test.js test/artifact/safe-tar.test.js test/artifact/image-archive.test.js test/artifact/filesystem.test.js test/artifact/layer-secrets.test.js test/artifact/inventory.test.js test/artifact/vuln-correlate.test.js test/artifact/signature.test.js test/artifact/provenance-graph.test.js test/artifact/mismatch.test.js test/artifact/vex.test.js test/artifact/registry.test.js test/artifact/sources-cosign-evidence.test.js test/cli/artifact-sources-cli.test.js test/cli/artifact-provenance-cli.test.js test/witness-bundle.test.js",
|
|
90
|
+
"test:dataflow": "node --test test/privacy-catalog.test.js test/privacy-deep-walker.test.js test/privacy-deep-e2e.test.js test/container-taint.test.js test/stored-taint-families.test.js test/py-annotation-sources.test.js test/fn-reach.test.js test/deep-taint.test.js test/calibration.test.js test/holdout-eval.test.js test/cross-lang-meta.test.js test/cross-lang-queues.test.js test/phase5-xlang.test.js test/phase5-coverage.test.js test/phase6-taint.test.js test/llm-validator-consistency.test.js test/llm-validator-default-on.test.js test/llm-validator-preset.test.js test/parser-py-cst.test.js test/chrome-probe.test.js test/parser-cs-kt.test.js test/parser-cs-annotations.test.js test/parser-cs-control-flow.test.js test/parser-kt-control-flow.test.js test/parser-js-annotations.test.js test/parser-java-annotations.test.js test/parser-java-control-flow.test.js test/parser-java-calls.test.js test/parser-go.test.js test/parser-rust.test.js test/rust-taint-flow.test.js test/parser-php-rb.test.js test/parser-php-control-flow.test.js test/interproc-k2.test.js test/proven-clean.test.js test/backward-default.test.js test/incremental-cache.test.js test/string-regex-lattice.test.js test/closure-capture.test.js test/points-to.test.js test/type-stubs.test.js test/soft-taint.test.js test/ifds.test.js test/symbolic-exec-proof.test.js test/ifds-summary-edges.test.js test/stub-aware-filter.test.js test/cross-repo.test.js test/proof-gate.test.js test/layer-recall.test.js test/java-taint-flow.test.js test/kt-taint-flow.test.js test/sanitizer-typed-flow.test.js test/sanitizer-gate-unit.test.js test/proof-safe.test.js test/collection-taint.test.js test/kcfa-context.test.js test/kcfa-callstring.test.js test/flow-parity.test.js test/callgraph-resolve.test.js test/import-reachability.test.js test/ir-stats.test.js test/parser-cpp.test.js test/parser-js-decorators.test.js test/parser-js-if-else-cfg.test.js test/cpp-integration.test.js test/engine-reconnect.test.js test/phase2-scoping.test.js test/engine-recall.test.js test/catalog-annotation-source.test.js test/catalog-cs-p1.test.js test/catalog-expanded.test.js test/builtin-summaries.test.js test/interproc-mutation-crash.test.js test/mutated-param-taint.test.js test/return-sink-check.test.js test/higher-order-inline-callback.test.js test/interproc-findings-discard.test.js test/runscan-deep-option.test.js test/summary-cache-convergence.test.js test/parser-rb-calls.test.js test/parser-rb-module-level.test.js test/balanced-call.test.js test/destructuring-taint.test.js test/computed-member-taint.test.js test/string-interpolation-taint.test.js test/dataflow-deadcode-severity.test.js test/catalog-dotted-callee-lookup.test.js test/builtin-summary-family-blind-kill.test.js test/deep-mode-annotator-pipeline.test.js test/receiver-type-and-nested-calls.test.js test/member-write-and-loop-taint.test.js test/parser-py-module-level.test.js test/parser-php-module-level.test.js test/r14b-module-level-e2e.test.js test/annotation-taint-engine.test.js test/interproc-nested-call-taint.test.js test/catalog-xpath-injection.test.js test/catalog-ldap-injection.test.js test/catalog-xxe.test.js test/catalog-response-splitting.test.js test/catalog-code-injection.test.js test/parser-rb-control-flow.test.js test/catalog-ruby-p1.test.js test/callee-receiver-taint.test.js test/catalog-command-injection-p4.test.js test/catalog-xss-p4.test.js test/catalog-path-ssrf-p3.test.js test/catalog-ai-model-provider-precision.test.js test/catalog-orm-write.test.js test/dataflow/provenance-pipeline-integration.test.js test/dataflow-progress.test.js test/scan-progress.test.js test/sard-leakage-pipeline.test.js test/sard-split-scoring.test.js test/sard-llm-isolation.test.js test/sard-mutation-adversarial.test.js test/sard-baseline-adaptive-tolerance.test.js test/sard-independent-holdout.test.js test/sard-flow-aware-scoring.test.js test/sard-macro-score-support-floor.test.js test/sard-php-split.test.js test/parser-java-nary-concat.test.js test/sard-naive-baseline.test.js test/field-taint-cross-method.test.js test/field-taint-engine-unit.test.js test/coercion-sanitizer-kill.test.js test/constant-propagation-path-sensitivity.test.js test/parser-java-assignments.test.js test/catalog-java-sard.test.js test/parser-cs-juliet-shapes.test.js test/catalog-cs-sard-p2.test.js test/sard-cwe-key-merge.test.js test/parser-php-backtick.test.js test/parser-php-include.test.js test/php-include-merge.test.js test/parser-php-array-field-taint.test.js test/parser-php-cond-assign.test.js test/parser-rust-redos.test.js test/multi-sink-taint-chain.test.js test/static-field-cross-class-call-string.test.js test/regex-guard-narrowing.test.js test/object-instance-field-taint.test.js test/param-type-inference.test.js test/smt-path-feasibility.test.js test/cross-service-taint.test.js test/dataflow-expr-taint-scaling.test.js test/evaluation/guard-dominance.test.js test/evaluation/engine-mechanisms.test.js test/rx-exec-receiver.test.js test/incremental-dependency.test.js test/incremental-integrity.test.js test/demand-query.test.js test/ir-cache.test.js test/canonicalize-parity.test.js test/dataflow-source-attribution.test.js",
|
|
91
91
|
"test:mcp": "node --test test/mcp.test.js test/mcp-apply-fix-multifile.test.js test/mcp-protocol-smoke.test.js test/mcp-audit.test.js test/audit-cli.test.js test/mcp-scratchpad.test.js test/mcp-offload.test.js test/sca-upgrade.test.js test/lsp-server.test.js test/mcp-dataflow-tools.test.js test/cli/governance-propose-edit.test.js test/cli/remediation-open-update.test.js test/cli/remediation-verify-reopen.test.js test/cli/dataflow-observations.test.js test/cli/federate-declare-list.test.js test/capabilities/tools.test.js",
|
|
92
92
|
"test:report": "node --test test/report-scan-invariants.test.js test/junit.test.js test/ci.test.js test/poc-generator.test.js test/verifier.test.js test/verifier-target.test.js test/annotator-errors.test.js test/git-history-batch.test.js test/default-scan-drift.test.js test/bundle-optional-deps.test.js test/scan-health.test.js test/finding-schema.test.js test/pipeline/finding-schema-provenance.test.js test/evidence-provenance.test.js test/report/provenance-output.test.js test/report/provenance-format-parity.test.js test/legacy-compat.test.js test/annotator-runner.test.js test/detector-runner.test.js test/detector-errors.test.js test/analyzer-supervisor.test.js test/cascade-worker-pool.test.js test/cascade-pool-wiring.test.js test/coverage-ledger.test.js test/coverage-ledger-completeness.test.js test/assurance-mode.test.js test/artifact-registry.test.js test/artifact-registry-completeness.test.js test/retention-policy.test.js test/state-lifecycle-report.test.js test/legal-hold.test.js test/production-feedback.test.js test/calibration-feedback.test.js test/encryption-provider.test.js test/grader-calibration.test.js test/pr-delta-gate.test.js test/vex.test.js test/report-render.test.js test/oscal-conformance.test.js test/cli-fix-apply.test.js test/apply-fix-service.test.js test/high-impact-approval-gate.test.js test/approver-registry.test.js test/suppression-exceptions.test.js test/fix-history.test.js test/producer-registry.test.js test/producer-collector.test.js test/enrichment-completion.test.js",
|
|
93
93
|
"test:lineage": "node --test test/lineage/analysis-completion.test.js test/lineage/schema.test.js test/lineage/ids.test.js test/lineage/protection.test.js test/lineage/classification.test.js test/lineage/validate.test.js test/lineage/json-schema-parity.test.js test/lineage/flagship-fixture.test.js test/lineage/flagship-fixture-semantics.test.js test/lineage/field-identity.test.js test/lineage/engine-expr-resolver.test.js test/lineage/engine-walker.test.js test/lineage/engine-integration.test.js test/lineage/engine-receiver-identity.test.js test/lineage/summaries.test.js test/lineage/driver.test.js test/lineage/engine-provenance.test.js test/lineage/engine-provenance-interprocedural.test.js test/lineage/path-store.test.js test/lineage/path-query.test.js test/lineage/flow-grade.test.js test/lineage/source-registry.test.js test/lineage/sink-registry.test.js test/lineage/transform-catalog.test.js test/lineage/handling-analyzer.test.js test/lineage/registry-real-code.test.js test/lineage/source-seeding.test.js test/lineage/graph-builder.test.js test/lineage/coverage.test.js test/lineage/resolve-destination.test.js test/lineage/queue-detail.test.js test/lineage/index.test.js test/lineage/obligation-mapping.test.js test/lineage/obligation-predicates.test.js test/lineage/obligation-predicates-walkthrough.test.js test/lineage/transit-protection.test.js test/lineage/at-rest-protection.test.js test/lineage/protection-summary.test.js test/lineage/ac01-multi-sink.test.js test/lineage/policy-verdict.test.js test/lineage/governance-refs.test.js test/lineage/export-privacy.test.js test/lineage/export-json.test.js test/lineage/export-csv.test.js test/lineage/decision-story.test.js test/lineage/export-briefing.test.js test/lineage/graph-snapshot.test.js test/lineage/graph-diff.test.js test/lineage/drift-policy.test.js test/lineage/recipient-profile.test.js test/lineage/recipient-registry.test.js test/lineage/recipient-wiring.test.js test/lineage/redact-graph.test.js test/lineage-scan-wiring.test.js test/lineage-artifact-write.test.js test/bench-data-lineage-runner.test.js test/bundle-frontend.test.js test/bundle-frontend-golden.test.js test/generate-html-report.test.js test/export-image.test.js test/lineage/scenario.test.js test/lineage/scenario-engine.test.js test/lineage/scenario-diff.test.js test/lineage/scenario-no-obligation-wiring.test.js test/lineage/impact-assessment.test.js test/lineage/impact-engine.test.js test/lineage/impact-no-obligation-wiring.test.js test/lineage/governance-edit.test.js test/lineage/remediation.test.js test/lineage/runtime-observation.test.js test/lineage/observation-correlation.test.js test/lineage/observation-adapters.test.js test/lineage/observation-store.test.js test/lineage/runtime-corroboration-wiring.test.js test/lineage/cross-repo-link.test.js test/lineage/federation-loader.test.js test/lineage/cross-repo-link-wiring.test.js test/lineage/runtime-assurance.test.js test/lineage/runtime-edge-states.test.js test/lineage/runtime-explorer-view.test.js test/cli/runtime-explorer-cli.test.js test/cli/runtime-identity-cli.test.js test/cli/runtime-twin-cli.test.js test/lineage/cut-planner.test.js test/lineage/scc.test.js test/lineage/scc-solver.test.js test/graph/graph-views.test.js test/lineage/dag07-completion.test.js test/lineage/owned-build.test.js test/dataflow/boundary-propagation.test.js test/lineage/boundary-contract.test.js test/lineage/correlation-v2.test.js test/lineage/path-control-evidence.test.js test/lineage/smt-z3-backend.test.js test/cli/dag-commands.test.js",
|
|
94
94
|
"test:bench-modules": "node --test test/phase4-harness.test.js test/pipeline.test.js test/proof-corpus-lib.test.js test/proof-corpus-runner.test.js",
|
|
95
95
|
"test:server": "node --test test/server/graph-loader.test.js test/server/security.test.js test/server/routes.test.js test/server/http-server.test.js test/server/cmd-explore.test.js test/server/cmd-dataflow-export.test.js test/server/static-assets.test.js test/server/dataflow-scenario-cli.test.js test/server/dataflow-impact-cli.test.js",
|
|
96
|
-
"test:haskell": "node --test test/haskell/haskell-weak-hash-names.test.js test/haskell/haskell-models-extended.test.js test/haskell/haskell-rules-extended.test.js test/haskell/haskell-injection.test.js test/haskell/haskell-ir.test.js test/haskell/haskell-manifests.test.js test/haskell/haskell-parser.test.js test/haskell/haskell-remediation.test.js test/haskell/haskell-resolved-graph.test.js test/haskell/haskell-sca.test.js test/haskell/haskell-sca-severity.test.js test/haskell/haskell-web-guards.test.js test/haskell/haskell-advisory-feed.test.js test/haskell/haskell-security-rules.test.js test/haskell/haskell-taint.test.js test/haskell/haskell-boundaries.test.js test/haskell/haskell-live-feed-scan.test.js test/haskell/haskell-supply-bounds.test.js test/haskell/haskell-ir-scaling.test.js test/haskell/haskell-boundary-propagation.test.js",
|
|
96
|
+
"test:haskell": "node --test test/haskell/haskell-alias-taint.test.js test/haskell/haskell-weak-hash-names.test.js test/haskell/haskell-models-extended.test.js test/haskell/haskell-rules-extended.test.js test/haskell/haskell-injection.test.js test/haskell/haskell-ir.test.js test/haskell/haskell-manifests.test.js test/haskell/haskell-parser.test.js test/haskell/haskell-remediation.test.js test/haskell/haskell-resolved-graph.test.js test/haskell/haskell-sca.test.js test/haskell/haskell-sca-severity.test.js test/haskell/haskell-web-guards.test.js test/haskell/haskell-advisory-feed.test.js test/haskell/haskell-security-rules.test.js test/haskell/haskell-taint.test.js test/haskell/haskell-boundaries.test.js test/haskell/haskell-live-feed-scan.test.js test/haskell/haskell-supply-bounds.test.js test/haskell/haskell-ir-scaling.test.js test/haskell/haskell-boundary-propagation.test.js test/haskell/haskell-generalization.test.js test/haskell/haskell-trust-boundary.test.js test/haskell/haskell-comment-invariance.test.js test/haskell/haskell-remediation-classes.test.js test/haskell/haskell-trust-boundary-frameworks.test.js test/haskell/haskell-generic-detectors.test.js",
|
|
97
97
|
"test:nix": "node --test test/nix/nix-option-types.test.js test/nix/nixos-hardening-daemon.test.js test/nix/nixos-hardening-extended.test.js 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 test/nix/nix-boundary-scope.test.js",
|
|
98
|
-
"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",
|
|
98
|
+
"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-parity-haskell.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",
|
|
99
99
|
"test:language-stress": "node --test --test-concurrency=1 test/language/language-stress-offline.test.js",
|
|
100
100
|
"test:language-tools": "node --test test/haskell/haskell-web-auth.test.js test/haskell/haskell-support-gate.test.js",
|
|
101
101
|
"bench:language-support:check": "node ../bench/language-support/check.mjs",
|
|
@@ -42,8 +42,14 @@ const INTERPRETER_NAMES = new Set([
|
|
|
42
42
|
'time', 'timeout', 'sudo', 'doas', 'su', 'ssh', 'osascript', 'launchctl', 'script', 'expect', 'tclsh', 'wish',
|
|
43
43
|
'awk', 'gawk', 'mawk', 'nawk', 'java', 'jshell', 'deno', 'bun', 'nodejs', 'swift', 'make', 'gmake',
|
|
44
44
|
'npm', 'npx', 'yarn', 'pnpm', 'pip', 'pip3', 'pipx', 'gem', 'bundle', 'cargo', 'go', 'gradle', 'mvn', 'watch',
|
|
45
|
+
// Haskell and Nix toolchains run code supplied by the project: GHC and cabal execute Template Haskell splices and a
|
|
46
|
+
// custom Setup.hs at build time, runghc/ghci interpret a source file, and the Nix evaluators run the expressions they
|
|
47
|
+
// are given (builders run the derivation's build script).
|
|
48
|
+
'ghc', 'ghci', 'runghc', 'runhaskell', 'cabal', 'stack',
|
|
49
|
+
'nix', 'nix-build', 'nix-shell', 'nix-instantiate', 'nix-env', 'nix-store',
|
|
45
50
|
]);
|
|
46
|
-
|
|
51
|
+
// Versioned toolchain binaries (`ghc-9.14.1`, `runghc-9.14.1`, `cabal-3.14`) are the same executables under another name.
|
|
52
|
+
const INTERPRETER_FAMILY = /^(?:python|pypy|ruby|perl|php|lua|luajit|node|tclsh)[0-9.]*$|^(?:ghc|ghci|runghc|runhaskell|cabal|stack)-[0-9][0-9.]*$/;
|
|
47
53
|
|
|
48
54
|
/**
|
|
49
55
|
* What kind of executable this is. `interpreter` is true for a known shell or
|
package/src/dataflow/CLAUDE.md
CHANGED
|
@@ -38,6 +38,19 @@ Layer-2 taint engine. Walks the Layer-1 IR (`../ir/`) with field-sensitive forwa
|
|
|
38
38
|
- **Flat-string callees — the one that mattered most.** The mutator rule tested `callee.kind === 'member'`, a shape ONLY `parser-js.js` (Babel) emits. The seven hand-rolled parsers emit a flat dot-joined string (`"items.append"`), so container writes in Python/Ruby/PHP/Go/Java/C#/Kotlin never matched at all. Same frontend duality `_calleeReceiverTainted` documents. This was invisible from the outside because a PY-SAST pattern rule caught the same sink, so the taint-layer miss looked like a detection.
|
|
39
39
|
- **Measured honestly:** `bench:layer-recall` is UNCHANGED at 116/215 after this landed — the corpus holds no container-shaped entries, which is exactly why the taint-recall-to-80% push closed at 83.9% (115/137 of the dataflow-shaped subset) "without Tier 5". This is capability proven by targeted tests (`test/container-taint.test.js`, including two precision cases that must NOT fire), not a measured recall gain. Do not quote it as one. Self-scan held at 427 with zero new findings across ~700 files.
|
|
40
40
|
|
|
41
|
+
## Source attribution (evidence chain origin)
|
|
42
|
+
|
|
43
|
+
A finding's `source`/`chain` come from `_taintSources`: records keyed by the variable a source call was assigned to. Before this was
|
|
44
|
+
extended, a value DERIVED from a source (`b = 'ls ' + a`), a value that crossed a call, or one returned from a function reached the sink with an
|
|
45
|
+
empty chain, so nothing downstream could tell a request read from a command-line argument. Now: `_inheritSources` copies the records reaching
|
|
46
|
+
a derived value; a summary computed under an entry-taint state names its tainted parameters with placeholder records (`_paramPlaceholders`,
|
|
47
|
+
`isParam`) that `_mergeSummaryFindings` resolves, at the call site that merges the finding, to the sources of the actual arguments
|
|
48
|
+
(`_resolveParams`); a function's return keeps the sources behind it (`returnSources`, followed through `from` into the finding's
|
|
49
|
+
`sourceRoots`). A parameter annotation that is a catalog source is recorded too (`matchAnnotationParamSources`, `annotation: true`, mapped to
|
|
50
|
+
`source.kind: 'parameter'`). The summary cache is keyed by entry state only, so the cached summary never carries a caller's sources: they
|
|
51
|
+
are added to a COPY at each merge. Pinned in `test/dataflow-source-attribution.test.js`. The multi-sink aggregation in `engine.js` ignores
|
|
52
|
+
`info` findings (a finding lowered by a guard or a trust verdict is not a flow worth aggregating).
|
|
53
|
+
|
|
41
54
|
## Scope — what we still do NOT model (today)
|
|
42
55
|
|
|
43
56
|
- **Call-string (k>1) context-sensitivity.** Context is the *value* abstraction — which params are tainted at entry — not the call stack. Two call paths that reach a helper with the same tainted-arg shape share a summary. Entry-state granularity is also param-level, not arbitrary access paths (`f(obj)` with `obj.a` tainted ≡ `obj.b` tainted).
|
|
@@ -182,3 +195,5 @@ member-based: `hs-src-caller-controlled-param` (a text parameter of an exported
|
|
|
182
195
|
`hs-lin-src-record-parameter` (the lineage form of a record parameter). `engine.js` skips the literal-skeleton filter for Haskell
|
|
183
196
|
catalog entries and requires a markup literal for `htmlSkeleton` sinks. Pinned counts in the lineage registry tests change
|
|
184
197
|
deliberately when the model registry grows. Details: `scanner/src/language/CLAUDE.md`.
|
|
198
|
+
|
|
199
|
+
Three Haskell-scoped behaviours live in `engine.js` (all guarded by `_currentFile` being a `.hs`/`.lhs` file, so no other language changes): a `union` (the branches of an `if`, a `case`, guards) handed to another call as an argument (`get "/p" $ do .. case m of A -> sink x`) or bound as a statement is a sink site in EACH branch (`_nestedSinkFindings`; the `assign` case); `exprTaint` of a `member` whose object is a call result (the elements of `words s`) is the object's taint; and the catalog gains four annotation sources, `hs-src-handler-param-<framework>` (a text parameter of an exported function that returns a Scotty/Yesod/Servant/WAI handler value: request input, provenance `url-param`), beside the weaker `hs-src-caller-controlled-param`.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// callee the Haskell IR produces (`System.Process.callCommand`), never a bare name, so a user's own
|
|
4
4
|
// function with the same name resolves to its own module and cannot match.
|
|
5
5
|
|
|
6
|
-
import { HS_SOURCES, HS_SINKS, HS_SANITIZERS } from '../language/haskell-models.js';
|
|
6
|
+
import { HS_SOURCES, HS_SINKS, HS_SANITIZERS, HS_HANDLER_PARAM_SOURCES } from '../language/haskell-models.js';
|
|
7
7
|
|
|
8
8
|
const idOf = (kind, e, extra = '') => `hs-${kind}-${e.module}.${e.name}${extra}`.replace(/[^A-Za-z0-9.\-_]/g, '-').toLowerCase();
|
|
9
9
|
|
|
@@ -30,6 +30,12 @@ export const HASKELL_CATALOG = Object.freeze([
|
|
|
30
30
|
// LINEAGE form of a record parameter of an exported function (see lineage/haskell-view.js)
|
|
31
31
|
{ kind: 'source', id: 'hs-lin-src-record-parameter', language: 'hs', framework: 'haskell', match: { type: 'member', object: '$hs', prop: 'hs:record-parameter' }, label: 'record parameter of an exported function (caller-supplied customer data)' },
|
|
32
32
|
{ kind: 'source', id: 'hs-src-caller-controlled-param', language: 'hs', framework: 'haskell', match: { type: 'annotation', name: 'hs:caller-controlled' }, label: 'text parameter of an exported function (caller-controlled)' },
|
|
33
|
+
// A text parameter of an exported function that returns a framework handler value: request input the framework passed in (see
|
|
34
|
+
// language/haskell-models.js `HS_HANDLER_PARAM_SOURCES`). A plain library export keeps the weaker caller-controlled source above.
|
|
35
|
+
...Object.values(HS_HANDLER_PARAM_SOURCES).map((d) => ({
|
|
36
|
+
kind: 'source', id: `hs-src-handler-param-${d.framework}`, language: 'hs', framework: d.framework,
|
|
37
|
+
match: { type: 'annotation', name: d.decorator }, label: d.label, provenance: d.provenance,
|
|
38
|
+
})),
|
|
33
39
|
...HS_SANITIZERS.map((e) => ({
|
|
34
40
|
kind: 'sanitizer', id: idOf('san', e), language: 'hs',
|
|
35
41
|
match: { type: 'call', callee: `${e.module}.${e.name}` }, effect: 'strip', appliesTo: e.appliesTo, note: e.note,
|
package/src/dataflow/catalog.js
CHANGED
|
@@ -2743,5 +2743,19 @@ export function matchAnnotationParams(paramAnnotations, file) {
|
|
|
2743
2743
|
return tainted;
|
|
2744
2744
|
}
|
|
2745
2745
|
|
|
2746
|
+
// The same lookup as matchAnnotationParams, keeping what each matched source says about itself so the engine can record an origin for the
|
|
2747
|
+
// parameter (an evidence chain that begins at "parameter x of f, request input" instead of at the sink).
|
|
2748
|
+
export function matchAnnotationParamSources(paramAnnotations, file) {
|
|
2749
|
+
const out = [];
|
|
2750
|
+
if (!paramAnnotations || !paramAnnotations.length) return out;
|
|
2751
|
+
for (const pa of paramAnnotations) {
|
|
2752
|
+
const raw = ANNOTATION_INDEX.get(pa.decorator);
|
|
2753
|
+
if (!raw) continue;
|
|
2754
|
+
const hit = filterByProvenance(raw).filter(h => _languageAllowed(h, file)).find(h => h.kind === 'source');
|
|
2755
|
+
if (hit) out.push({ name: pa.name, id: hit.id, label: hit.label || pa.decorator, provenance: hit.provenance || null });
|
|
2756
|
+
}
|
|
2757
|
+
return out;
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2746
2760
|
// For tests and reflection.
|
|
2747
2761
|
export function _catalogSize() { return CATALOG.length; }
|
package/src/dataflow/engine.js
CHANGED
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
// sanitizer must never silently drop a real vulnerability, so the walk never
|
|
43
43
|
// treats a sanitizer call as clearing the tainted path on its own.
|
|
44
44
|
|
|
45
|
-
import { matchSource, matchSinkOrSanitizer, matchMemberWriteSink, matchAnnotationParams } from './catalog.js';
|
|
45
|
+
import { matchSource, matchSinkOrSanitizer, matchMemberWriteSink, matchAnnotationParams, matchAnnotationParamSources } from './catalog.js';
|
|
46
46
|
import { functionRecord } from '../ir/callgraph.js';
|
|
47
47
|
import { accessPathOf, isCoveredBy, hasTaintedDescendant, addPath, removePathAndDescendants, joinSets as joinAccessSets, setsEqual as accessSetsEqual, pathIsCoveredByPrefix } from './access-paths.js';
|
|
48
48
|
import { aliasesForVar } from './points-to.js';
|
|
@@ -394,7 +394,7 @@ function _nestedCallReturnTainted(calleeExpr, argExprs, state, callContext, argT
|
|
|
394
394
|
if (!sum && fn && fn.cfg) {
|
|
395
395
|
sum = callContext._summaryCache.compute(qid, entry, () => {
|
|
396
396
|
const inner = {
|
|
397
|
-
_findings: [], _taintSources:
|
|
397
|
+
_findings: [], _taintSources: _paramPlaceholders(fn, entry), _returnTainted: false,
|
|
398
398
|
_stack: new Set(), deadlineMs: callContext.deadlineMs,
|
|
399
399
|
_summaryCache: callContext._summaryCache,
|
|
400
400
|
_callGraph: callContext._callGraph,
|
|
@@ -404,6 +404,7 @@ function _nestedCallReturnTainted(calleeExpr, argExprs, state, callContext, argT
|
|
|
404
404
|
try { analyzeFunction(fn, _unionAnnotationTaint(fn, entry), inner); } catch {}
|
|
405
405
|
return {
|
|
406
406
|
returnTainted: !!inner._returnTainted,
|
|
407
|
+
returnSources: inner._returnSources || [],
|
|
407
408
|
mutatedParams: inner._mutatedParamsOut || new Set(),
|
|
408
409
|
mutatedThisFields: inner._mutatedThisFieldsOut || new Set(),
|
|
409
410
|
taintedGlobals: new Set(),
|
|
@@ -411,7 +412,7 @@ function _nestedCallReturnTainted(calleeExpr, argExprs, state, callContext, argT
|
|
|
411
412
|
};
|
|
412
413
|
});
|
|
413
414
|
}
|
|
414
|
-
_mergeSummaryFindings(callContext, callContext._currentFnQid, sum, 'interproc');
|
|
415
|
+
_mergeSummaryFindings(callContext, callContext._currentFnQid, sum, 'interproc', { args: argExprs || [], state });
|
|
415
416
|
// Next-gen taint capability #3: a single-field getter's return is tainted
|
|
416
417
|
// whenever THIS call's own receiver already carries that field as
|
|
417
418
|
// tainted, independent of `sum.returnTainted` (which reflects only the
|
|
@@ -628,6 +629,9 @@ function exprTaint(expr, state, callContext) {
|
|
|
628
629
|
return argsTainted || nestedTainted || receiverTainted;
|
|
629
630
|
}
|
|
630
631
|
case 'unknown': return false;
|
|
632
|
+
// Haskell: the element of a collection that is itself a call result (`words s`'s elements, `lines body`'s) is a `member` whose object is a call,
|
|
633
|
+
// which is not an access path. An element of a tainted collection is tainted; reading a field of a tainted value is the same fact.
|
|
634
|
+
case 'member': return /\.(?:hs|lhs)$/i.test(_currentFile || '') ? exprTaint(expr.object, state, callContext) : false;
|
|
631
635
|
default: return false;
|
|
632
636
|
}
|
|
633
637
|
}
|
|
@@ -1430,6 +1434,15 @@ const _sinkLine = (call, fallback) => (/\.(?:hs|lhs)$/i.test(_currentFile || '')
|
|
|
1430
1434
|
function _nestedSinkFindings(argExprs, state, callContext, line) {
|
|
1431
1435
|
const findings = [];
|
|
1432
1436
|
for (const arg of (argExprs || [])) {
|
|
1437
|
+
// Haskell: a conditional or `case` handed to a combinator (`get "/p" $ do ... case m of A -> sink x; B -> next`) is a union of the branch
|
|
1438
|
+
// ACTIONS, each of which runs when its branch is taken: every branch call is a sink site, as at the top of a return (see the `return` case).
|
|
1439
|
+
if (arg && arg.kind === 'union' && /\.(?:hs|lhs)$/i.test(_currentFile || '')) {
|
|
1440
|
+
const leaves = [];
|
|
1441
|
+
const walk = (v, d) => { if (!v || d > 40) return; if (v.kind === 'union') (v.branches || []).forEach((b) => walk(b, d + 1)); else if (v.kind === 'call') leaves.push(v); };
|
|
1442
|
+
walk(arg, 0);
|
|
1443
|
+
findings.push(..._nestedSinkFindings(leaves, state, callContext, line));
|
|
1444
|
+
continue;
|
|
1445
|
+
}
|
|
1433
1446
|
if (!arg || arg.kind !== 'call') continue;
|
|
1434
1447
|
const { cat, argTaints } = _matchCallCatalog(arg.callee, arg.args, state, callContext);
|
|
1435
1448
|
const at = _sinkLine(arg, line);
|
|
@@ -1490,14 +1503,100 @@ function _memberWriteSinkFindings(hits, sourceExpr, state, callContext, line, ta
|
|
|
1490
1503
|
// list: a chain of 20 one-line wrappers produced 131072 copies of one finding, and the explosion ended in the whole result being dropped. The
|
|
1491
1504
|
// same sink reached again by the same finding is one finding, so a copy is skipped when this context already holds it.
|
|
1492
1505
|
function _findingKey(f) { return `${f.file ?? ''}|${f.line ?? ''}|${f.cwe ?? ''}|${f.vuln ?? ''}|${typeof f.callee === 'string' ? f.callee : ''}|${f.id ?? ''}`; }
|
|
1493
|
-
|
|
1506
|
+
// A variable that is TAINTED BY DERIVATION (`y = f(x)`, `(a : b) <- return x`) has no source of its own: the sources the engine recorded are
|
|
1507
|
+
// keyed by the variable a source call was assigned to. Without a record for `y` the evidence chain of a sink reached through `y` begins at the
|
|
1508
|
+
// sink, and nothing downstream can say where the data came from. The sources reaching the expression it was derived from are copied to it.
|
|
1509
|
+
// Bounded: at most 5 per assignment and 4000 records per analysis; the original record is untouched.
|
|
1510
|
+
// A callee is analysed once per ENTRY STATE (which parameters carry taint), not per caller, so inside that analysis a tainted parameter has no
|
|
1511
|
+
// origin of its own. Each tainted parameter gets a placeholder record (`isParam`); a finding or return that rests on it is resolved to the
|
|
1512
|
+
// sources of the arguments at the call site that merges it (`_resolveParams`), and a placeholder that no call site resolves is dropped.
|
|
1513
|
+
function _paramPlaceholders(fn, entry) {
|
|
1514
|
+
const out = [];
|
|
1515
|
+
if (!fn || !Array.isArray(fn.params) || !entry || !entry.size) return out;
|
|
1516
|
+
for (const p of fn.params) {
|
|
1517
|
+
if (typeof p !== 'string') continue;
|
|
1518
|
+
let hit = entry.has(p);
|
|
1519
|
+
if (!hit) for (const x of entry) if (typeof x === 'string' && x.startsWith(p + '.')) { hit = true; break; }
|
|
1520
|
+
if (hit) out.push({ varName: p, sourceId: `param:${fn.qid}:${p}`, sourceLabel: `parameter ${p}`, provenance: 'param', line: fn.line || 0, isParam: true });
|
|
1521
|
+
}
|
|
1522
|
+
return out;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
function _resolveParams(entries, replacement, depth = 0) {
|
|
1526
|
+
const out = [];
|
|
1527
|
+
const seen = new Set();
|
|
1528
|
+
const add = (e) => { const k = `${e.sourceId}|${e.line}`; if (!seen.has(k)) { seen.add(k); out.push(e); } };
|
|
1529
|
+
for (const e of entries || []) {
|
|
1530
|
+
if (!e) continue;
|
|
1531
|
+
if (e.isParam) { for (const r of replacement || []) add(r); continue; }
|
|
1532
|
+
if (Array.isArray(e.from) && e.from.length && depth < 8) add({ ...e, from: _resolveParams(e.from, replacement, depth + 1) });
|
|
1533
|
+
else add(e);
|
|
1534
|
+
}
|
|
1535
|
+
return out;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
function _inheritSources(callContext, target, exprs, state) {
|
|
1539
|
+
const list = callContext._taintSources;
|
|
1540
|
+
if (!target || !Array.isArray(list) || list.length === 0 || list.length > 4000) return;
|
|
1541
|
+
let n = 0;
|
|
1542
|
+
for (const ex of exprs) {
|
|
1543
|
+
for (const s of _sourcesReachingExpr(ex, state, list)) {
|
|
1544
|
+
if (n >= 5) return;
|
|
1545
|
+
if (s.varName === target) continue;
|
|
1546
|
+
if (list.some((x) => x.varName === target && x.sourceId === s.sourceId && x.line === s.line)) continue;
|
|
1547
|
+
list.push({ ...s, varName: target, derivedFrom: s.varName });
|
|
1548
|
+
n++;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
function _rootSources(trace, depth = 0, out = [], seen = new Set()) {
|
|
1554
|
+
for (const t of trace || []) {
|
|
1555
|
+
if (!t) continue;
|
|
1556
|
+
if (Array.isArray(t.from) && t.from.length && depth < 8) { _rootSources(t.from, depth + 1, out, seen); continue; }
|
|
1557
|
+
const k = `${t.sourceLabel}|${t.line}|${t.provenance}`;
|
|
1558
|
+
if (!seen.has(k)) { seen.add(k); out.push({ label: t.sourceLabel, provenance: t.provenance || null, line: t.line, file: t.file || null }); }
|
|
1559
|
+
}
|
|
1560
|
+
return out;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
function _mergeSummaryFindings(callContext, callerQid, sum, via, callSite) {
|
|
1494
1564
|
if (!sum || !Array.isArray(sum.findings) || !sum.findings.length) return;
|
|
1495
1565
|
if (!callContext._findingKeys) callContext._findingKeys = new Set(callContext._findings.map(_findingKey));
|
|
1566
|
+
// A callee's finding was computed under an entry-taint state (which parameters carry taint), not under any
|
|
1567
|
+
// particular caller, so the summary cannot name where that taint came from. THIS call site can: the sources the
|
|
1568
|
+
// caller's own analysis recorded for the arguments it passes. A finding with no source of its own inherits them
|
|
1569
|
+
// (a copy; the shared summary is untouched). Without this an evidence chain begins at the sink with no origin,
|
|
1570
|
+
// and nothing downstream can tell a request value from a command-line argument.
|
|
1571
|
+
let argSources = null;
|
|
1572
|
+
const inherited = () => {
|
|
1573
|
+
if (argSources) return argSources;
|
|
1574
|
+
argSources = [];
|
|
1575
|
+
if (callSite && Array.isArray(callSite.args)) {
|
|
1576
|
+
const seen = new Set();
|
|
1577
|
+
for (const a of callSite.args) {
|
|
1578
|
+
for (const src of _sourcesReachingExpr(a, callSite.state, callContext._taintSources)) {
|
|
1579
|
+
const k = `${src.sourceId}|${src.line}`;
|
|
1580
|
+
if (!seen.has(k)) { seen.add(k); argSources.push(src); }
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return argSources;
|
|
1585
|
+
};
|
|
1496
1586
|
for (const f of sum.findings) {
|
|
1497
1587
|
const key = _findingKey(f);
|
|
1498
1588
|
if (callContext._findingKeys.has(key)) continue;
|
|
1499
1589
|
callContext._findingKeys.add(key);
|
|
1500
|
-
|
|
1590
|
+
let out = { ...f, _funcQid: callerQid || null, _via: via };
|
|
1591
|
+
const tr = Array.isArray(f.trace) ? f.trace : [];
|
|
1592
|
+
if (tr.some((t) => t && (t.isParam || (t.from && t.from.length)))) {
|
|
1593
|
+
const resolved = _resolveParams(tr, callSite ? inherited() : []).slice(0, 5);
|
|
1594
|
+
out = { ...out, trace: resolved, sourceProvenance: resolved[0] ? (resolved[0].provenance || null) : null };
|
|
1595
|
+
} else if (callSite && tr.length === 0) {
|
|
1596
|
+
const inh = inherited();
|
|
1597
|
+
if (inh.length) out = { ...out, trace: inh.slice(0, 5), sourceProvenance: inh[0].provenance || null };
|
|
1598
|
+
}
|
|
1599
|
+
callContext._findings.push(out);
|
|
1501
1600
|
}
|
|
1502
1601
|
}
|
|
1503
1602
|
|
|
@@ -1541,6 +1640,10 @@ function step(node, stateIn, callContext) {
|
|
|
1541
1640
|
node.source.callee, node.source.args, _sinkCat, _sinkArgTaints,
|
|
1542
1641
|
state, callContext, node.line, node.source.kwargs).findings);
|
|
1543
1642
|
findings.push(..._nestedSinkFindings(node.source.args, state, callContext, node.line));
|
|
1643
|
+
} else if (node.source && node.source.kind === 'union' && /\.(?:hs|lhs)$/i.test(_currentFile || '')) {
|
|
1644
|
+
// Haskell: a conditional or `case` used as a STATEMENT (not the last one) is bound to a scratch name; every branch action in it runs when its
|
|
1645
|
+
// branch is taken, so each is a sink site (the last statement of a block is handled by the `return` case).
|
|
1646
|
+
findings.push(..._nestedSinkFindings([node.source], state, callContext, node.line));
|
|
1544
1647
|
}
|
|
1545
1648
|
// PRD R13(a): the assignment TARGET can itself be a sink shape
|
|
1546
1649
|
// (el.innerHTML = tainted) — additive to the RHS-call-sink check
|
|
@@ -1618,7 +1721,7 @@ function step(node, stateIn, callContext) {
|
|
|
1618
1721
|
// callee's internal noise.
|
|
1619
1722
|
sum = callContext._summaryCache.compute(qid, entry, () => {
|
|
1620
1723
|
const inner = {
|
|
1621
|
-
_findings: [], _taintSources:
|
|
1724
|
+
_findings: [], _taintSources: _paramPlaceholders(fn, entry), _returnTainted: false,
|
|
1622
1725
|
_stack: new Set(), deadlineMs: callContext.deadlineMs,
|
|
1623
1726
|
_summaryCache: callContext._summaryCache,
|
|
1624
1727
|
_callGraph: callContext._callGraph,
|
|
@@ -1628,6 +1731,7 @@ function step(node, stateIn, callContext) {
|
|
|
1628
1731
|
try { analyzeFunction(fn, _unionAnnotationTaint(fn, entry), inner); } catch {}
|
|
1629
1732
|
return {
|
|
1630
1733
|
returnTainted: !!inner._returnTainted,
|
|
1734
|
+
returnSources: inner._returnSources || [],
|
|
1631
1735
|
mutatedParams: inner._mutatedParamsOut || new Set(),
|
|
1632
1736
|
mutatedThisFields: inner._mutatedThisFieldsOut || new Set(),
|
|
1633
1737
|
taintedGlobals: new Set(),
|
|
@@ -1643,7 +1747,7 @@ function step(node, stateIn, callContext) {
|
|
|
1643
1747
|
};
|
|
1644
1748
|
});
|
|
1645
1749
|
}
|
|
1646
|
-
_mergeSummaryFindings(callContext, callContext._currentFnQid, sum, 'interproc');
|
|
1750
|
+
_mergeSummaryFindings(callContext, callContext._currentFnQid, sum, 'interproc', { args: callArgs, state: callerTainted });
|
|
1647
1751
|
// Next-gen taint capability #3: `x = bad.getData()` — a single-
|
|
1648
1752
|
// field getter's return is tainted whenever THIS call's own
|
|
1649
1753
|
// receiver already carries that field, independent of `sum`
|
|
@@ -1651,12 +1755,19 @@ function step(node, stateIn, callContext) {
|
|
|
1651
1755
|
const _getterTainted = _calleeGetterFieldTainted(node.source.callee, newState, callContext);
|
|
1652
1756
|
if ((sum && sum.returnTainted) || _getterTainted) {
|
|
1653
1757
|
newState = _addPathAliasAware(newState, target, callContext);
|
|
1758
|
+
// `from`: what made the arguments tainted. The return is the callee's, but the taint it carries began wherever the
|
|
1759
|
+
// caller's own arguments got theirs, and a reader (or a trust-boundary check) needs that origin, not the hop.
|
|
1760
|
+
const _from = [];
|
|
1761
|
+
for (const a of callArgs) for (const sr of _sourcesReachingExpr(a, callerTainted, callContext._taintSources)) _from.push(sr);
|
|
1762
|
+
if (sum && Array.isArray(sum.returnSources)) for (const sr of _resolveParams(sum.returnSources, _from.slice())) _from.push(sr);
|
|
1654
1763
|
callContext._taintSources.push({
|
|
1655
1764
|
varName: target,
|
|
1656
1765
|
sourceId: `interproc:${qid}`,
|
|
1657
1766
|
sourceLabel: `interproc-return:${calleeName}`,
|
|
1658
1767
|
provenance: 'interproc',
|
|
1659
1768
|
line: node.line,
|
|
1769
|
+
file: _currentFile || null,
|
|
1770
|
+
...(_from.length ? { from: _from.slice(0, 5) } : {}),
|
|
1660
1771
|
});
|
|
1661
1772
|
}
|
|
1662
1773
|
// applyAtCallSite — mutated params (and, next-gen capability #3,
|
|
@@ -1677,6 +1788,7 @@ function step(node, stateIn, callContext) {
|
|
|
1677
1788
|
const _argTainted = (node.source.args || []).some(a => exprTaint(a, newState, callContext));
|
|
1678
1789
|
if (builtin.returnTainted && _argTainted) {
|
|
1679
1790
|
newState = _addPathAliasAware(newState, target, callContext);
|
|
1791
|
+
_inheritSources(callContext, target, node.source.args || [], newState);
|
|
1680
1792
|
} else if (!builtin.returnTainted) {
|
|
1681
1793
|
// PRD R4b: a builtin summary saying returnTainted:false can mean
|
|
1682
1794
|
// two very different things — a genuinely non-deriving function
|
|
@@ -1738,7 +1850,7 @@ function step(node, stateIn, callContext) {
|
|
|
1738
1850
|
newState = _addPathAliasAware(newState, target, callContext);
|
|
1739
1851
|
const sourcePath = accessPathOf(node.source);
|
|
1740
1852
|
if (sourcePath) newState = addPath(newState, sourcePath);
|
|
1741
|
-
callContext._taintSources.push({ varName: target, sourceId: src.id, sourceLabel: src.label, provenance: src.provenance || null, line: node.line });
|
|
1853
|
+
callContext._taintSources.push({ varName: target, sourceId: src.id, sourceLabel: src.label, provenance: src.provenance || null, line: node.line, file: _currentFile || null });
|
|
1742
1854
|
} else if (exprTaint(node.source, newState, callContext)) {
|
|
1743
1855
|
// P1.1: when the source IS a pure access path (e.g., RHS is `obj.foo.bar`),
|
|
1744
1856
|
// taint the TARGET as well as transitively propagate the source path so
|
|
@@ -1748,6 +1860,7 @@ function step(node, stateIn, callContext) {
|
|
|
1748
1860
|
newState = _addPathAliasAware(newState, target, callContext);
|
|
1749
1861
|
const sourcePath = accessPathOf(node.source);
|
|
1750
1862
|
if (sourcePath && !isCoveredBy(newState, sourcePath)) newState = addPath(newState, sourcePath);
|
|
1863
|
+
_inheritSources(callContext, target, [node.source], newState);
|
|
1751
1864
|
}
|
|
1752
1865
|
} else {
|
|
1753
1866
|
// Re-assigning a previously-tainted var to a clean value clears it
|
|
@@ -1786,7 +1899,7 @@ function step(node, stateIn, callContext) {
|
|
|
1786
1899
|
if (!sum && entry.size && fn && fn.cfg) {
|
|
1787
1900
|
sum = callContext._summaryCache.compute(qid, entry, () => {
|
|
1788
1901
|
const inner = {
|
|
1789
|
-
_findings: [], _taintSources:
|
|
1902
|
+
_findings: [], _taintSources: _paramPlaceholders(fn, entry), _returnTainted: false,
|
|
1790
1903
|
_stack: new Set(), deadlineMs: callContext.deadlineMs,
|
|
1791
1904
|
_summaryCache: callContext._summaryCache,
|
|
1792
1905
|
_callGraph: callContext._callGraph,
|
|
@@ -1796,6 +1909,7 @@ function step(node, stateIn, callContext) {
|
|
|
1796
1909
|
try { analyzeFunction(fn, _unionAnnotationTaint(fn, entry), inner); } catch {}
|
|
1797
1910
|
return {
|
|
1798
1911
|
returnTainted: !!inner._returnTainted,
|
|
1912
|
+
returnSources: inner._returnSources || [],
|
|
1799
1913
|
mutatedParams: inner._mutatedParamsOut || new Set(),
|
|
1800
1914
|
mutatedThisFields: inner._mutatedThisFieldsOut || new Set(),
|
|
1801
1915
|
taintedGlobals: new Set(),
|
|
@@ -1806,7 +1920,7 @@ function step(node, stateIn, callContext) {
|
|
|
1806
1920
|
};
|
|
1807
1921
|
});
|
|
1808
1922
|
}
|
|
1809
|
-
_mergeSummaryFindings(callContext, callContext._currentFnQid, sum, 'interproc');
|
|
1923
|
+
_mergeSummaryFindings(callContext, callContext._currentFnQid, sum, 'interproc', { args: node.args || [], state });
|
|
1810
1924
|
// Next-gen taint capability #3: the plain-statement-position
|
|
1811
1925
|
// counterpart of the assign-from-call site above — `bad.setData(
|
|
1812
1926
|
// tainted);` never assigns anything, but the receiver's OWN field
|
|
@@ -2047,6 +2161,12 @@ function step(node, stateIn, callContext) {
|
|
|
2047
2161
|
}
|
|
2048
2162
|
if (exprTaint(node.value, state, callContext)) {
|
|
2049
2163
|
callContext._returnTainted = true;
|
|
2164
|
+
// Where the returned taint began, for the caller that will name this function's return as a source (`from`, below).
|
|
2165
|
+
const rs = _sourcesReachingExpr(node.value, state, callContext._taintSources);
|
|
2166
|
+
if (rs.length) {
|
|
2167
|
+
const keep = (callContext._returnSources ||= []);
|
|
2168
|
+
for (const r of rs) if (keep.length < 8 && !keep.some((k) => k.sourceId === r.sourceId && k.line === r.line)) keep.push(r);
|
|
2169
|
+
}
|
|
2050
2170
|
}
|
|
2051
2171
|
return { state, findings };
|
|
2052
2172
|
}
|
|
@@ -2180,6 +2300,13 @@ function analyzeFunction(fn, entryState, callContext) {
|
|
|
2180
2300
|
// unchanged and lets `step` consult `aliasesForVar` when callContext._pointsTo
|
|
2181
2301
|
// is present.
|
|
2182
2302
|
if (callContext) callContext._currentFnQid = fn.qid;
|
|
2303
|
+
// A parameter the language front end marked as an input source (a request value handed to a handler, a caller-supplied text) has an
|
|
2304
|
+
// origin like any other source read: record it, so the evidence chain of a finding that rests on it names where the value came from.
|
|
2305
|
+
if (callContext && Array.isArray(callContext._taintSources) && Array.isArray(fn.paramAnnotations) && fn.paramAnnotations.length) {
|
|
2306
|
+
for (const r of matchAnnotationParamSources(fn.paramAnnotations, fn.file)) {
|
|
2307
|
+
if (!callContext._taintSources.some((x) => x.varName === r.name && x.sourceId === r.id)) callContext._taintSources.push({ varName: r.name, sourceId: r.id, sourceLabel: r.label, provenance: r.provenance, line: fn.line || 0, file: fn.file || null, annotation: true });
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2183
2310
|
const deadlineMs = (callContext && typeof callContext.deadlineMs === 'number') ? callContext.deadlineMs : Infinity;
|
|
2184
2311
|
const visited = 0;
|
|
2185
2312
|
let iterations = 0;
|
|
@@ -2494,6 +2621,7 @@ export function runTaintEngine(perFileIR, callGraph, opts = {}) {
|
|
|
2494
2621
|
exitStateByQid.set(fn.qid, ctx._exitState || new Set());
|
|
2495
2622
|
const next = {
|
|
2496
2623
|
returnTainted: !!ctx._returnTainted,
|
|
2624
|
+
returnSources: ctx._returnSources || [],
|
|
2497
2625
|
mutatedParams: ctx._mutatedParamsOut || new Set(),
|
|
2498
2626
|
mutatedThisFields: ctx._mutatedThisFieldsOut || new Set(),
|
|
2499
2627
|
taintedGlobals: new Set(),
|
|
@@ -2695,6 +2823,7 @@ export function runTaintEngine(perFileIR, callGraph, opts = {}) {
|
|
|
2695
2823
|
// _mergeSummaryFindings — dedup in _collectFindings makes this safe.
|
|
2696
2824
|
const fieldSummary = {
|
|
2697
2825
|
returnTainted: !!ctx._returnTainted,
|
|
2826
|
+
returnSources: ctx._returnSources || [],
|
|
2698
2827
|
mutatedParams: ctx._mutatedParamsOut || new Set(),
|
|
2699
2828
|
mutatedThisFields: ctx._mutatedThisFieldsOut || new Set(),
|
|
2700
2829
|
taintedGlobals: new Set(),
|
|
@@ -2801,6 +2930,7 @@ export function runTaintEngine(perFileIR, callGraph, opts = {}) {
|
|
|
2801
2930
|
_collectFindings(fn, ctx._findings);
|
|
2802
2931
|
summaryCache.set(fn.qid, qualifiedSeeds, {
|
|
2803
2932
|
returnTainted: !!ctx._returnTainted,
|
|
2933
|
+
returnSources: ctx._returnSources || [],
|
|
2804
2934
|
mutatedParams: ctx._mutatedParamsOut || new Set(),
|
|
2805
2935
|
mutatedThisFields: ctx._mutatedThisFieldsOut || new Set(),
|
|
2806
2936
|
taintedGlobals: new Set(),
|
|
@@ -2846,6 +2976,7 @@ export function runTaintEngine(perFileIR, callGraph, opts = {}) {
|
|
|
2846
2976
|
// only place that used to merge findings) at all.
|
|
2847
2977
|
summaryCache.set(fn.qid, taintedEntry, {
|
|
2848
2978
|
returnTainted: !!ctx._returnTainted,
|
|
2979
|
+
returnSources: ctx._returnSources || [],
|
|
2849
2980
|
mutatedParams: ctx._mutatedParamsOut || new Set(),
|
|
2850
2981
|
mutatedThisFields: ctx._mutatedThisFieldsOut || new Set(),
|
|
2851
2982
|
taintedGlobals: new Set(),
|
|
@@ -2922,6 +3053,7 @@ export function runTaintEngine(perFileIR, callGraph, opts = {}) {
|
|
|
2922
3053
|
try { analyzeFunction(cbFn, _unionAnnotationTaint(cbFn, cbEntry), inner); } catch {}
|
|
2923
3054
|
return {
|
|
2924
3055
|
returnTainted: !!inner._returnTainted,
|
|
3056
|
+
returnSources: inner._returnSources || [],
|
|
2925
3057
|
mutatedParams: inner._mutatedParamsOut || new Set(),
|
|
2926
3058
|
mutatedThisFields: inner._mutatedThisFieldsOut || new Set(),
|
|
2927
3059
|
taintedGlobals: new Set(),
|
|
@@ -3064,10 +3196,15 @@ export function runTaintEngine(perFileIR, callGraph, opts = {}) {
|
|
|
3064
3196
|
// 'source-from-network' factor permanently dead (it reads
|
|
3065
3197
|
// t.provenance off chain/trace entries).
|
|
3066
3198
|
sourceProvenance: f.sourceProvenance || null,
|
|
3199
|
+
// The ORIGINS behind the trace: an `interproc-return` hop is followed back through `from` to the sources that tainted its
|
|
3200
|
+
// arguments. Only a finding whose trace has such a hop gets the field, so every other finding is unchanged.
|
|
3201
|
+
...(Array.isArray(f.trace) && f.trace.some((t) => t && t.from) ? { sourceRoots: _rootSources(f.trace) } : {}),
|
|
3067
3202
|
source: f.trace && f.trace.length ? {
|
|
3068
3203
|
file: fn.file,
|
|
3069
3204
|
line: f.trace[0].line,
|
|
3070
3205
|
label: f.trace[0].sourceLabel,
|
|
3206
|
+
// a parameter annotated as a source is not part of a finding's stable identity (posture/stable-id.js): naming it must not rename the finding
|
|
3207
|
+
...(f.trace[0].annotation ? { kind: 'parameter' } : {}),
|
|
3071
3208
|
} : null,
|
|
3072
3209
|
sink: {
|
|
3073
3210
|
file: fn.file,
|
|
@@ -19,7 +19,8 @@ export const HASKELL_PRIVACY_SINKS = Object.freeze([
|
|
|
19
19
|
// log: derived from the same table the sensitive-logging rule reads
|
|
20
20
|
...HS_LOG_SINKS.map((e) => ({
|
|
21
21
|
kind: 'sink', id: `privacy-hs-log-${e.module}.${e.name}`.toLowerCase(), language: 'hs', framework: 'haskell', category: 'log',
|
|
22
|
-
|
|
22
|
+
// a variadic log call (printf) carries its data in every argument after the format
|
|
23
|
+
match: { type: 'call', callee: `${e.module}.${e.name}` }, argIndex: e.argFrom !== undefined ? 'all' : e.argIndex,
|
|
23
24
|
vuln: { name: `Privacy Leak (${e.channel} log)`, severity: 'medium', cwe: 'CWE-359', remediation: 'Do not log regulated data (PII/PHI/PCI). Redact or hash the field before logging.' },
|
|
24
25
|
})),
|
|
25
26
|
// response
|