@clear-capabilities/agentic-security-scanner 0.152.0 → 0.153.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 +54 -0
- package/dist/1336.index.js +16010 -0
- package/dist/agentic-security.mjs +3 -3
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +4 -3
- package/src/dataflow/backward.js +104 -7
- package/src/dataflow/cross-service-taint.js +154 -11
- package/src/dataflow/engine.js +23 -1
- package/src/dataflow/exploit-prover.js +111 -23
- package/src/dataflow/index.js +12 -2
- package/src/dataflow/string-domain.js +36 -0
- package/src/engine.js +10 -6
- package/src/ir/class-hierarchy.js +160 -1
- package/src/report/index.js +12 -0
- package/src/dataflow/smt-feasibility.js +0 -189
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
1e727ef20f7fd2c897af4598b51db75771cc0b75e07dd00a30bd840d34736b10 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.153.0",
|
|
4
4
|
"description": "Scanner engine for the agentic-security Claude Code plugin — SAST, SCA (function-level reachability + CISA KEV), secrets, IaC, prompt-injection, MCP/agent-tool audit, auth/authZ deep analysis, attack chains, PoC generation, business logic, toxic-combinations scoring, SBOM, pipeline integrity, compliance attestation, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
},
|
|
49
49
|
"optionalDependencies": {
|
|
50
50
|
"tree-sitter-wasms": "0.1.13",
|
|
51
|
-
"web-tree-sitter": "0.20.8"
|
|
51
|
+
"web-tree-sitter": "0.20.8",
|
|
52
|
+
"z3-solver": "^5.2.0"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"@types/node": "^26.5.1",
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
"test:glob": "node --test test/glob-compat.test.js",
|
|
71
72
|
"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",
|
|
72
73
|
"test:posture": "node --test test/threat-model.test.js test/git-hardening.test.js test/privacy-framework.test.js test/material-change.test.js test/drift.test.js test/scorecard.test.js test/accuracy-scorecard.test.js test/scorecard-gate.test.js test/predeploy-gate.test.js test/release-check.test.js test/pre-push-gate.test.js test/dependency-currency.test.js test/package-contents-check.test.js test/gate-verdict-cache.test.js test/run-unit-tests.test.js test/release-workflow.test.js test/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-bundle.test.js test/obligation-evidence-pack.test.js test/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-policy-completeness.test.js test/egress-policy-integration.test.js test/router.test.js test/legacy-alias-redirect.test.js test/cache-economics.test.js test/coverage-report.test.js test/corpus-status.test.js test/provenance.test.js test/secret-history.test.js test/execution-proof.test.js test/fix-metrics.test.js test/corpus-enroll.test.js test/prove-findings.test.js test/corpus-match.test.js test/corpus-provenance.test.js test/learning-quorum.test.js test/llm-cache-integrity.test.js test/proof-artifact.test.js test/providers.test.js test/fleet.test.js test/autopilot.test.js test/autopilot-cli.test.js test/verifier-independence.test.js test/integrity-legacy-key.test.js test/suppression-visibility.test.js test/cost-ceiling.test.js test/local-endpoint.test.js test/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",
|
|
73
|
-
"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",
|
|
74
|
+
"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",
|
|
74
75
|
"test:mcp": "node --test test/mcp.test.js test/mcp-protocol-smoke.test.js test/mcp-audit.test.js test/audit-cli.test.js test/mcp-scratchpad.test.js test/mcp-offload.test.js test/sca-upgrade.test.js test/lsp-server.test.js test/mcp-dataflow-tools.test.js test/cli/governance-propose-edit.test.js test/cli/remediation-open-update.test.js test/cli/remediation-verify-reopen.test.js test/cli/dataflow-observations.test.js test/cli/federate-declare-list.test.js",
|
|
75
76
|
"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",
|
|
76
77
|
"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",
|
package/src/dataflow/backward.js
CHANGED
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
// callers (BFS-bounded) to keep the slicer fast.
|
|
30
30
|
|
|
31
31
|
import { accessPathOf, pathIsCoveredByPrefix } from './access-paths.js';
|
|
32
|
+
import { matchSinkOrSanitizer } from './catalog.js';
|
|
33
|
+
import { safeSanitizerOutputRegex as _safeSanitizerOutputRegex } from './string-domain.js';
|
|
34
|
+
import { _collectExprVars } from './engine.js';
|
|
32
35
|
|
|
33
36
|
const SLICE_BUDGET_NODES = 200;
|
|
34
37
|
const SLICE_BUDGET_CALLERS = 5;
|
|
@@ -55,10 +58,20 @@ function _reverseEdges(cfg) {
|
|
|
55
58
|
*
|
|
56
59
|
* Returns an ordered list of trace steps (source-first):
|
|
57
60
|
* [
|
|
58
|
-
* { line, kind: 'source',
|
|
59
|
-
* { line, kind: 'assign',
|
|
60
|
-
* { line, kind: '
|
|
61
|
-
*
|
|
61
|
+
* { line, kind: 'source', label, varName, path },
|
|
62
|
+
* { line, kind: 'assign', from, to, path },
|
|
63
|
+
* { line, kind: 'sanitize', callee, sanitizerId, path }, // a RECOGNIZED
|
|
64
|
+
* // catalog sanitizer
|
|
65
|
+
* // applied on the path
|
|
66
|
+
* { line, kind: 'call', callee, sanitizerId: null, path }, // any OTHER
|
|
67
|
+
* // call — best-effort
|
|
68
|
+
* // continuity only;
|
|
69
|
+
* // a sound consumer
|
|
70
|
+
* // (e.g. the SMT
|
|
71
|
+
* // encoder) must NOT
|
|
72
|
+
* // treat this as a
|
|
73
|
+
* // known transform
|
|
74
|
+
* { line, kind: 'sink', callee, argIndex, path },
|
|
62
75
|
* ]
|
|
63
76
|
*/
|
|
64
77
|
export function sliceBackward(fn, sinkNode, sinkArgPath) {
|
|
@@ -127,6 +140,63 @@ export function sliceBackward(fn, sinkNode, sinkArgPath) {
|
|
|
127
140
|
}
|
|
128
141
|
// Otherwise, follow the def of the new query path upstream.
|
|
129
142
|
work.push({ nid, queryPath: srcAp });
|
|
143
|
+
} else if (node.source && node.source.kind === 'call') {
|
|
144
|
+
// Next-gen taint capability #5 (SMT path feasibility rebuild):
|
|
145
|
+
// `accessPathOf` returns null for a CALL expression by design (it
|
|
146
|
+
// only handles pure ident/member chains), so `const clean =
|
|
147
|
+
// sanitize(x); sink(clean);` — arguably the single most common
|
|
148
|
+
// real-world sanitizer idiom — previously dead-ended the slice
|
|
149
|
+
// right here with no 'source' step ever appended: `srcAp` is null,
|
|
150
|
+
// so neither branch above fires, and the bare "walk predecessors
|
|
151
|
+
// regardless" loop below can only ever find a DIFFERENT def of the
|
|
152
|
+
// SAME queryPath, never continue into the call's own argument.
|
|
153
|
+
// Fixed by treating a call-shaped RHS as a real, tracked step: a
|
|
154
|
+
// recognized CATALOG SANITIZER gets its own 'sanitize' kind (the
|
|
155
|
+
// SMT encoder below trusts ONLY this shape, via the sanitizer's
|
|
156
|
+
// proven output-regex, to constrain a value); anything else gets a
|
|
157
|
+
// generic 'call' kind, for explainability continuity only — the
|
|
158
|
+
// SMT encoder must refuse to build a sound proof across an opaque
|
|
159
|
+
// call it cannot reason about, never treat it as either a
|
|
160
|
+
// transparent passthrough or an implicit sanitizer. Deliberately
|
|
161
|
+
// single-argument, intraprocedural only, matching this whole
|
|
162
|
+
// module's own documented v1 scope: continues into the FIRST
|
|
163
|
+
// argument carrying a resolvable access path, not every argument
|
|
164
|
+
// (a sanitizer/helper's OTHER arguments are typically
|
|
165
|
+
// configuration, e.g. `escape(x, {mode: 'html'})`, not additional
|
|
166
|
+
// taint sources).
|
|
167
|
+
const callee = node.source.callee;
|
|
168
|
+
const args = Array.isArray(node.source.args) ? node.source.args : [];
|
|
169
|
+
let argPath = null;
|
|
170
|
+
for (const a of args) { const p = accessPathOf(a); if (p) { argPath = p; break; } }
|
|
171
|
+
const hits = matchSinkOrSanitizer(callee, fn.file);
|
|
172
|
+
const sanitizerHit = Array.isArray(hits) ? hits.find(h => h.kind === 'sanitizer') : null;
|
|
173
|
+
const calleeName = typeof callee === 'string' ? callee
|
|
174
|
+
: callee && callee.kind === 'ident' ? callee.name
|
|
175
|
+
: callee && callee.kind === 'member' && typeof callee.prop === 'string' ? callee.prop
|
|
176
|
+
: null;
|
|
177
|
+
// `outputRegexSource` is a SEPARATE, narrower signal than
|
|
178
|
+
// `sanitizerId` above: the broad 706-entry catalog match is fine
|
|
179
|
+
// for EXPLAINABILITY (labelling a step "this looks like a
|
|
180
|
+
// sanitizer call" for a human reading the trace), but it matches
|
|
181
|
+
// by bare callee NAME — a project-local function that merely
|
|
182
|
+
// happens to be named the same as a cataloged sanitizer (e.g. a
|
|
183
|
+
// local `sanitize()` helper that does something unrelated) would
|
|
184
|
+
// match it too. A soundness-critical consumer (the SMT
|
|
185
|
+
// infeasibility check) must never treat that broad match as
|
|
186
|
+
// license to assume a specific, provable output shape — only
|
|
187
|
+
// `safeSanitizerOutputRegex`'s own small, hand-vetted set of
|
|
188
|
+
// unambiguous global builtins earns that trust.
|
|
189
|
+
const outputRegexSource = calleeName ? _safeSanitizerOutputRegex(calleeName) : null;
|
|
190
|
+
out.push({
|
|
191
|
+
line: node.line || 0,
|
|
192
|
+
kind: sanitizerHit ? 'sanitize' : 'call',
|
|
193
|
+
callee: calleeName,
|
|
194
|
+
sanitizerId: sanitizerHit ? sanitizerHit.id : null,
|
|
195
|
+
outputRegexSource: outputRegexSource ? outputRegexSource.source : null,
|
|
196
|
+
outputRegexFlags: outputRegexSource ? outputRegexSource.flags : null,
|
|
197
|
+
path: queryPath,
|
|
198
|
+
});
|
|
199
|
+
if (argPath) work.push({ nid, queryPath: argPath });
|
|
130
200
|
}
|
|
131
201
|
}
|
|
132
202
|
|
|
@@ -178,9 +248,36 @@ export function annotateBackwardSlices(findings, perFileIR, callGraph) {
|
|
|
178
248
|
}
|
|
179
249
|
if (!sinkNode) { skipped++; continue; }
|
|
180
250
|
// The tainted arg path — derive from the tainted argument's expression.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
251
|
+
// `f.argIndex` can be a real number, the string 'all' (any arg tainted —
|
|
252
|
+
// no single index to point at) or 'rhs' (a member-write sink) — only a
|
|
253
|
+
// plain numeric index names one real argument to slice from.
|
|
254
|
+
const argExprs = sinkNode.args || [];
|
|
255
|
+
const taintedArg = typeof f.argIndex === 'number' ? argExprs[f.argIndex] : undefined;
|
|
256
|
+
let argPath = accessPathOf(taintedArg);
|
|
257
|
+
let slice = argPath ? sliceBackward(fn, sinkNode, argPath) : null;
|
|
258
|
+
// Next-gen taint capability #5 (SMT path-feasibility rebuild):
|
|
259
|
+
// `accessPathOf` only ever succeeds for a BARE ident/member chain — the
|
|
260
|
+
// single most common real shape, a sink argument built by string
|
|
261
|
+
// concatenation (`'SELECT ...' + id`, a template literal, string.format
|
|
262
|
+
// args, …), is a `binary`/`tpl`/`call` expression, so `accessPathOf`
|
|
263
|
+
// returns null and the slice used to fall back to the unmatchable
|
|
264
|
+
// placeholder string `arg[${f.argIndex}]` — no real variable is ever
|
|
265
|
+
// named that, so `sliceBackward` could only ever return the bare sink
|
|
266
|
+
// node, structurally regardless of AGENTIC_SECURITY_BACKWARD_SLICE.
|
|
267
|
+
// Fixed by extracting every free variable/access-path referenced
|
|
268
|
+
// ANYWHERE in the tainted argument expression (the same tree-walker
|
|
269
|
+
// `engine.js`'s own `_sourcesReachingExpr` uses to attribute a finding
|
|
270
|
+
// to its real source) and trying each one in turn, keeping the first
|
|
271
|
+
// that yields a genuine (more-than-just-the-sink) slice.
|
|
272
|
+
if ((!slice || slice.length <= 1) && taintedArg) {
|
|
273
|
+
const vars = new Set();
|
|
274
|
+
_collectExprVars(taintedArg, vars);
|
|
275
|
+
for (const candidate of vars) {
|
|
276
|
+
const candidateSlice = sliceBackward(fn, sinkNode, candidate);
|
|
277
|
+
if (candidateSlice && candidateSlice.length > 1) { slice = candidateSlice; break; }
|
|
278
|
+
if (!slice) slice = candidateSlice;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
184
281
|
if (slice && slice.length) {
|
|
185
282
|
f.backwardSlice = slice;
|
|
186
283
|
f.pathSteps = (f.pathSteps || []).concat(slice.map(s => ({
|
|
@@ -39,6 +39,24 @@
|
|
|
39
39
|
// either a monorepo or a federated-scan API. We DO emit cross-service
|
|
40
40
|
// findings when the local service is on the receiving end of an edge,
|
|
41
41
|
// based on the declared upstream taint contract.
|
|
42
|
+
//
|
|
43
|
+
// SCOPE NOTE (next-gen taint capability #7 rebuild): step 1 of the
|
|
44
|
+
// algorithm above ("mark every consume entry-point as tainted-by-default")
|
|
45
|
+
// is NOT implemented anywhere in this file, or anywhere else in the
|
|
46
|
+
// engine — there is no mechanism that seeds a NEW taint source from a
|
|
47
|
+
// `consumes` declaration (e.g. marking a Kafka consumer callback's
|
|
48
|
+
// message parameter as tainted). This module only ANNOTATES findings the
|
|
49
|
+
// taint engine already discovers via its own normal source catalog
|
|
50
|
+
// (`catalog.js`) with cross-service context; it never makes a
|
|
51
|
+
// previously-invisible flow visible. In practice this means an HTTP
|
|
52
|
+
// edge benefits fully (HTTP request sources — body/query/header/cookie —
|
|
53
|
+
// are extensively cataloged already), while a Kafka/queue edge benefits
|
|
54
|
+
// only when the consumed payload happens to ALSO reach an already-
|
|
55
|
+
// cataloged source through some other path — which is rare, since raw
|
|
56
|
+
// message-queue payloads have no catalog entry of their own. Seeding new
|
|
57
|
+
// sources from `consumes` declarations would be a genuinely separate,
|
|
58
|
+
// larger capability; left as a deliberate, documented boundary here
|
|
59
|
+
// rather than attempted in this rebuild.
|
|
42
60
|
|
|
43
61
|
import * as fs from 'node:fs';
|
|
44
62
|
import * as path from 'node:path';
|
|
@@ -116,6 +134,52 @@ export function incomingEdges(graph, currentService) {
|
|
|
116
134
|
return (graph.edges || []).filter(e => e.to === currentService.name);
|
|
117
135
|
}
|
|
118
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Split a URL path into its non-empty segments, e.g. "/balances/:id" ->
|
|
139
|
+
* ["balances", ":id"].
|
|
140
|
+
*/
|
|
141
|
+
function _pathSegments(p) {
|
|
142
|
+
return String(p || '').split('/').filter(Boolean);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* True when a path segment is a parameter placeholder, in EITHER of the
|
|
147
|
+
* two conventions this file's own schema comment documents:
|
|
148
|
+
* Express/Koa-style `:userId` or template-style `{userId}`.
|
|
149
|
+
*/
|
|
150
|
+
function _isWildcardSegment(seg) {
|
|
151
|
+
return /^:.+$/.test(seg) || /^\{[^}]+\}$/.test(seg);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Structural path-template match between an `exposes[].route` entry
|
|
156
|
+
* (e.g. "GET /balances/:userId") and an `edges[].path` entry (e.g.
|
|
157
|
+
* "/balances/{userId}").
|
|
158
|
+
*
|
|
159
|
+
* The previous implementation did `expose.route.includes(edge.path)` — a
|
|
160
|
+
* crude substring check. This file's OWN header comment documents the
|
|
161
|
+
* two sides using DIFFERENT placeholder syntaxes (`:userId` vs
|
|
162
|
+
* `{userId}`), so that substring check could never succeed on the
|
|
163
|
+
* example it was written to describe — confirmed by direct reproduction
|
|
164
|
+
* against that exact example, which returned zero contracts. Fixed by
|
|
165
|
+
* comparing SEGMENT-BY-SEGMENT: a literal segment must match exactly, a
|
|
166
|
+
* placeholder segment on EITHER side matches any segment on the other
|
|
167
|
+
* (since we don't know the upstream's real parameter name convention
|
|
168
|
+
* without parsing it, and the security question — "does an edge exist
|
|
169
|
+
* pointing at this exact path shape" — doesn't depend on knowing it).
|
|
170
|
+
* The route's leading HTTP method token (if present) is stripped before
|
|
171
|
+
* comparing; method itself is not matched (this schema has no per-edge
|
|
172
|
+
* method field), matching the pre-existing, unaudited behavior.
|
|
173
|
+
*/
|
|
174
|
+
function _pathTemplateMatches(exposeRoute, edgePath) {
|
|
175
|
+
if (!exposeRoute || !edgePath) return false;
|
|
176
|
+
const routePath = String(exposeRoute).replace(/^[A-Z]+\s+/, '');
|
|
177
|
+
const a = _pathSegments(routePath);
|
|
178
|
+
const b = _pathSegments(String(edgePath).split('?')[0]);
|
|
179
|
+
if (a.length !== b.length) return false;
|
|
180
|
+
return a.every((seg, i) => seg === b[i] || _isWildcardSegment(seg) || _isWildcardSegment(b[i]));
|
|
181
|
+
}
|
|
182
|
+
|
|
119
183
|
/**
|
|
120
184
|
* For each consume entry on the current service, locate the upstream
|
|
121
185
|
* `exposes` entry that produces it (matched by path/topic) and return
|
|
@@ -131,7 +195,7 @@ export function upstreamTaintContract(graph, currentService) {
|
|
|
131
195
|
const upstream = graph.services[edge.from];
|
|
132
196
|
if (!upstream) continue;
|
|
133
197
|
for (const expose of upstream.exposes || []) {
|
|
134
|
-
const matches = (edge.via === 'http' && edge.path && expose.route && expose.route
|
|
198
|
+
const matches = (edge.via === 'http' && edge.path && expose.route && _pathTemplateMatches(expose.route, edge.path))
|
|
135
199
|
|| (edge.via === 'kafka' && edge.topic && consume.source === edge.topic);
|
|
136
200
|
if (matches) {
|
|
137
201
|
contracts.push({
|
|
@@ -148,10 +212,82 @@ export function upstreamTaintContract(graph, currentService) {
|
|
|
148
212
|
return contracts;
|
|
149
213
|
}
|
|
150
214
|
|
|
215
|
+
// Escapes every regex metacharacter in a literal field name before it is
|
|
216
|
+
// embedded in a `\b...\b` pattern. The previous version only replaced the
|
|
217
|
+
// FIRST `.` (`field.replace('.', '\\.')`, no `/g` flag) — any OTHER dot in
|
|
218
|
+
// a multi-segment field name (e.g. "a.b.c") was left as a live regex
|
|
219
|
+
// metacharacter matching any character, silently over-broadening the
|
|
220
|
+
// match. Fixed generally, for every metacharacter, not just repeated dots.
|
|
221
|
+
function _escapeRegex(s) {
|
|
222
|
+
return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Provenance values (catalog.js's per-source `provenance` field, e.g.
|
|
226
|
+
// 'http-body' for req.body) that mean "this value arrived over an
|
|
227
|
+
// incoming HTTP request" — the signal used to correlate a finding with a
|
|
228
|
+
// declared `via: 'http'` cross-service edge.
|
|
229
|
+
const HTTP_PROVENANCE = new Set(['http-body', 'url-param', 'path-param', 'header', 'cookie']);
|
|
230
|
+
|
|
231
|
+
// Every provenance value attached anywhere on a finding: the primary
|
|
232
|
+
// source (`f.sourceProvenance`) plus every step in its source chain
|
|
233
|
+
// (`f.chain[].provenance`) — a finding can have more than one source
|
|
234
|
+
// reaching the sink, and any one of them may be the HTTP-originated one.
|
|
235
|
+
function _findingProvenances(f) {
|
|
236
|
+
const provs = new Set();
|
|
237
|
+
if (f && f.sourceProvenance) provs.add(f.sourceProvenance);
|
|
238
|
+
if (f && Array.isArray(f.chain)) {
|
|
239
|
+
for (const c of f.chain) { if (c && c.provenance) provs.add(c.provenance); }
|
|
240
|
+
}
|
|
241
|
+
return provs;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Every textual source LABEL attached anywhere on a finding (the catalog
|
|
245
|
+
// entry's own fixed label, e.g. "req.body" — never the specific accessed
|
|
246
|
+
// property, which the engine does not retain on the finding today). Used
|
|
247
|
+
// only as a best-effort fallback for non-HTTP transports (Kafka/queue),
|
|
248
|
+
// where there is no provenance category to correlate against at all.
|
|
249
|
+
function _findingSourceText(f) {
|
|
250
|
+
const parts = [];
|
|
251
|
+
if (f && f.source && f.source.label) parts.push(f.source.label);
|
|
252
|
+
if (f && Array.isArray(f.chain)) {
|
|
253
|
+
for (const c of f.chain) { if (c && c.label) parts.push(c.label); }
|
|
254
|
+
}
|
|
255
|
+
return parts.join(' ');
|
|
256
|
+
}
|
|
257
|
+
|
|
151
258
|
/**
|
|
152
259
|
* Annotate findings whose source matches a cross-service taint contract.
|
|
153
|
-
* Adds `crossService: { from,
|
|
154
|
-
* severity by one tier (medium → high, high →
|
|
260
|
+
* Adds `crossService: { from, to, via, taintedFields, matchedField,
|
|
261
|
+
* precision }` and bumps severity by one tier (medium → high, high →
|
|
262
|
+
* critical).
|
|
263
|
+
*
|
|
264
|
+
* The previous implementation matched a contract's declared field NAMES
|
|
265
|
+
* as literal text against `f.source.snippet`/`f.source.expr` — fields
|
|
266
|
+
* that confirmed direct code reading never exist on a real finding (the
|
|
267
|
+
* real shape, set in `engine.js`, is `f.source = {file, line, label}`).
|
|
268
|
+
* That made this function a structural no-op for every real taint-engine
|
|
269
|
+
* finding, verified via a direct `runScan()` reproduction of the exact
|
|
270
|
+
* scenario this file's own header describes.
|
|
271
|
+
*
|
|
272
|
+
* Fixed with two correlation strategies, chosen by transport, since a
|
|
273
|
+
* real finding never carries the SPECIFIC field name that reached the
|
|
274
|
+
* sink (only the catalog source's fixed label, e.g. "req.body" — see
|
|
275
|
+
* `_findingSourceText`'s own comment):
|
|
276
|
+
* - `via: 'http'`: correlate by PROVENANCE — an HTTP request source is
|
|
277
|
+
* already reliably categorized by the catalog (`http-body`,
|
|
278
|
+
* `url-param`, `header`, `cookie`, …), so this is a real, sound
|
|
279
|
+
* signal rather than a text-match guess. `matchedField` is left null
|
|
280
|
+
* and `precision: 'transport'` records honestly that the match is at
|
|
281
|
+
* the endpoint level, not a specific declared field — the security
|
|
282
|
+
* conclusion (this value arrived across a declared trust boundary)
|
|
283
|
+
* holds regardless of which exact field it was.
|
|
284
|
+
* - every other transport (Kafka/queue): no provenance category exists
|
|
285
|
+
* for a raw message payload (per this file's own SCOPE NOTE), so the
|
|
286
|
+
* best available signal is literal field-name text matching against
|
|
287
|
+
* whatever source labels the finding does carry — kept as a
|
|
288
|
+
* best-effort fallback, `precision: 'field'` when it hits, but this
|
|
289
|
+
* will rarely fire until source-seeding from `consumes` declarations
|
|
290
|
+
* exists (documented as a separate, not-yet-attempted capability).
|
|
155
291
|
*/
|
|
156
292
|
export function annotateCrossServiceFindings(findings, graph, currentService) {
|
|
157
293
|
if (!Array.isArray(findings) || !graph || !currentService) return { annotated: 0, bumped: 0 };
|
|
@@ -159,18 +295,25 @@ export function annotateCrossServiceFindings(findings, graph, currentService) {
|
|
|
159
295
|
if (!contracts.length) return { annotated: 0, bumped: 0 };
|
|
160
296
|
let annotated = 0, bumped = 0;
|
|
161
297
|
for (const f of findings) {
|
|
162
|
-
const sourceExpr = (f.source && (f.source.snippet || f.source.expr)) || f.snippet || '';
|
|
163
298
|
for (const contract of contracts) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
299
|
+
let matchedField = null;
|
|
300
|
+
let matched = false;
|
|
301
|
+
if (contract.via === 'http') {
|
|
302
|
+
const provs = _findingProvenances(f);
|
|
303
|
+
matched = [...provs].some(p => HTTP_PROVENANCE.has(p));
|
|
304
|
+
} else {
|
|
305
|
+
const text = _findingSourceText(f);
|
|
306
|
+
matchedField = contract.taintedFields.find(field => new RegExp(`\\b${_escapeRegex(field)}\\b`).test(text)) || null;
|
|
307
|
+
matched = !!matchedField;
|
|
308
|
+
}
|
|
309
|
+
if (!matched) continue;
|
|
169
310
|
f.crossService = {
|
|
170
311
|
from: contract.upstreamService,
|
|
171
312
|
to: currentService.name,
|
|
172
313
|
via: contract.via,
|
|
173
|
-
|
|
314
|
+
taintedFields: contract.taintedFields,
|
|
315
|
+
matchedField,
|
|
316
|
+
precision: matchedField ? 'field' : 'transport',
|
|
174
317
|
};
|
|
175
318
|
annotated++;
|
|
176
319
|
// Severity bump.
|
|
@@ -199,4 +342,4 @@ export function runCrossServiceTaint(scanRoot, findings) {
|
|
|
199
342
|
return annotateCrossServiceFindings(findings, graph, current);
|
|
200
343
|
}
|
|
201
344
|
|
|
202
|
-
export const _internals = { _normalizeGraph, SERVICES_FILE_NAMES };
|
|
345
|
+
export const _internals = { _normalizeGraph, SERVICES_FILE_NAMES, _pathTemplateMatches, _escapeRegex };
|
package/src/dataflow/engine.js
CHANGED
|
@@ -547,7 +547,15 @@ function exprTaint(expr, state, callContext) {
|
|
|
547
547
|
// Collects the variable / access-path roots referenced by `expr` and returns
|
|
548
548
|
// the _taintSources entries whose varName matches one of those roots. This
|
|
549
549
|
// replaces "first source we ever saw" with "sources tied to this argument."
|
|
550
|
-
|
|
550
|
+
// Exported (next-gen taint capability #5, SMT path-feasibility rebuild) so
|
|
551
|
+
// backward.js's `annotateBackwardSlices` can reuse the SAME free-variable
|
|
552
|
+
// extraction for a COMPOUND tainted-sink-argument expression (`'SELECT...'
|
|
553
|
+
// + id`, not a bare identifier) — `accessPathOf` alone returns null for
|
|
554
|
+
// anything but a pure ident/member chain, which previously made
|
|
555
|
+
// `annotateBackwardSlices` fall back to an unmatchable placeholder string
|
|
556
|
+
// for the overwhelmingly common "string-concatenation into a sink" shape,
|
|
557
|
+
// silently degrading the backward slice to just the bare sink node.
|
|
558
|
+
export function _collectExprVars(expr, out) {
|
|
551
559
|
if (!expr) return;
|
|
552
560
|
if (typeof expr === 'string') { out.add(expr); return; }
|
|
553
561
|
if (expr.kind === 'ident' && expr.name) { out.add(expr.name); return; }
|
|
@@ -2602,6 +2610,20 @@ export function runTaintEngine(perFileIR, callGraph, opts = {}) {
|
|
|
2602
2610
|
// backward-slice annotation was permanently a no-op regardless of
|
|
2603
2611
|
// AGENTIC_SECURITY_BACKWARD_SLICE.
|
|
2604
2612
|
...(f._funcQid ? { _funcQid: f._funcQid } : {}),
|
|
2613
|
+
// argIndex: which sink argument is the tainted one (a number, or the
|
|
2614
|
+
// literal 'all'/'rhs') — set upstream at the finding's creation site
|
|
2615
|
+
// but, exactly like `_funcQid`/`callee` above, silently dropped by
|
|
2616
|
+
// this same allowlist before backward.js's `annotateBackwardSlices`
|
|
2617
|
+
// ever saw it. Without it, `annotateBackwardSlices` falls back to
|
|
2618
|
+
// the placeholder string `arg[undefined]` for EVERY finding (it can
|
|
2619
|
+
// never be a real access path, since no source ever names a
|
|
2620
|
+
// variable that literally), so `sliceBackward` always started and
|
|
2621
|
+
// ended at the bare sink node with no source/sanitize steps at all
|
|
2622
|
+
// — the backward-slice pass was a structural no-op for real findings
|
|
2623
|
+
// regardless of AGENTIC_SECURITY_BACKWARD_SLICE, discovered while
|
|
2624
|
+
// rebuilding the SMT path-feasibility mechanism (next-gen taint
|
|
2625
|
+
// capability #5) to depend on a REAL slice for a sound proof.
|
|
2626
|
+
...(f.argIndex !== undefined ? { argIndex: f.argIndex } : {}),
|
|
2605
2627
|
// callee: kept as plain `callee` (not underscore-prefixed) to match
|
|
2606
2628
|
// backward.js's own contract, which reads `f.callee` on both real and
|
|
2607
2629
|
// fake-fixture findings throughout its module and test suite. It is
|