@clear-capabilities/agentic-security-scanner 0.163.0 → 0.164.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 +22 -0
- package/dist/9560.index.js +22 -8
- package/dist/agentic-security.mjs +1 -1
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +5 -5
- package/src/capabilities/CLAUDE.md +15 -11
- package/src/capabilities/probes.js +22 -8
- package/src/dataflow/CLAUDE.md +1 -0
- package/src/dataflow/catalog.js +21 -1
- package/src/dataflow/engine.js +47 -2
- package/src/sandbox/CLAUDE.md +49 -17
- package/src/sandbox/backend-namespace.js +223 -109
- package/src/sandbox/capabilities.js +12 -0
- package/src/sandbox/control-probes.js +54 -4
- package/src/sandbox/linux-probes.js +178 -0
- package/src/sandbox/linux-rootfs.js +256 -0
- package/src/sandbox/supervise.js +31 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
cca41fc14779bcf557d1a825a32e919b51bcd4c59d73e814d37ab2ed93ca8278 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.164.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",
|
|
@@ -68,14 +68,14 @@
|
|
|
68
68
|
"test:extras": "AGENTIC_SECURITY_CPP_DATAFLOW=1 node --test test/cpp-dataflow.test.js && node --experimental-test-module-mocks --test test/fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection-k8s-admission.test.js && node --experimental-test-module-mocks --test test/lineage-fault-injection.test.js && npm run test:python",
|
|
69
69
|
"loop": "node ../scripts/loop-engineering/run.mjs",
|
|
70
70
|
"test:loop-real-prd": "node --test ../scripts/loop-engineering/test/loop-real-prd.test.js",
|
|
71
|
-
"test:loop": "node --test --test-concurrency=1 ../scripts/loop-engineering/test/loop-manifest.test.js ../scripts/loop-engineering/test/loop-watchdog.test.js ../scripts/loop-engineering/test/loop-background.test.js ../scripts/loop-engineering/test/loop-dashboard.test.js ../scripts/loop-engineering/test/loop-evidence.test.js ../scripts/loop-engineering/test/loop-recovery.test.js ../scripts/loop-engineering/test/loop-remote.test.js ../scripts/loop-engineering/test/loop-runbook.test.js ../scripts/loop-engineering/test/loop-assurance-profile.test.js ../scripts/loop-engineering/test/loop-assurance-runtime.test.js",
|
|
71
|
+
"test:loop": "node --test --test-concurrency=1 ../scripts/loop-engineering/test/loop-manifest.test.js ../scripts/loop-engineering/test/loop-watchdog.test.js ../scripts/loop-engineering/test/loop-background.test.js ../scripts/loop-engineering/test/loop-dashboard.test.js ../scripts/loop-engineering/test/loop-evidence.test.js ../scripts/loop-engineering/test/loop-recovery.test.js ../scripts/loop-engineering/test/loop-remote.test.js ../scripts/loop-engineering/test/loop-runbook.test.js ../scripts/loop-engineering/test/loop-assurance-profile.test.js ../scripts/loop-engineering/test/loop-assurance-runtime.test.js ../scripts/loop-engineering/test/loop-bounds.test.js ../scripts/loop-engineering/test/loop-spend.test.js",
|
|
72
72
|
"test:python": "node ../scripts/run-python-tests.mjs",
|
|
73
73
|
"test:smoke": "node --test test/smoke.test.js test/demo-app.test.js",
|
|
74
74
|
"test:ollama-e2e": "AGENTIC_SECURITY_OLLAMA_E2E=1 node --test test/ollama-e2e.test.js",
|
|
75
75
|
"test:glob": "node --test test/glob-compat.test.js",
|
|
76
76
|
"test:sast": "node --test test/cpp-hardcoded-secret.test.js test/sibling-guard.test.js test/comment-blindness.test.js test/crypto-specialist.test.js test/llm.test.js test/llm-cost-advisor.test.js test/llm-owasp.test.js test/logic.test.js test/authz.test.js test/model-load.test.js test/prompt-template.test.js test/business-logic.test.js test/python-sinks.test.js test/phase1-detectors.test.js test/phase2-detectors.test.js test/phase3-v3.test.js test/phase7-extensions.test.js test/phase8-extensions.test.js test/new-cwe-detectors.test.js test/file-upload.test.js test/llmsecops-detectors.test.js test/db-taint.test.js test/dart-swift.test.js test/zip-slip-containment-guard.test.js test/redos-anchoring.test.js test/path-guard-validator-call.test.js test/redos-nfa.test.js test/weak-randomness.test.js test/csharp-pipeline.test.js test/csharp-cross-file.test.js test/post-quantum-crypto.test.js test/web3-advanced.test.js test/cloud-iam-k8s.test.js test/crypto-protocol.test.js test/ml-supply-chain.test.js test/mcp-audit-scan.test.js test/wrong-context-sanitizer.test.js test/sanitizer-context.test.js test/frontend-hygiene.test.js test/csv-injection.test.js test/stored-taint.test.js test/tree-sitter-sinks.test.js test/kotlin-structural.test.js test/ruby-php-structural.test.js test/java-csharp-structural.test.js test/java-structural-cross-file.test.js test/guard-recognition.test.js test/guard-window-identifier-correlation.test.js test/js-python-framework-structural.test.js test/go-structural.test.js test/secret-concat.test.js test/secret-redaction.test.js test/xss-reflected-multilang.test.js test/code-injection-multilang.test.js test/xxe-multilang.test.js test/xpath-injection-multilang.test.js test/gapfill-batch13.test.js test/agent-untrusted-flow.test.js test/api-authz.test.js test/event-entrypoint.test.js test/iac-terraform.test.js test/cross-service.test.js test/rbac-consistency.test.js test/adversarial-robustness.test.js test/secret-entropy.test.js test/comment-strip.test.js test/juliet-path-filter-gate.test.js test/java-bench-shape-opt-in.test.js test/java-ast-folding.test.js test/java-bench-extras.test.js test/reachability-missing-callgraph-data.test.js test/rate-limit.test.js test/auth-signals.test.js test/convention-deviation.test.js test/resource-exhaustion.test.js test/resource-exhaustion-realworld.test.js test/redirect-toctou.test.js test/redirect-toctou-realworld.test.js test/codegen-sink.test.js test/ownership-authz.test.js test/ownership-authz-realworld.test.js test/t21-precision-audit.test.js test/secrets-coverage.test.js test/prompt-injection-payloads.test.js test/iac-cloud-templates.test.js test/ruby-path-join.test.js test/sast/manifest-line-tracking.test.js test/rust-sast.test.js test/java-deserialization-precision.test.js test/pipeline-env-secret.test.js test/mark-used-vuln-functions.test.js",
|
|
77
77
|
"test:posture": "node --test test/threat-model.test.js test/git-hardening.test.js test/privacy-framework.test.js test/material-change.test.js test/drift.test.js test/scorecard.test.js test/accuracy-scorecard.test.js test/scorecard-gate.test.js test/predeploy-gate.test.js test/release-check.test.js test/pre-push-gate.test.js test/dependency-currency.test.js test/package-contents-check.test.js test/gate-verdict-cache.test.js test/run-unit-tests.test.js test/temp-leak-guard.test.js test/release-workflow.test.js test/release-workflow-structure.test.js test/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-bundle.test.js test/obligation-evidence-pack.test.js test/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-policy-completeness.test.js test/egress-policy-integration.test.js test/router.test.js test/legacy-alias-redirect.test.js test/cache-economics.test.js test/coverage-report.test.js test/corpus-status.test.js test/provenance.test.js test/secret-history.test.js test/execution-proof.test.js test/fix-metrics.test.js test/corpus-enroll.test.js test/prove-findings.test.js test/corpus-match.test.js test/corpus-provenance.test.js test/learning-quorum.test.js test/llm-cache-integrity.test.js test/proof-artifact.test.js test/providers.test.js test/fleet.test.js test/autopilot.test.js test/autopilot-cli.test.js test/verifier-independence.test.js test/integrity-legacy-key.test.js test/suppression-visibility.test.js test/cost-ceiling.test.js test/local-endpoint.test.js test/ollama-provider.test.js test/ollama-capabilities.test.js test/ollama-offline-egress.test.js test/ollama-fix-proposal.test.js test/ollama-explain-proposal.test.js test/ollama-poc-proposal.test.js test/ollama-model-probe.test.js test/ollama-agent-tools.test.js test/ollama-agent-loop.test.js test/ollama-e2e.test.js test/ollama-oom-feedback.test.js test/ollama-doc-drift.test.js test/model-trust.test.js test/vuln-archaeology.test.js test/scan-checkpoint.test.js test/llm-redact.test.js test/world-class-batch2.test.js test/world-class-modules.test.js test/privacy-ir-adapter.test.js test/privacy-taxonomy.test.js test/privacy-sink-policy.test.js test/privacy-governance.test.js test/privacy-inventory.test.js test/integration-end-to-end.test.js test/streak-cli-wiring.test.js test/stack-playbook.test.js test/machine-output-flag.test.js test/stdout-flush.test.js test/format-determinism.test.js test/only-flag-channels.test.js test/confidence.test.js test/security-trend.test.js test/calibration-drift.test.js test/validator-metrics.test.js test/clustering-ir-taint-line.test.js test/concurrency-cwe.test.js test/compliance-mapping-liveness.test.js test/module-artifact-liveness.test.js test/supply-chain-provenance-completeness.test.js test/compliance-severity-threshold.test.js test/compliance-severity-policy.test.js test/compliance-evidence-binding.test.js test/compliance-evidence-signing.test.js test/compliance-file-contains.test.js test/label-isolation.test.js test/evidence-grade-wording.test.js test/llm-model-status.test.js test/egress-policy-constraints.test.js test/egress-redact.test.js test/egress-audit.test.js test/egress-approved-providers.test.js test/family-registry.test.js test/coverage-strength.test.js test/framework-provenance.test.js test/framework-provenance-controlrefs.test.js test/third-party-verification.test.js test/proof-coverage.test.js test/calibration-holdout.test.js test/independent-population-gate.test.js test/catalog-freshness.test.js test/incremental-parity.test.js test/surface-smoke.test.js test/fix-coverage.test.js test/raw-source-carveout.test.js test/mlbom-conformance.test.js test/deploy-gate-replay.test.js test/sbom-conformance.test.js test/unscored-claims.test.js test/mcp-rug-pull.test.js test/agent-boundary-taint.test.js test/confinement-adversarial.test.js test/comment-strip-cost.test.js test/reachability-claim.test.js test/entrypoint-breadth.test.js test/dep-file-admission.test.js test/custom-rules-freshness.test.js test/posture/provenance-schema.test.js test/posture/provenance-cache.test.js test/helpers/build-git-fixture.test.js test/posture/provenance-git-evidence.test.js test/posture/provenance-dag-walk.test.js test/posture/provenance-confidence.test.js test/posture/provenance-predicate-replay.test.js test/posture/provenance-origin-resolver.test.js test/posture/provenance-ai-authorship.test.js test/posture/provenance-branch-entry.test.js test/posture/provenance-evidence-attribution.test.js test/posture/provenance-lifecycle.test.js test/posture/provenance-missing-control-resolver.test.js test/posture/provenance-missing-control-wiring.test.js test/posture/provenance-coordinator.test.js test/posture/provenance-sca-origin.test.js test/posture/provenance-transitive-sca.test.js test/posture/provenance-coordinator-sca.test.js test/posture/provenance-providers.test.js test/posture/provenance-evidence-bundle.test.js test/posture/provenance-repo-lineage.test.js test/posture/provenance-secrets-logic.test.js test/cli/provenance-flags.test.js test/cli/attest-provenance.test.js test/cli/attest-obligations.test.js test/cli/models.test.js test/cli/setup-llm.test.js test/cli/fix-retry.test.js test/cli/compliance-walkthrough-lineage.test.js test/cli/dataflow-export-privacy.test.js test/cli/dataflow-export-briefing.test.js test/cli/lineage-snapshot-persist.test.js test/cli/dataflow-diff.test.js test/cli/dataflow-watch.test.js test/cli/dataflow-recipients.test.js test/cli/dataflow-coverage.test.js test/security/provenance-safety.test.js test/security/provenance-injection.test.js test/posture/remediation-ledger.test.js test/posture/assurance-baseline.test.js test/posture/assurance-contracts.test.js test/posture/assurance-config.test.js test/evidence-issuer.test.js test/load-factor.test.js",
|
|
78
|
-
"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",
|
|
78
|
+
"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",
|
|
79
79
|
"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",
|
|
80
80
|
"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",
|
|
81
81
|
"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",
|
|
@@ -106,11 +106,11 @@
|
|
|
106
106
|
"evaluation": "node ../scripts/evaluation.mjs",
|
|
107
107
|
"evaluation:synthetic": "node ../scripts/evaluation.mjs synthetic",
|
|
108
108
|
"test:routing": "node --test test/routing/outcomes.test.js test/routing/economics.test.js test/routing/adapter.test.js test/routing/calibration.test.js test/routing/decide.test.js test/routing/unchanged-when-off.test.js test/routing/shadow.test.js test/routing/promotion.test.js test/routing/drift.test.js test/routing/canary.test.js test/routing/feedback.test.js test/routing/report.test.js",
|
|
109
|
-
"test:release-closure": "node --test test/release-closure/closure-plan.test.js test/release-closure/closure-record.test.js test/release-closure/compatibility.test.js test/release-closure/rollout-stages.test.js test/release-closure/migration-readers.test.js test/release-closure/rollback.test.js test/release-closure/wrappers.test.js test/release-closure/final-closure.test.js test/release-closure/final-closure-controller.test.js test/release-closure/runner-hardening.test.js",
|
|
109
|
+
"test:release-closure": "node --test test/release-closure/closure-plan.test.js test/release-closure/closure-record.test.js test/release-closure/compatibility.test.js test/release-closure/rollout-stages.test.js test/release-closure/migration-readers.test.js test/release-closure/rollback.test.js test/release-closure/wrappers.test.js test/release-closure/final-closure.test.js test/release-closure/final-closure-controller.test.js test/release-closure/runner-hardening.test.js test/release-closure/remote-attestation.test.js",
|
|
110
110
|
"release:closure:check": "node ../scripts/release-closure.mjs --run",
|
|
111
111
|
"release:closure:static": "node ../scripts/release-closure.mjs --static",
|
|
112
112
|
"test:documentation": "node --test test/documentation/drift-check.test.js test/documentation/generated-pages.test.js test/documentation/scope-and-examples.test.js test/documentation/policy-cards.test.js test/documentation/operations-and-review.test.js",
|
|
113
|
-
"test:capabilities": "node --test test/capabilities/manifest.test.js test/capabilities/filesystem.test.js test/capabilities/exec.test.js test/capabilities/network.test.js test/capabilities/canary.test.js test/capabilities/tools.test.js test/capabilities/recovery.test.js test/capabilities/receipts.test.js test/capabilities/adversarial.test.js",
|
|
113
|
+
"test:capabilities": "node --test test/capabilities/manifest.test.js test/capabilities/filesystem.test.js test/capabilities/exec.test.js test/capabilities/network.test.js test/capabilities/canary.test.js test/capabilities/tools.test.js test/capabilities/recovery.test.js test/capabilities/receipts.test.js test/capabilities/adversarial.test.js test/capabilities/linux-enforcement.test.js",
|
|
114
114
|
"test:verification": "node --test test/verification/verification-record.test.js test/verification/oracle-adapters.test.js test/verification/replay-manifest.test.js test/verification/patch-negative.test.js test/verification/non-taint-classes.test.js test/verification/interface-equivalence.test.js test/verification/advisory-gating.test.js test/verification/oracle-conformance.test.js",
|
|
115
115
|
"test:deployment": "node --test test/deployment/boundary-graph.test.js test/deployment/config-adapters.test.js test/deployment/trace-correlation.test.js test/deployment/cross-repo.test.js test/deployment/attack-paths.test.js test/deployment/drift.test.js test/deployment/boundary-presentation.test.js test/deployment/differentiation-ablation.test.js test/deployment/boundaries-cli.test.js",
|
|
116
116
|
"test:portfolio": "node --test test/portfolio/manifest.test.js test/portfolio/bundle.test.js test/portfolio/signing.test.js test/portfolio/work-units.test.js test/portfolio/resume.test.js test/portfolio/scheduler.test.js test/portfolio/progress.test.js test/portfolio/retention.test.js test/portfolio/backend.test.js",
|
|
@@ -67,15 +67,20 @@ report says so. Without the opt-in an isolation-required task is `unsupported`
|
|
|
67
67
|
(`platform-unsupported`). A control that is not proved BLOCKS the run (typed
|
|
68
68
|
`blocked`, `missing-execution-backend`, the target never executes).
|
|
69
69
|
|
|
70
|
-
**Linux is `
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
caps are
|
|
70
|
+
**Linux is `partially-verified`**, and only by the hosted `sandbox-linux` job. Its
|
|
71
|
+
active probes proved write confinement, read denial, environment scrubbing, the
|
|
72
|
+
no-network default, tree termination, the file-size limit, read confinement to
|
|
73
|
+
declared roots and multi-root writes on the namespace backend (a pivoted tmpfs
|
|
74
|
+
root; protected paths absent or masked; a PID namespace the kernel tears down).
|
|
75
|
+
NOT verified: **mediated network** is not implemented on Linux (an empty network
|
|
76
|
+
namespace has no path to a proxy), so a task that declares a destination is
|
|
77
|
+
`blocked` there with `network-mediation is unsupported`, never allowed; and
|
|
78
|
+
**process-count caps** are never asserted on any backend (on Linux the cause of
|
|
79
|
+
the earlier non-refusal is that the resource prelude runs under dash, whose
|
|
80
|
+
`ulimit` has no `-u`; no replacement is proved). `maxProcesses` is carried and
|
|
81
|
+
reported `unverified`, memory caps `not-enforced`. The execution suites under
|
|
82
|
+
`test/capabilities/` still skip on Linux (their helpers select the userspace
|
|
83
|
+
backend only and several assertions encode macOS semantics); see the gaps below.
|
|
79
84
|
|
|
80
85
|
## Known limits (also in `REPORT_LIMITATIONS`)
|
|
81
86
|
|
|
@@ -107,8 +112,7 @@ attempts were stopped on this backend in this run; it does not show the sandbox
|
|
|
107
112
|
### What is still not claimed
|
|
108
113
|
|
|
109
114
|
Tool and delegation checks are policy at the tool boundary (the tool then runs in the server process): the
|
|
110
|
-
runner remains the only enforcement layer. Linux
|
|
111
|
-
record, and no Linux outcome is asserted. No process-count cap is asserted. The corpus is not wired into the
|
|
115
|
+
runner remains the only enforcement layer. Linux is `partially-verified` in the platform statement (the controls listed there, on the hosted job only) and the attack-coverage record still says `unverified` for Linux, because the corpus is not run on Linux. No process-count cap is asserted. The corpus is not wired into the
|
|
112
116
|
`sandbox-linux` CI job: it would need execution cases that skip loudly (never silently pass) on Linux, and that
|
|
113
117
|
has not been shown from this workspace. The enforced-mode release gate (`enforcedModeReleaseGate`) is a library
|
|
114
118
|
function used by the suite; it is not yet called from `scripts/release-check.mjs`.
|
|
@@ -48,15 +48,18 @@ function mk(prefix) { return fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(
|
|
|
48
48
|
function rm(p) { try { fs.rmSync(p, { recursive: true, force: true }); } catch { /* best effort */ } }
|
|
49
49
|
const token = (p) => `${p}-${crypto.randomBytes(8).toString('hex')}`;
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
// `run` is a seam so a test can hand the probe a runner that is deliberately
|
|
52
|
+
// wrong (a read root left too wide, a root left writable) and see the probe say
|
|
53
|
+
// so: a probe that cannot fail proves nothing.
|
|
54
|
+
export async function probeFsRead(backend, runner = runConfinedSupervised) {
|
|
55
|
+
if (backend !== 'userspace' && backend !== 'namespace') return unsupported(`read confinement to declared roots is not implemented on the ${backend} backend`);
|
|
53
56
|
const root = mk('agsec-cap-r-'); const ro = mk('agsec-cap-ro-'); const outside = mk('agsec-cap-out-');
|
|
54
57
|
try {
|
|
55
58
|
const okTok = token('READABLE'); const secTok = token('SEALED');
|
|
56
59
|
fs.writeFileSync(path.join(ro, 'r.txt'), okTok);
|
|
57
60
|
fs.writeFileSync(path.join(outside, 's.txt'), secTok);
|
|
58
61
|
fs.symlinkSync(path.join(outside, 's.txt'), path.join(ro, 'link'));
|
|
59
|
-
const run = (script, readRoots) =>
|
|
62
|
+
const run = (script, readRoots) => runner(['/bin/sh', '-c', script], { root, readRoots, timeoutMs: 8000, graceMs: 300 });
|
|
60
63
|
const pos = await run(`cat '${ro}/r.txt'`, [ro]);
|
|
61
64
|
if (!pos.stdout.includes(okTok)) return notProved(`positive control failed: a file inside a declared root was not readable (${pos.status})`);
|
|
62
65
|
const open = await run(`cat '${outside}/s.txt'`, [ro, outside]);
|
|
@@ -75,13 +78,13 @@ async function probeFsRead(backend) {
|
|
|
75
78
|
} finally { rm(root); rm(ro); rm(outside); }
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
async function probeMultiWrite(backend) {
|
|
79
|
-
if (backend !== 'userspace') return unsupported(`several declared write roots are not implemented on the ${backend} backend`);
|
|
81
|
+
export async function probeMultiWrite(backend, runner = runConfinedSupervised) {
|
|
82
|
+
if (backend !== 'userspace' && backend !== 'namespace') return unsupported(`several declared write roots are not implemented on the ${backend} backend`);
|
|
80
83
|
const root = mk('agsec-cap-w-'); const w1 = mk('agsec-cap-w1-'); const w2 = mk('agsec-cap-w2-');
|
|
81
84
|
const ro = mk('agsec-cap-wro-'); const outside = mk('agsec-cap-wout-');
|
|
82
85
|
try {
|
|
83
86
|
const script = [`echo a > '${w1}/f'`, `echo b > '${w2}/f'`, `echo c > '${ro}/f'`, `echo d > '${outside}/f'`, 'true'].join('; ');
|
|
84
|
-
await
|
|
87
|
+
await runner(['/bin/sh', '-c', script], { root, readRoots: [ro], writeRoots: [w1, w2], timeoutMs: 8000, graceMs: 300 });
|
|
85
88
|
if (!fs.existsSync(path.join(w1, 'f')) || !fs.existsSync(path.join(w2, 'f'))) return notProved('positive control failed: a declared write root was not writable');
|
|
86
89
|
if (fs.existsSync(path.join(ro, 'f'))) return notProved('a root declared read-only was writable');
|
|
87
90
|
if (fs.existsSync(path.join(outside, 'f'))) return notProved('an undeclared directory was writable');
|
|
@@ -197,8 +200,19 @@ export function isAdvertisedBackend(backend, platform = process.platform) {
|
|
|
197
200
|
export function platformStatements() {
|
|
198
201
|
return Object.freeze({
|
|
199
202
|
linux: {
|
|
200
|
-
backend: 'namespace', status: '
|
|
201
|
-
|
|
203
|
+
backend: 'namespace', status: 'partially-verified',
|
|
204
|
+
// Evidence is the hosted sandbox-linux CI job (ubuntu-latest, x86_64), whose active probes
|
|
205
|
+
// (attack plus positive control, with fault-injection counterparts) proved these controls.
|
|
206
|
+
// Anything not listed here is not claimed: a verified control is verified on that runner image
|
|
207
|
+
// and kernel, not on every Linux host, which is why the job runs on every push.
|
|
208
|
+
evidence: 'sandbox-linux CI job (hosted ubuntu-latest, x86_64); scripts/sandbox-linux-verify.mjs',
|
|
209
|
+
verifiedControls: Object.freeze([
|
|
210
|
+
'write-confinement', 'read-denial', 'env-scrub', 'network', 'tree-termination', 'file-size-limit',
|
|
211
|
+
'fs-read-confinement', 'fs-multi-root-write',
|
|
212
|
+
]),
|
|
213
|
+
unsupportedControls: Object.freeze(['network-mediation']),
|
|
214
|
+
unassertedControls: Object.freeze(['process-cap']),
|
|
215
|
+
note: 'advertised for enforced mode. Proved by active probes on the hosted Linux runner (not on every Linux host): write-confinement, read-denial, env-scrub, network, tree-termination, file-size-limit, fs-read-confinement, fs-multi-root-write. Mediated network is NOT implemented on the namespace backend (an empty network namespace has no path to a proxy), so a task that declares a network destination is blocked on Linux, never allowed. Process-count caps are never claimed: the cause of the earlier non-refusal was found (the shell used for the resource prelude has no process-limit option, so the cap was never applied) and no replacement has been proved. The attack corpus is not run in the sandbox-linux job, so corpus coverage stays unverified on Linux.',
|
|
202
216
|
},
|
|
203
217
|
darwin: {
|
|
204
218
|
backend: 'userspace', status: 'host-proved-not-advertised',
|
package/src/dataflow/CLAUDE.md
CHANGED
|
@@ -16,6 +16,7 @@ Layer-2 taint engine. Walks the Layer-1 IR (`../ir/`) with field-sensitive forwa
|
|
|
16
16
|
- Guarded by `bench/mutation/runner.mjs` (`npm run bench:mutation:check`), which scores verdict-flip correctness over metamorphic rewrites (verdict must hold) and adversarial near-misses (verdict must flip). Removing the family check makes it exit non-zero.
|
|
17
17
|
- **`match.receiver`** (optional, regex source string) constrains a call to call sites whose receiver chain has a matching segment. Callee matching is by *bare name*, so `callee: 'write'` otherwise matches `process.stdout.write` and `fh.write` as readily as `document.write`. Works for both sinks (via `matchSinkOrSanitizer`) and sources (via `matchSource`'s CALL-source path). `match.receiverBase` is an optional, independent second pattern (both `_receiverAllowed` checks must pass) — use it when `receiver` alone would still match an unrelated same-named local, e.g. `py-flask-args-get` requires *both* a property segment (`args`/`form`/`headers`/…) *and* a `request`/`req` segment, so `args = parse(); args.get(x)` doesn't fire just because some catalog entry happens to be named `get`.
|
|
18
18
|
- **`match.receiverExclude`** (optional, regex source string) is the only NEGATIVE form — `receiver`/`receiverBase` can only ever REQUIRE a match, which cannot express "fire on a bare call, but not on THIS receiver." `py-compile` needs exactly that: Python's dangerous `compile()` builtin (`compile(source, filename, mode)`, equivalent to `eval` when followed by `exec`) is called BARE, but the catalog's bare-name last-segment fallback (`_calleeIndexHits`) makes the same key also match `re.compile(pattern)` — an ordinary regex compiler with no code-execution capability. `receiverExclude: '^re$'` fixes it. Checked FIRST, independently of `receiver`/`receiverBase`, and a bare call (no receiver segments) can never match an exclude pattern — so setting only `receiverExclude` never blocks the no-receiver case it exists to preserve. Found via this project's own self-scan once `_calleeReceiverTainted` (below) started correctly propagating taint through `.get()`-style calls on a tainted dict/file-read chain — the sink was always mismatched; the engine fix just gave taint a path to reach it. Guarded by `test/catalog-code-injection.test.js`.
|
|
19
|
+
- **`match.receiverTypeExclude`** (optional, array of regex sources) is the type-level negative form: a CONFIDENTLY resolved receiver type matching it removes the entry (`_receiverTypeAllowed`); an unresolved type never excludes, so unknown stays conservative. `js-exec` uses `['^RegExp$']` because a regex's `.exec()` shares its method name with `child_process.exec` and is far more common on request data (it was a high-volume false positive). The type comes from three places: `catalog.js`'s `_inlineReceiverType` (a regex literal or `RegExp(...)` call as the receiver, applied inside `matchSinkOrSanitizer` so every caller gets it), CHA `classOfVar` (in-function `new RegExp`), and `engine.js`'s `_regexNamesByFile` (a file-wide name set: every assignment to the name anywhere in the file is a regex initialiser and it is never a parameter; built by `_buildRegexNames` at the start of `runTaintEngine`, because CHA cannot see a module-scope const from inside a handler). The name set is deliberately scope-blind, so a name bound to anything else anywhere in the file is not trusted. Guarded by `test/rx-exec-receiver.test.js` and the `rx-exec-*` cases in `bench/mutation/runner.mjs`.
|
|
19
20
|
- **`language`** is enforced by `_languageAllowed` (Phase 2) via a table (`_LANG_EXT` in `catalog.js`) covering all ten catalog languages — `js`, `py`, `cs`, `kt`, `go`, `php`, `rb`, `java`, `rs` by literal regex kept byte-identical to `ir/index.js`'s own dispatch regexes (pinned by `test/phase2-scoping.test.js`, which compares `.source` directly, not just "does this regex literal appear somewhere in the file"), and `cpp` by delegating to `ir/parser-cpp.js`'s `cppExtRe()` so it can't drift from the parser. A language with no table entry stays permissive (matches any file) — additive by design, so it can't regress a language before its entry exists.
|
|
20
21
|
- **Path feasibility.** Constant-folds `if` conditions to prune unreachable branches.
|
|
21
22
|
- **Per-flow source attribution.** Sources reported on a finding are the ones actually reaching the sink argument (via free-var matching in the sink expression), NOT the first source the worklist happened to see. Premortem-derived.
|
package/src/dataflow/catalog.js
CHANGED
|
@@ -79,7 +79,7 @@ export const CATALOG = [
|
|
|
79
79
|
vuln: { name: 'SQL Injection (db.execute)', severity: 'critical', cwe: 'CWE-89',
|
|
80
80
|
remediation: 'Use parameterized queries: db.execute("SELECT * FROM t WHERE id = ?", [id]).' } },
|
|
81
81
|
// OS command.
|
|
82
|
-
{ kind: 'sink', id: 'js-exec', language: 'js', framework: 'node', match: { type: 'call', callee: 'exec'
|
|
82
|
+
{ kind: 'sink', id: 'js-exec', language: 'js', framework: 'node', match: { type: 'call', callee: 'exec', receiverTypeExclude: ['^RegExp$'] }, argIndex: 0,
|
|
83
83
|
vuln: { name: 'Command Injection (child_process.exec)', severity: 'critical', cwe: 'CWE-78',
|
|
84
84
|
remediation: 'Use execFile or spawn with an argv array instead of exec — exec invokes the shell. If shell features are required, escape with shell-escape, never string-concat user input.' } },
|
|
85
85
|
{ kind: 'sink', id: 'js-execSync', language: 'js', framework: 'node', match: { type: 'call', callee: 'execSync' }, argIndex: 0,
|
|
@@ -2381,6 +2381,13 @@ function _receiverAllowed(entry, calleeExpr) {
|
|
|
2381
2381
|
// vocabulary. Over-matching here is the safe direction: it only ever
|
|
2382
2382
|
// *permits* a match the pattern layer already made.
|
|
2383
2383
|
function _receiverTypeAllowed(entry, receiverType) {
|
|
2384
|
+
// `receiverTypeExclude` is the NEGATIVE counterpart of `receiverTypeIn`: a
|
|
2385
|
+
// confidently resolved receiver type that matches it removes the entry. An
|
|
2386
|
+
// unresolved type (null) never excludes, so the conservative behaviour is
|
|
2387
|
+
// unchanged whenever the engine cannot tell. `js-exec` uses it to drop a
|
|
2388
|
+
// RegExp's `.exec()`, which shares its method name with child_process.exec.
|
|
2389
|
+
const ex = entry.match && entry.match.receiverTypeExclude;
|
|
2390
|
+
if (ex && ex.length && receiverType && ex.some((p) => new RegExp(p).test(String(receiverType)))) return false;
|
|
2384
2391
|
const pats = entry.match && entry.match.receiverTypeIn;
|
|
2385
2392
|
if (!pats || !pats.length) return true;
|
|
2386
2393
|
if (!receiverType) return true;
|
|
@@ -2638,8 +2645,21 @@ export function matchSource(expr, file, receiverType) {
|
|
|
2638
2645
|
return null;
|
|
2639
2646
|
}
|
|
2640
2647
|
|
|
2648
|
+
// A receiver whose type is evident from the expression itself, with no
|
|
2649
|
+
// binding to resolve: a regex literal (`/x/.exec(s)`) or a direct construction
|
|
2650
|
+
// (`new RegExp(p).exec(s)`). Returns 'RegExp' or null. Callers that can also
|
|
2651
|
+
// resolve a bound variable pass their own `receiverType`, which wins.
|
|
2652
|
+
function _inlineReceiverType(calleeExpr) {
|
|
2653
|
+
const o = calleeExpr && typeof calleeExpr === 'object' && calleeExpr.kind === 'member' ? calleeExpr.object : null;
|
|
2654
|
+
if (!o) return null;
|
|
2655
|
+
if (o.kind === 'literal' && o.isRegex) return 'RegExp';
|
|
2656
|
+
if (o.kind === 'call' && o.callee && o.callee.kind === 'ident' && o.callee.name === 'RegExp') return 'RegExp';
|
|
2657
|
+
return null;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2641
2660
|
export function matchSinkOrSanitizer(calleeExpr, file, receiverType) {
|
|
2642
2661
|
if (!calleeExpr) return null;
|
|
2662
|
+
receiverType = receiverType || _inlineReceiverType(calleeExpr);
|
|
2643
2663
|
const raw = _calleeIndexHits(calleeExpr);
|
|
2644
2664
|
if (!raw.length) return null;
|
|
2645
2665
|
const hits = filterByProvenance(raw)
|
package/src/dataflow/engine.js
CHANGED
|
@@ -166,6 +166,11 @@ function _fullyFlattenMemberChain(calleeExpr) {
|
|
|
166
166
|
// "unknown", never as a signal to suppress or refuse (see this file's
|
|
167
167
|
// "Unknown ≠ clean" global constraint).
|
|
168
168
|
function _receiverTypeFor(calleeExpr, callContext) {
|
|
169
|
+
// A bare identifier receiver that every binding in this file initialises to a
|
|
170
|
+
// regex is a RegExp (see _regexNamesForFile). Checked before CHA, which
|
|
171
|
+
// cannot see a module-scope const from inside the handler that uses it.
|
|
172
|
+
if (calleeExpr && calleeExpr.kind === 'member' && calleeExpr.object && calleeExpr.object.kind === 'ident'
|
|
173
|
+
&& _currentFile && _regexNamesByFile.get(_currentFile)?.has(calleeExpr.object.name)) return 'RegExp';
|
|
169
174
|
if (!callContext || !callContext._cha) return null;
|
|
170
175
|
const flat = _fullyFlattenMemberChain(calleeExpr);
|
|
171
176
|
if (!flat || !flat.includes('.')) return null;
|
|
@@ -197,6 +202,45 @@ function _receiverTypeFor(calleeExpr, callContext) {
|
|
|
197
202
|
return classOfVar(callContext._cha, _currentFile, callContext._currentFnQid, parts[0]);
|
|
198
203
|
}
|
|
199
204
|
|
|
205
|
+
// File -> Set of identifier names that are provably RegExp objects: the name
|
|
206
|
+
// is assigned at least once, EVERY assignment to it anywhere in the file (any
|
|
207
|
+
// function, any scope, including module scope) is a regex literal or a
|
|
208
|
+
// `RegExp(...)` / `new RegExp(...)` construction, and it is never a function
|
|
209
|
+
// parameter. Name-based and file-wide on purpose: the IR has no scope
|
|
210
|
+
// resolution, so a name that is ever bound to anything else is treated as
|
|
211
|
+
// unknown, which keeps the catalog's conservative behaviour. Used so
|
|
212
|
+
// `SEMVER.exec(s)` is not read as child_process.exec. Residual gap: a name
|
|
213
|
+
// bound by an import or a destructuring that lowers to no assign node, AND
|
|
214
|
+
// also assigned a regex in an unrelated scope of the same file.
|
|
215
|
+
const _regexNamesByFile = new Map();
|
|
216
|
+
|
|
217
|
+
function _isRegexInit(src) {
|
|
218
|
+
if (!src || typeof src !== 'object') return false;
|
|
219
|
+
if (src.kind === 'literal' && src.isRegex) return true;
|
|
220
|
+
return src.kind === 'call' && !!src.callee && src.callee.kind === 'ident' && src.callee.name === 'RegExp';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function _buildRegexNames(fns) {
|
|
224
|
+
_regexNamesByFile.clear();
|
|
225
|
+
const byFile = new Map(); // file -> Map(name -> boolean allRegex)
|
|
226
|
+
for (const fn of fns) {
|
|
227
|
+
if (!fn || !fn.file || !fn.cfg || !fn.cfg.nodes) continue;
|
|
228
|
+
let names = byFile.get(fn.file);
|
|
229
|
+
if (!names) { names = new Map(); byFile.set(fn.file, names); }
|
|
230
|
+
for (const p of fn.params || []) if (typeof p === 'string') names.set(p, false);
|
|
231
|
+
for (const n of Object.values(fn.cfg.nodes)) {
|
|
232
|
+
if (!n || n.kind !== 'assign' || typeof n.target !== 'string' || n.target.includes('.')) continue;
|
|
233
|
+
const prev = names.get(n.target);
|
|
234
|
+
names.set(n.target, (prev === undefined || prev === true) && _isRegexInit(n.source));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
for (const [file, names] of byFile) {
|
|
238
|
+
const ok = new Set();
|
|
239
|
+
for (const [name, allRegex] of names) if (allRegex) ok.add(name);
|
|
240
|
+
if (ok.size) _regexNamesByFile.set(file, ok);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
200
244
|
// Narrower than _flattenCalleeName: the name to hand to callGraph.resolve().
|
|
201
245
|
// Only a bare identifier call (`helper()`) — or a pre-flattened STRING, which
|
|
202
246
|
// is how the Go/PHP/Ruby/Python/C++ parsers already emit call targets —
|
|
@@ -1987,7 +2031,7 @@ function analyzeFunction(fn, entryState, callContext) {
|
|
|
1987
2031
|
for (const [nid, ctx] of ictx) {
|
|
1988
2032
|
const node = nodes[nid];
|
|
1989
2033
|
if (!node || node.kind !== 'call') continue;
|
|
1990
|
-
const cat = matchSinkOrSanitizer(node.callee, _currentFile);
|
|
2034
|
+
const cat = matchSinkOrSanitizer(node.callee, _currentFile, _receiverTypeFor(node.callee, callContext));
|
|
1991
2035
|
const sink = cat && cat.find((e) => e.kind === 'sink');
|
|
1992
2036
|
if (!sink) continue;
|
|
1993
2037
|
const inS = inStates.get(nid) || new Set();
|
|
@@ -2001,7 +2045,7 @@ function analyzeFunction(fn, entryState, callContext) {
|
|
|
2001
2045
|
if (implicitState.size) {
|
|
2002
2046
|
for (const [nid, node] of Object.entries(nodes)) {
|
|
2003
2047
|
if (!node || node.kind !== 'call') continue;
|
|
2004
|
-
const cat = matchSinkOrSanitizer(node.callee, _currentFile);
|
|
2048
|
+
const cat = matchSinkOrSanitizer(node.callee, _currentFile, _receiverTypeFor(node.callee, callContext));
|
|
2005
2049
|
const sink = cat && cat.find((e) => e.kind === 'sink');
|
|
2006
2050
|
if (!sink) continue;
|
|
2007
2051
|
const inS = inStates.get(nid) || new Set();
|
|
@@ -2109,6 +2153,7 @@ export function runTaintEngine(perFileIR, callGraph, opts = {}) {
|
|
|
2109
2153
|
const fnList = [...callGraph.functions.values()].sort((a, b) =>
|
|
2110
2154
|
a.qid < b.qid ? -1 : a.qid > b.qid ? 1 : 0
|
|
2111
2155
|
);
|
|
2156
|
+
_buildRegexNames(fnList);
|
|
2112
2157
|
// PRD W0.1 (SARD_80_F1_EXECUTION_PRD.md) — the main per-function loop below
|
|
2113
2158
|
// silently `break`s once `n > fnLimit` (default 5000), which for a single
|
|
2114
2159
|
// large SARD CWE directory (Java CWE-89 alone: 3668 files, 17604
|
package/src/sandbox/CLAUDE.md
CHANGED
|
@@ -281,9 +281,9 @@ privilegeDrop`), the same mechanism `limits.js` uses for an unenforceable
|
|
|
281
281
|
limit. It is never silently skipped, and the escape test that covers the
|
|
282
282
|
rebind attack fails if it is missing rather than quietly passing.
|
|
283
283
|
|
|
284
|
-
**
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
**Executed on a hosted Linux runner.** The `sandbox-linux` job ran the suite
|
|
285
|
+
(see the verification sections below); this section describes code that has
|
|
286
|
+
been exercised, on that runner image and kernel only.
|
|
287
287
|
|
|
288
288
|
## Timeout does not kill the process tree
|
|
289
289
|
|
|
@@ -297,18 +297,24 @@ inside the policy profile — their writes and egress remain confined — but th
|
|
|
297
297
|
are still running and still consuming resources. A caller that needs a hard
|
|
298
298
|
tree kill must implement it.
|
|
299
299
|
|
|
300
|
-
The namespace backend
|
|
301
|
-
|
|
302
|
-
namespace
|
|
303
|
-
|
|
304
|
-
|
|
300
|
+
The namespace backend closes this with the kernel, not a sweep: the command
|
|
301
|
+
runs under `--pid --fork --kill-child`, so when the `unshare` supervisor dies
|
|
302
|
+
the namespace's init gets SIGKILL and the kernel kills every member, including
|
|
303
|
+
a double-forked `setsid` descendant that no process-group or sweep logic can
|
|
304
|
+
find. `--kill-child` is used only where `unshare` advertises it
|
|
305
|
+
(`namespaceTreeKill`); without it supervised execution is refused. Proved by
|
|
306
|
+
the `linux-probes.js` probes on the hosted runner (kill the supervisor, kill
|
|
307
|
+
the init directly). Earlier text here said killing the direct child should reap
|
|
308
|
+
the tree; CI showed it did not (the killed process was the supervisor, not the
|
|
309
|
+
init) until `--kill-child` was added.
|
|
305
310
|
|
|
306
311
|
## Known limitation, deliberately accepted: reads are not confined
|
|
307
312
|
|
|
308
313
|
The userspace policy allows `(allow file-read*)` globally — that backend
|
|
309
314
|
confines **writes**, network egress, and resource use, but **not reads**. (The
|
|
310
|
-
kernel-namespace backend has the same cut
|
|
311
|
-
tree is rebound read-only, not detached, so everything on it stays readable.
|
|
315
|
+
kernel-namespace backend has the same cut in its DEFAULT mode: its mount
|
|
316
|
+
tree is rebound read-only, not detached, so everything on it stays readable.
|
|
317
|
+
Its capability mode, entered with `readRoots`, does confine reads; see below.)
|
|
312
318
|
A confined command can read any file on the host the OS-level
|
|
313
319
|
permissions allow, including outside the sandbox root. Exfiltration of
|
|
314
320
|
readable host files (writing what was read to network or to a location the
|
|
@@ -364,20 +370,26 @@ then runs supervised with protected paths read-denied.
|
|
|
364
370
|
default protected set is the key directory, common credential directories
|
|
365
371
|
under the home directory, plus caller-supplied sealed-label and evidence
|
|
366
372
|
directories. A denylist is a floor, not a claim of host-wide read isolation.
|
|
367
|
-
The namespace backend
|
|
368
|
-
|
|
373
|
+
The namespace backend implements it by masking: an empty tmpfs over a
|
|
374
|
+
protected directory, `/dev/null` over a protected file (in capability mode a
|
|
375
|
+
protected path outside every declared root is simply absent). Proved by the
|
|
376
|
+
protected-canary probes on the hosted runner.
|
|
369
377
|
- **Process trees** (`supervise.js`): own process group, periodic descendant
|
|
370
378
|
sweep, SIGTERM, grace, SIGKILL, then a poll that confirms no survivors; the
|
|
371
379
|
same cleanup runs on a clean exit. Cancel is an `AbortSignal` (status
|
|
372
380
|
`cancelled`). Output is capped. Known gap: a double-fork plus setsid between
|
|
373
381
|
two sweeps escapes; only a PID namespace or cgroup closes it. Supervised
|
|
374
|
-
execution exists
|
|
375
|
-
|
|
382
|
+
execution exists for the userspace backend and for the namespace backend
|
|
383
|
+
where `--kill-child` is available (see above).
|
|
376
384
|
- **Active probes**: write confinement, read denial, env scrub, network, tree
|
|
377
385
|
termination and the file-size limit each run an attack plus a positive
|
|
378
386
|
control through the real backend. `process-cap` is reported `unverified` on
|
|
379
|
-
every backend and must not be claimed enforced
|
|
380
|
-
macOS
|
|
387
|
+
every backend and must not be claimed enforced. Per-uid and system-wide on
|
|
388
|
+
macOS. On Linux the cause of the earlier non-refusal is now known: the
|
|
389
|
+
resource prelude is run by `/bin/sh`, which is dash on the hosted runner, and
|
|
390
|
+
dash's `ulimit` has no `-u` option (`ulimit: Illegal option -u` appears in
|
|
391
|
+
the `sandbox-linux` log), so the cap was never applied at all. No replacement
|
|
392
|
+
(for example `prlimit`) has been proved, so the cap stays unasserted.
|
|
381
393
|
`capabilityReport()` carries the standing note that Linux enforcement is
|
|
382
394
|
evidenced only where the `sandbox-linux` CI job ran the same probes.
|
|
383
395
|
- **Status**: target output is returned labelled `untrusted` and is never parsed
|
|
@@ -396,7 +408,27 @@ then runs supervised with protected paths read-denied.
|
|
|
396
408
|
- `writeRoots`: further writable subtrees (readable too). `cwd`: working directory, which must exist.
|
|
397
409
|
- `networkProxyPort`: opens exactly one loopback port (`(remote ip "localhost:PORT")`); DNS, every other port and every other address stay denied.
|
|
398
410
|
|
|
399
|
-
Verified by execution on macOS only (`test/capabilities/`, probes in `capabilities/probes.js`). The namespace backend implements
|
|
411
|
+
Verified by execution on macOS only (`test/capabilities/`, probes in `capabilities/probes.js`). The namespace backend implements `readRoots`, `writeRoots`, `cwd` and
|
|
412
|
+
`denyReadPaths` as **capability mode** (entered when `readRoots` is an array): a
|
|
413
|
+
tmpfs root holding only the runtime baseline (`/usr`, `/bin`, `/lib*`, a few
|
|
414
|
+
identity files, a fresh `/proc` and a minimal `/dev`) plus the declared roots,
|
|
415
|
+
read-only for read roots and read-write for write roots, protected paths absent
|
|
416
|
+
or masked, then `pivot_root` and a detached old root (`linux-rootfs.js` builds
|
|
417
|
+
the plan and the setup script; `backend-namespace.js` runs it). Capability drop
|
|
418
|
+
(`setpriv`, with no-new-privs) is required in this mode, not best effort.
|
|
419
|
+
`networkProxyPort` is REFUSED: mediated network is not implemented on Linux, so
|
|
420
|
+
a task that declares a destination is blocked there.
|
|
421
|
+
|
|
422
|
+
### Linux evidence
|
|
423
|
+
|
|
424
|
+
`linux-probes.js` and the generic probes in `control-probes.js` and
|
|
425
|
+
`capabilities/probes.js` attack each control and pair it with a positive
|
|
426
|
+
control; `test/capabilities/linux-enforcement.test.js` also runs every probe
|
|
427
|
+
against a deliberately wrong runner (fault injection) and requires it to say so.
|
|
428
|
+
`scripts/sandbox-linux-verify.mjs` (the `sandbox-linux` job) fails if a required
|
|
429
|
+
suite skips, a required control is not proved, or a control that must stay
|
|
430
|
+
unasserted claims to be proved, and uploads the evidence table. Exploratory runs
|
|
431
|
+
on a local aarch64 VM are not evidence; only the hosted x86_64 job is.
|
|
400
432
|
|
|
401
433
|
## Extending this module
|
|
402
434
|
|