@clear-capabilities/agentic-security-scanner 0.143.0 → 0.144.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 +304 -0
- package/bin/agentic-security.js +477 -47
- package/dist/1.index.js +223 -0
- package/dist/113.index.js +108 -17
- package/dist/144.index.js +163 -0
- package/dist/178.index.js +1 -1
- package/dist/238.index.js +3 -2
- package/dist/265.index.js +191 -0
- package/dist/384.index.js +1 -1
- package/dist/435.index.js +165 -52
- package/dist/526.index.js +108 -17
- package/dist/552.index.js +97 -0
- package/dist/637.index.js +1 -1
- package/dist/730.index.js +311 -0
- package/dist/736.index.js +301 -0
- package/dist/824.index.js +7 -0
- package/dist/905.index.js +88 -22
- package/dist/920.index.js +491 -0
- package/dist/970.index.js +109 -0
- package/dist/agentic-security.mjs +13 -13
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/calibration-seed.json +2 -0
- package/package.json +19 -11
- package/src/dataflow/index.js +18 -0
- package/src/dataflow/privacy-catalog.js +290 -0
- package/src/dataflow/privacy-deep-walker.js +515 -0
- package/src/dataflow/privacy-governance.js +126 -0
- package/src/dataflow/privacy-inventory.js +154 -0
- package/src/dataflow/privacy-sink-policy.js +125 -0
- package/src/dataflow/privacy-taint.js +115 -54
- package/src/dataflow/privacy-taxonomy.js +233 -0
- package/src/discovery/disprove.js +7 -3
- package/src/discovery/hunter.js +9 -5
- package/src/discovery/index.js +2 -2
- package/src/discovery/llm-invoke.js +69 -13
- package/src/egress/audit.js +147 -0
- package/src/egress/policy.js +313 -0
- package/src/egress/redact.js +180 -0
- package/src/engine.js +575 -288
- package/src/fix/apply-fix-service.js +403 -0
- package/src/fix/approver-registry.js +157 -0
- package/src/llm-validator/index.js +86 -9
- package/src/llm-validator/model-status.js +66 -0
- package/src/mcp/tools.js +157 -50
- package/src/pipeline/analyzer-supervisor.js +93 -0
- package/src/pipeline/analyzer-worker.js +26 -0
- package/src/pipeline/annotator-runner.js +33 -0
- package/src/pipeline/assurance-mode.js +91 -0
- package/src/pipeline/cascade-worker-pool.js +172 -0
- package/src/pipeline/cascade-worker.js +43 -0
- package/src/pipeline/coverage-ledger.js +0 -0
- package/src/pipeline/detector-runner.js +51 -0
- package/src/pipeline/enrichment-completion.js +58 -0
- package/src/pipeline/evidence-provenance.js +91 -0
- package/src/pipeline/finding-schema.js +101 -0
- package/src/pipeline/legacy-compat.js +101 -0
- package/src/pipeline/producer-collector.js +48 -0
- package/src/pipeline/producer-registry.js +112 -0
- package/src/pipeline/scan-health.js +144 -0
- package/src/posture/CLAUDE.md +2 -0
- package/src/posture/accuracy-scorecard.js +96 -1
- package/src/posture/adversary-agent.js +15 -3
- package/src/posture/artifact-registry.js +217 -0
- package/src/posture/auditor-walkthrough.js +70 -8
- package/src/posture/calibration-feedback.js +201 -0
- package/src/posture/calibration-seed.json +2 -0
- package/src/posture/calibration.js +25 -0
- package/src/posture/compliance-evidence-signing.js +131 -0
- package/src/posture/compliance-policy.js +314 -17
- package/src/posture/custom-rules.js +36 -0
- package/src/posture/deterministic.js +8 -1
- package/src/posture/encryption-provider.js +205 -0
- package/src/posture/evidence-grade-wording.js +71 -0
- package/src/posture/fix-history.js +113 -19
- package/src/posture/fix-honesty-gate.js +47 -6
- package/src/posture/fix-verify.js +56 -7
- package/src/posture/fleet.js +0 -0
- package/src/posture/flow-narration.js +7 -2
- package/src/posture/legal-hold.js +140 -0
- package/src/posture/llm-redteam.js +10 -1
- package/src/posture/material-change.js +90 -0
- package/src/posture/policy-bundle.js +274 -0
- package/src/posture/privacy-framework.js +33 -6
- package/src/posture/production-feedback.js +179 -0
- package/src/posture/retention-policy.js +132 -0
- package/src/posture/risk-dollars.js +216 -26
- package/src/posture/scan-checkpoint.js +176 -31
- package/src/posture/state-dir.js +36 -1
- package/src/posture/state-lifecycle-report.js +77 -0
- package/src/posture/suppressions.js +59 -3
- package/src/privacy/ir-adapter.js +380 -0
- package/src/report/index.js +51 -2
- package/src/report/oscal.js +7 -2
- package/src/sast/cpp.js +3 -14
- package/src/sca/llm-function-extract.js +6 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
cdbd5909c8fe1682a351069baff2466e3d1ee7c525f2122909a4954f0c488edf agentic-security.mjs
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_doc": "Seed calibration corpus for P1.3 / FR-UX-1. Each entry is (family, tp, fp) from running the engine against labeled benchmarks. The runtime merges this with the customer's .agentic-security/validator-metrics.json. Customer counts override when their n is higher.",
|
|
3
3
|
"_source": "OWASP Benchmark v1.2 (Java) + Juliet Java + Juliet C/C++ + synthetic-bench fixtures + curated NodeGoat — counts as-of v0.48.0. ~30 samples per family minimum for calibrated emit.",
|
|
4
|
+
"_generatedAt": "2026-05-18T19:15:00-07:00",
|
|
5
|
+
"_generatedAtNote": "FR-207: the actual git commit timestamp of this file (git log -1 --format=%aI), recorded here as a machine-readable field so posture/calibration.js's calibrationFreshness() can compute a real age instead of inferring one from the '_source' version string. Update this value only when the family counts below actually change.",
|
|
4
6
|
"_caveat": "This is a SEED corpus, not a held-out test set. The PRD G1 target (Brier ≤ 0.10) requires a separate held-out labeled set; that work is queued for Phase 5 finalization. Calibrated values shipped now are honest empirical TP rates from this seed, with their Wilson 95% CI and N visible so consumers can judge.",
|
|
5
7
|
"families": {
|
|
6
8
|
"sql-injection": { "tp": 41, "fp": 3 },
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clear-capabilities/agentic-security-scanner",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Scanner engine for the agentic-security Claude Code plugin
|
|
3
|
+
"version": "0.144.0",
|
|
4
|
+
"description": "Scanner engine for the agentic-security Claude Code plugin \u2014 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",
|
|
7
7
|
"bin": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@babel/preset-react": "^8.0.1",
|
|
41
41
|
"@babel/preset-typescript": "^8.0.1",
|
|
42
42
|
"java-parser": "^3.0.1",
|
|
43
|
-
"js-yaml": "^5.
|
|
43
|
+
"js-yaml": "^5.4.1",
|
|
44
44
|
"safe-regex": "^2.1.1"
|
|
45
45
|
},
|
|
46
46
|
"optionalDependencies": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"web-tree-sitter": "0.20.8"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@types/node": "^26.
|
|
51
|
+
"@types/node": "^26.3.0",
|
|
52
52
|
"@vercel/ncc": "^0.45.0"
|
|
53
53
|
},
|
|
54
54
|
"overrides": {
|
|
@@ -59,19 +59,19 @@
|
|
|
59
59
|
"build": "ncc build bin/agentic-security.js -o dist --minify -e web-tree-sitter -e tree-sitter-wasms && rm -rf dist/compliance-frameworks && mkdir -p dist/compliance-frameworks && cp src/posture/compliance-frameworks/*.json dist/compliance-frameworks/ && mv dist/index.js dist/agentic-security.mjs && rm -f dist/package.json && chmod +x dist/agentic-security.mjs && node -e \"const fs=require('fs');const p='dist/agentic-security.mjs';const c=fs.readFileSync(p,'utf8');if(!c.startsWith('#!'))fs.writeFileSync(p,'#!/usr/bin/env node\\n'+c);\" && node -e \"const fs=require('fs');const c=require('crypto');const h=c.createHash('sha256').update(fs.readFileSync('dist/agentic-security.mjs')).digest('hex');fs.writeFileSync('dist/agentic-security.mjs.sha256',h+' agentic-security.mjs\\n');\"",
|
|
60
60
|
"prepare": "node ../scripts/pre-push-gate.mjs --install-hook",
|
|
61
61
|
"prepublishOnly": "npm run build && node ../scripts/sync-scanner-changelog.mjs && node ../scripts/release-check.mjs",
|
|
62
|
-
"test": "
|
|
62
|
+
"test": "node ../scripts/run-unit-tests.mjs && 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 && npm run test:python",
|
|
63
63
|
"test:python": "node ../scripts/run-python-tests.mjs",
|
|
64
64
|
"test:smoke": "node --test test/smoke.test.js test/demo-app.test.js",
|
|
65
65
|
"test:glob": "node --test test/glob-compat.test.js",
|
|
66
|
-
"test:sast": "node --test test/sibling-guard.test.js test/comment-blindness.test.js test/crypto-specialist.test.js test/llm.test.js test/llm-cost-advisor.test.js test/llm-owasp.test.js test/logic.test.js test/authz.test.js test/model-load.test.js test/prompt-template.test.js test/business-logic.test.js test/python-sinks.test.js test/phase1-detectors.test.js test/phase2-detectors.test.js test/phase3-v3.test.js test/phase7-extensions.test.js test/phase8-extensions.test.js test/new-cwe-detectors.test.js test/file-upload.test.js test/llmsecops-detectors.test.js test/db-taint.test.js test/dart-swift.test.js test/zip-slip-containment-guard.test.js test/redos-anchoring.test.js test/path-guard-validator-call.test.js test/redos-nfa.test.js test/weak-randomness.test.js test/csharp-pipeline.test.js test/post-quantum-crypto.test.js test/web3-advanced.test.js test/cloud-iam-k8s.test.js test/crypto-protocol.test.js test/ml-supply-chain.test.js test/mcp-audit-scan.test.js test/wrong-context-sanitizer.test.js test/sanitizer-context.test.js test/frontend-hygiene.test.js test/csv-injection.test.js test/stored-taint.test.js test/tree-sitter-sinks.test.js test/kotlin-structural.test.js test/ruby-php-structural.test.js test/java-csharp-structural.test.js test/guard-recognition.test.js test/guard-window-identifier-correlation.test.js test/js-python-framework-structural.test.js test/go-structural.test.js test/secret-concat.test.js test/secret-redaction.test.js test/xss-reflected-multilang.test.js test/code-injection-multilang.test.js test/xxe-multilang.test.js test/xpath-injection-multilang.test.js test/gapfill-batch13.test.js test/agent-untrusted-flow.test.js test/api-authz.test.js test/event-entrypoint.test.js test/iac-terraform.test.js test/cross-service.test.js test/rbac-consistency.test.js test/adversarial-robustness.test.js test/secret-entropy.test.js test/comment-strip.test.js test/juliet-path-filter-gate.test.js test/java-bench-shape-opt-in.test.js test/reachability-missing-callgraph-data.test.js test/rate-limit.test.js test/auth-signals.test.js test/convention-deviation.test.js test/resource-exhaustion.test.js test/resource-exhaustion-realworld.test.js test/redirect-toctou.test.js test/redirect-toctou-realworld.test.js test/codegen-sink.test.js test/ownership-authz.test.js test/ownership-authz-realworld.test.js test/t21-precision-audit.test.js test/secrets-coverage.test.js test/prompt-injection-payloads.test.js test/iac-cloud-templates.test.js test/ruby-path-join.test.js",
|
|
67
|
-
"test:posture": "node --test 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/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-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/router.test.js test/legacy-alias-redirect.test.js test/cache-economics.test.js test/coverage-report.test.js test/corpus-status.test.js test/provenance.test.js test/secret-history.test.js test/execution-proof.test.js test/fix-metrics.test.js test/corpus-enroll.test.js test/prove-findings.test.js test/corpus-match.test.js test/corpus-provenance.test.js test/learning-quorum.test.js test/llm-cache-integrity.test.js test/proof-artifact.test.js test/providers.test.js test/fleet.test.js test/autopilot.test.js test/autopilot-cli.test.js test/verifier-independence.test.js test/integrity-legacy-key.test.js test/suppression-visibility.test.js test/cost-ceiling.test.js test/local-endpoint.test.js test/model-trust.test.js test/vuln-archaeology.test.js test/scan-checkpoint.test.js test/llm-redact.test.js test/world-class-batch2.test.js test/world-class-modules.test.js test/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/family-registry.test.js test/coverage-strength.test.js test/framework-provenance.test.js test/third-party-verification.test.js test/proof-coverage.test.js test/calibration-holdout.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",
|
|
68
|
-
"test:dataflow": "node --test 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/parser-cs-kt.test.js test/parser-cs-annotations.test.js test/parser-cs-control-flow.test.js test/parser-kt-control-flow.test.js test/parser-js-annotations.test.js test/parser-java-annotations.test.js test/parser-java-control-flow.test.js test/parser-java-calls.test.js test/parser-go.test.js test/parser-php-rb.test.js test/parser-php-control-flow.test.js test/interproc-k2.test.js test/proven-clean.test.js test/backward-default.test.js test/incremental-cache.test.js test/string-regex-lattice.test.js test/closure-capture.test.js test/points-to.test.js test/type-stubs.test.js test/soft-taint.test.js test/ifds.test.js test/symbolic-exec-proof.test.js test/ifds-summary-edges.test.js test/stub-aware-filter.test.js test/cross-repo.test.js test/proof-gate.test.js test/layer-recall.test.js test/java-taint-flow.test.js test/kt-taint-flow.test.js test/sanitizer-typed-flow.test.js test/sanitizer-gate-unit.test.js test/proof-safe.test.js test/collection-taint.test.js test/kcfa-context.test.js test/kcfa-callstring.test.js test/flow-parity.test.js test/callgraph-resolve.test.js test/import-reachability.test.js test/ir-stats.test.js test/parser-cpp.test.js test/parser-js-decorators.test.js test/parser-js-if-else-cfg.test.js test/cpp-integration.test.js test/engine-reconnect.test.js test/phase2-scoping.test.js test/engine-recall.test.js test/catalog-annotation-source.test.js test/catalog-cs-p1.test.js test/catalog-expanded.test.js test/builtin-summaries.test.js test/interproc-mutation-crash.test.js test/mutated-param-taint.test.js test/return-sink-check.test.js test/higher-order-inline-callback.test.js test/interproc-findings-discard.test.js test/runscan-deep-option.test.js test/summary-cache-convergence.test.js test/parser-rb-calls.test.js test/parser-rb-module-level.test.js test/balanced-call.test.js test/destructuring-taint.test.js test/computed-member-taint.test.js test/string-interpolation-taint.test.js test/dataflow-deadcode-severity.test.js test/catalog-dotted-callee-lookup.test.js test/builtin-summary-family-blind-kill.test.js test/deep-mode-annotator-pipeline.test.js test/receiver-type-and-nested-calls.test.js test/member-write-and-loop-taint.test.js test/parser-py-module-level.test.js test/parser-php-module-level.test.js test/r14b-module-level-e2e.test.js test/annotation-taint-engine.test.js test/interproc-nested-call-taint.test.js test/catalog-xpath-injection.test.js test/catalog-ldap-injection.test.js test/catalog-xxe.test.js test/catalog-response-splitting.test.js test/catalog-code-injection.test.js test/parser-rb-control-flow.test.js test/catalog-ruby-p1.test.js test/callee-receiver-taint.test.js test/catalog-command-injection-p4.test.js test/catalog-xss-p4.test.js test/catalog-path-ssrf-p3.test.js",
|
|
66
|
+
"test:sast": "node --test test/cpp-hardcoded-secret.test.js test/sibling-guard.test.js test/comment-blindness.test.js test/crypto-specialist.test.js test/llm.test.js test/llm-cost-advisor.test.js test/llm-owasp.test.js test/logic.test.js test/authz.test.js test/model-load.test.js test/prompt-template.test.js test/business-logic.test.js test/python-sinks.test.js test/phase1-detectors.test.js test/phase2-detectors.test.js test/phase3-v3.test.js test/phase7-extensions.test.js test/phase8-extensions.test.js test/new-cwe-detectors.test.js test/file-upload.test.js test/llmsecops-detectors.test.js test/db-taint.test.js test/dart-swift.test.js test/zip-slip-containment-guard.test.js test/redos-anchoring.test.js test/path-guard-validator-call.test.js test/redos-nfa.test.js test/weak-randomness.test.js test/csharp-pipeline.test.js test/post-quantum-crypto.test.js test/web3-advanced.test.js test/cloud-iam-k8s.test.js test/crypto-protocol.test.js test/ml-supply-chain.test.js test/mcp-audit-scan.test.js test/wrong-context-sanitizer.test.js test/sanitizer-context.test.js test/frontend-hygiene.test.js test/csv-injection.test.js test/stored-taint.test.js test/tree-sitter-sinks.test.js test/kotlin-structural.test.js test/ruby-php-structural.test.js test/java-csharp-structural.test.js test/guard-recognition.test.js test/guard-window-identifier-correlation.test.js test/js-python-framework-structural.test.js test/go-structural.test.js test/secret-concat.test.js test/secret-redaction.test.js test/xss-reflected-multilang.test.js test/code-injection-multilang.test.js test/xxe-multilang.test.js test/xpath-injection-multilang.test.js test/gapfill-batch13.test.js test/agent-untrusted-flow.test.js test/api-authz.test.js test/event-entrypoint.test.js test/iac-terraform.test.js test/cross-service.test.js test/rbac-consistency.test.js test/adversarial-robustness.test.js test/secret-entropy.test.js test/comment-strip.test.js test/juliet-path-filter-gate.test.js test/java-bench-shape-opt-in.test.js test/reachability-missing-callgraph-data.test.js test/rate-limit.test.js test/auth-signals.test.js test/convention-deviation.test.js test/resource-exhaustion.test.js test/resource-exhaustion-realworld.test.js test/redirect-toctou.test.js test/redirect-toctou-realworld.test.js test/codegen-sink.test.js test/ownership-authz.test.js test/ownership-authz-realworld.test.js test/t21-precision-audit.test.js test/secrets-coverage.test.js test/prompt-injection-payloads.test.js test/iac-cloud-templates.test.js test/ruby-path-join.test.js",
|
|
67
|
+
"test:posture": "node --test 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/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-policy-completeness.test.js test/egress-policy-integration.test.js test/router.test.js test/legacy-alias-redirect.test.js test/cache-economics.test.js test/coverage-report.test.js test/corpus-status.test.js test/provenance.test.js test/secret-history.test.js test/execution-proof.test.js test/fix-metrics.test.js test/corpus-enroll.test.js test/prove-findings.test.js test/corpus-match.test.js test/corpus-provenance.test.js test/learning-quorum.test.js test/llm-cache-integrity.test.js test/proof-artifact.test.js test/providers.test.js test/fleet.test.js test/autopilot.test.js test/autopilot-cli.test.js test/verifier-independence.test.js test/integrity-legacy-key.test.js test/suppression-visibility.test.js test/cost-ceiling.test.js test/local-endpoint.test.js test/model-trust.test.js test/vuln-archaeology.test.js test/scan-checkpoint.test.js test/llm-redact.test.js test/world-class-batch2.test.js test/world-class-modules.test.js test/privacy-ir-adapter.test.js test/privacy-taxonomy.test.js test/privacy-sink-policy.test.js test/privacy-governance.test.js test/privacy-inventory.test.js test/integration-end-to-end.test.js test/streak-cli-wiring.test.js test/stack-playbook.test.js test/machine-output-flag.test.js test/stdout-flush.test.js test/format-determinism.test.js test/only-flag-channels.test.js test/confidence.test.js test/security-trend.test.js test/calibration-drift.test.js test/validator-metrics.test.js test/clustering-ir-taint-line.test.js test/concurrency-cwe.test.js test/compliance-mapping-liveness.test.js test/compliance-severity-threshold.test.js test/compliance-severity-policy.test.js test/compliance-evidence-binding.test.js test/compliance-evidence-signing.test.js test/compliance-file-contains.test.js test/label-isolation.test.js test/evidence-grade-wording.test.js test/llm-model-status.test.js test/egress-policy-constraints.test.js test/egress-redact.test.js test/egress-audit.test.js test/egress-approved-providers.test.js test/family-registry.test.js test/coverage-strength.test.js test/framework-provenance.test.js test/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",
|
|
68
|
+
"test:dataflow": "node --test test/privacy-catalog.test.js test/privacy-deep-walker.test.js test/privacy-deep-e2e.test.js test/container-taint.test.js test/stored-taint-families.test.js test/py-annotation-sources.test.js test/fn-reach.test.js test/deep-taint.test.js test/calibration.test.js test/holdout-eval.test.js test/cross-lang-meta.test.js test/cross-lang-queues.test.js test/phase5-xlang.test.js test/phase5-coverage.test.js test/phase6-taint.test.js test/llm-validator-consistency.test.js test/llm-validator-default-on.test.js test/llm-validator-preset.test.js test/parser-py-cst.test.js test/parser-cs-kt.test.js test/parser-cs-annotations.test.js test/parser-cs-control-flow.test.js test/parser-kt-control-flow.test.js test/parser-js-annotations.test.js test/parser-java-annotations.test.js test/parser-java-control-flow.test.js test/parser-java-calls.test.js test/parser-go.test.js test/parser-php-rb.test.js test/parser-php-control-flow.test.js test/interproc-k2.test.js test/proven-clean.test.js test/backward-default.test.js test/incremental-cache.test.js test/string-regex-lattice.test.js test/closure-capture.test.js test/points-to.test.js test/type-stubs.test.js test/soft-taint.test.js test/ifds.test.js test/symbolic-exec-proof.test.js test/ifds-summary-edges.test.js test/stub-aware-filter.test.js test/cross-repo.test.js test/proof-gate.test.js test/layer-recall.test.js test/java-taint-flow.test.js test/kt-taint-flow.test.js test/sanitizer-typed-flow.test.js test/sanitizer-gate-unit.test.js test/proof-safe.test.js test/collection-taint.test.js test/kcfa-context.test.js test/kcfa-callstring.test.js test/flow-parity.test.js test/callgraph-resolve.test.js test/import-reachability.test.js test/ir-stats.test.js test/parser-cpp.test.js test/parser-js-decorators.test.js test/parser-js-if-else-cfg.test.js test/cpp-integration.test.js test/engine-reconnect.test.js test/phase2-scoping.test.js test/engine-recall.test.js test/catalog-annotation-source.test.js test/catalog-cs-p1.test.js test/catalog-expanded.test.js test/builtin-summaries.test.js test/interproc-mutation-crash.test.js test/mutated-param-taint.test.js test/return-sink-check.test.js test/higher-order-inline-callback.test.js test/interproc-findings-discard.test.js test/runscan-deep-option.test.js test/summary-cache-convergence.test.js test/parser-rb-calls.test.js test/parser-rb-module-level.test.js test/balanced-call.test.js test/destructuring-taint.test.js test/computed-member-taint.test.js test/string-interpolation-taint.test.js test/dataflow-deadcode-severity.test.js test/catalog-dotted-callee-lookup.test.js test/builtin-summary-family-blind-kill.test.js test/deep-mode-annotator-pipeline.test.js test/receiver-type-and-nested-calls.test.js test/member-write-and-loop-taint.test.js test/parser-py-module-level.test.js test/parser-php-module-level.test.js test/r14b-module-level-e2e.test.js test/annotation-taint-engine.test.js test/interproc-nested-call-taint.test.js test/catalog-xpath-injection.test.js test/catalog-ldap-injection.test.js test/catalog-xxe.test.js test/catalog-response-splitting.test.js test/catalog-code-injection.test.js test/parser-rb-control-flow.test.js test/catalog-ruby-p1.test.js test/callee-receiver-taint.test.js test/catalog-command-injection-p4.test.js test/catalog-xss-p4.test.js test/catalog-path-ssrf-p3.test.js",
|
|
69
69
|
"test:mcp": "node --test test/mcp.test.js test/mcp-protocol-smoke.test.js test/mcp-audit.test.js test/audit-cli.test.js test/mcp-scratchpad.test.js test/mcp-offload.test.js test/sca-upgrade.test.js test/lsp-server.test.js",
|
|
70
|
-
"test:report": "node --test test/junit.test.js test/ci.test.js test/poc-generator.test.js test/verifier.test.js test/verifier-target.test.js test/annotator-errors.test.js test/grader-calibration.test.js test/pr-delta-gate.test.js test/vex.test.js test/report-render.test.js test/oscal-conformance.test.js",
|
|
70
|
+
"test:report": "node --test test/junit.test.js test/ci.test.js test/poc-generator.test.js test/verifier.test.js test/verifier-target.test.js test/annotator-errors.test.js test/scan-health.test.js test/finding-schema.test.js test/evidence-provenance.test.js test/legacy-compat.test.js test/annotator-runner.test.js test/detector-runner.test.js test/detector-errors.test.js test/analyzer-supervisor.test.js test/cascade-worker-pool.test.js test/cascade-pool-wiring.test.js test/coverage-ledger.test.js test/coverage-ledger-completeness.test.js test/assurance-mode.test.js test/artifact-registry.test.js test/artifact-registry-completeness.test.js test/retention-policy.test.js test/state-lifecycle-report.test.js test/legal-hold.test.js test/production-feedback.test.js test/calibration-feedback.test.js test/encryption-provider.test.js test/grader-calibration.test.js test/pr-delta-gate.test.js test/vex.test.js test/report-render.test.js test/oscal-conformance.test.js test/cli-fix-apply.test.js test/apply-fix-service.test.js test/high-impact-approval-gate.test.js test/approver-registry.test.js test/suppression-exceptions.test.js test/fix-history.test.js test/producer-registry.test.js test/producer-collector.test.js test/enrichment-completion.test.js",
|
|
71
71
|
"test:bench-modules": "node --test test/phase4-harness.test.js test/pipeline.test.js test/proof-corpus-lib.test.js test/proof-corpus-runner.test.js",
|
|
72
72
|
"test:lifecycle": "node --test test/lsp-protocol-smoke.test.js test/ide-surfaces.test.js test/detector-liveness.test.js test/bench-watchdog.test.js test/ci-parity.test.js test/dead-code.test.js test/no-dead-modules.test.js test/no-orphan-scripts.test.js test/no-stray-state.test.js test/tree-integrity.test.js test/stop-hook.test.js test/plugin-self-check.test.js test/skills-registry.test.js test/bodyguard.test.js test/cache-invalidator-guard.test.js test/dispatch-pre-tool.test.js test/dispatch-user-prompt.test.js test/session-start-model-capture.test.js test/sandbox.test.js test/sandbox-escape.test.js test/ci-templates.test.js test/agent-mcp-tools.test.js test/triage-command.test.js test/no-orphan-tests.test.js test/supply-command.test.js test/posture-command.test.js test/labs-command.test.js test/compliance-command.test.js test/check-doc-drift.test.js test/dist-chunks-tracked.test.js && node ../scripts/lint-command-descriptions.mjs",
|
|
73
73
|
"test:eval": "node --test test/independent-eval.test.js test/realworld-recall.test.js",
|
|
74
|
-
"test:discovery": "node --test test/discovery-partition.test.js test/discovery-lenses.test.js test/discovery-hunter.test.js test/discovery-confirm.test.js test/discovery-disprove.test.js test/discovery-judge.test.js test/discovery-run.test.js test/discovery-wiring.test.js test/discovery-llm-invoke.test.js",
|
|
74
|
+
"test:discovery": "node --test test/discovery-partition.test.js test/discovery-lenses.test.js test/discovery-hunter.test.js test/discovery-confirm.test.js test/discovery-disprove.test.js test/discovery-judge.test.js test/discovery-run.test.js test/discovery-wiring.test.js test/discovery-llm-invoke.test.js test/local-only-mode.test.js",
|
|
75
75
|
"eval:independent": "node ../bench/independent-eval/runner.mjs",
|
|
76
76
|
"eval:independent:gate": "node ../bench/independent-eval/runner.mjs --gate default",
|
|
77
77
|
"smoke": "node bin/agentic-security.js scan test/fixtures/vulnerable-js",
|
|
@@ -85,6 +85,8 @@
|
|
|
85
85
|
"bench:cve-replay:ci": "node ../bench/cve-replay/runner.mjs --ci",
|
|
86
86
|
"bench:cve-replay:check": "node ../bench/cve-replay/runner.mjs --check-baseline",
|
|
87
87
|
"bench:cve-replay:update-baseline": "node ../bench/cve-replay/runner.mjs --update-baseline",
|
|
88
|
+
"bench:independent:gate:check": "node ../scripts/independent-population-gate.mjs",
|
|
89
|
+
"bench:independent:update-gate-baseline": "node ../scripts/independent-population-gate.mjs --update-baseline",
|
|
88
90
|
"bench:router-replay": "node ../bench/router-replay/runner.mjs",
|
|
89
91
|
"bench:router-replay:check": "node ../bench/router-replay/runner.mjs --check-baseline",
|
|
90
92
|
"bench:router-replay:update-baseline": "node ../bench/router-replay/runner.mjs --update-baseline",
|
|
@@ -98,6 +100,9 @@
|
|
|
98
100
|
"bench:ttff": "node ../bench/ttff/runner.mjs",
|
|
99
101
|
"bench:ttff:check": "node ../bench/ttff/runner.mjs --check",
|
|
100
102
|
"bench:ttff:update-baseline": "node ../bench/ttff/runner.mjs --update-baseline",
|
|
103
|
+
"bench:memory": "node ../bench/memory/runner.mjs",
|
|
104
|
+
"bench:memory:check": "node ../bench/memory/runner.mjs --check",
|
|
105
|
+
"bench:memory:update-baseline": "node ../bench/memory/runner.mjs --update-baseline",
|
|
101
106
|
"bench:self-scan": "node ../bench/self-scan/measure.mjs",
|
|
102
107
|
"bench:self-scan:check": "node ../bench/self-scan/check.mjs",
|
|
103
108
|
"bench:mutation:check": "node ../bench/mutation/runner.mjs",
|
|
@@ -113,6 +118,9 @@
|
|
|
113
118
|
"bench:layer-recall": "node ../bench/layer-recall/runner.mjs",
|
|
114
119
|
"bench:layer-recall:check": "node ../bench/layer-recall/runner.mjs --check",
|
|
115
120
|
"bench:layer-recall:update-baseline": "node ../bench/layer-recall/runner.mjs --update-baseline",
|
|
121
|
+
"bench:privacy-recall": "node ../bench/privacy-recall/measure.mjs",
|
|
122
|
+
"bench:privacy-recall:check": "node ../bench/privacy-recall/check.mjs",
|
|
123
|
+
"bench:privacy-recall:update-baseline": "node ../bench/privacy-recall/check.mjs --update-baseline",
|
|
116
124
|
"bench:self-scan:update-baseline": "node ../bench/self-scan/check.mjs --update-baseline",
|
|
117
125
|
"bench:engine-recall": "node ../bench/engine-recall/measure.mjs",
|
|
118
126
|
"scorecard": "node ../scripts/scorecard.mjs",
|
|
@@ -151,4 +159,4 @@
|
|
|
151
159
|
"bugs": {
|
|
152
160
|
"url": "https://github.com/Clear-Capabilities/agentic-security/issues"
|
|
153
161
|
}
|
|
154
|
-
}
|
|
162
|
+
}
|
package/src/dataflow/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { runIfdsTaintEngine } from './ifds.js';
|
|
|
18
18
|
import { proveExploits } from './exploit-prover.js';
|
|
19
19
|
import { applyStubAwareFilter } from './stub-aware-filter.js';
|
|
20
20
|
import { loadProjectStubs } from '../ir/type-stubs.js';
|
|
21
|
+
import { runPrivacyTaintEngine } from './privacy-deep-walker.js';
|
|
21
22
|
|
|
22
23
|
export function runDeepAnalysis(perFileIR, callGraph, opts = {}) {
|
|
23
24
|
// Path-feasibility pass over every function before the taint walk.
|
|
@@ -121,6 +122,23 @@ export function runDeepAnalysis(perFileIR, callGraph, opts = {}) {
|
|
|
121
122
|
}
|
|
122
123
|
} catch { /* IFDS failure should not fail the scan */ }
|
|
123
124
|
}
|
|
125
|
+
// FR-403 step 3 (assurance-hardening PRD, D-0047): opt-in, isolated
|
|
126
|
+
// privacy-taint walker. OFF by default. Mirrors the IFDS wiring above
|
|
127
|
+
// exactly -- an additional pass whose findings are merged in, never
|
|
128
|
+
// replacing the primary engine's output, and whose failure must never
|
|
129
|
+
// fail the scan. See dataflow/privacy-deep-walker.js's own header for why
|
|
130
|
+
// this is a wholly separate module rather than a change to runTaintEngine/
|
|
131
|
+
// step/exprTaint above.
|
|
132
|
+
if (process.env.AGENTIC_SECURITY_PRIVACY_DEEP === '1') {
|
|
133
|
+
try {
|
|
134
|
+
const privacyFindings = runPrivacyTaintEngine(callGraph, opts);
|
|
135
|
+
const existing = new Set(findings.map(f => `${f.file}:${f.line}:${f.sink?.label || f.cwe || ''}`));
|
|
136
|
+
for (const f of privacyFindings) {
|
|
137
|
+
const key = `${f.file}:${f.line}:${f.sink?.label || f.cwe || ''}`;
|
|
138
|
+
if (!existing.has(key)) findings.push(f);
|
|
139
|
+
}
|
|
140
|
+
} catch { /* privacy-deep failure should not fail the scan */ }
|
|
141
|
+
}
|
|
124
142
|
for (const f of findings) f._pathFeasibilityPruned = totalPruned;
|
|
125
143
|
if (preSeededCache) {
|
|
126
144
|
Object.defineProperty(findings, '_incrementalStats', {
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// FR-403 (assurance-hardening PRD), Steps 1-2 of D-0041's decomposition plan:
|
|
2
|
+
// "Track flows through ... storage, logs, responses, analytics, email,
|
|
3
|
+
// files, object storage, queues, and outbound APIs."
|
|
4
|
+
//
|
|
5
|
+
// STEP 1: A catalog of PRIVACY-LEAK (CWE-359) sink entries, in the EXACT shape
|
|
6
|
+
// `dataflow/catalog.js` already uses for every other family (see that
|
|
7
|
+
// module's own header for the full `{kind, id, language, framework, match,
|
|
8
|
+
// argIndex, vuln}` contract) — mirroring `privacy-taint.js`'s existing
|
|
9
|
+
// `SINK_PATTERNS` (log/response/outboundHttp/thirdPartySdk/fileWrite/
|
|
10
|
+
// s3Upload/emailSend) plus the 2 sink categories FR-403 names that had no
|
|
11
|
+
// coverage anywhere (generic storage/DB, queues).
|
|
12
|
+
//
|
|
13
|
+
// DELIBERATELY NOT MERGED INTO `catalog.js`'s own `CATALOG` EXPORT, and NOT
|
|
14
|
+
// wired into `matchSinkOrSanitizer`/`runTaintEngine` — this is the single
|
|
15
|
+
// most important design decision in this file, found DURING implementation
|
|
16
|
+
// (not anticipated by D-0041's original grounding pass, which characterized
|
|
17
|
+
// step 1 as "zero behavior change"). `CATALOG` is a shared, always-active
|
|
18
|
+
// singleton: matchSinkOrSanitizer/matchSource read it unconditionally for
|
|
19
|
+
// EVERY scan, with no per-family or per-source filtering at the taint-state
|
|
20
|
+
// level (a tainted value's origin family is never recorded — only whether
|
|
21
|
+
// it is tainted at all). Appending these sink entries directly to `CATALOG`
|
|
22
|
+
// would therefore make them reachable by every ALREADY-ACTIVE general
|
|
23
|
+
// security source (`req.body`, `req.query`, etc.) immediately, producing
|
|
24
|
+
// spurious CWE-359 "privacy-leak" findings for ordinary attacker-input
|
|
25
|
+
// flows that have nothing to do with data actually classified PII/PHI/PCI
|
|
26
|
+
// by `privacy-taxonomy.js` — a real, immediate false-positive class, not
|
|
27
|
+
// the "zero risk" step D-0041's own step 1 was meant to be. Keeping this
|
|
28
|
+
// catalog in its own file, imported by nothing yet, is what makes it
|
|
29
|
+
// genuinely inert: these entries do not affect any scan's output until a
|
|
30
|
+
// future step (D-0041 step 3) builds a privacy-specific matcher that
|
|
31
|
+
// consults ONLY the sources `privacy-taxonomy.js` actually classifies,
|
|
32
|
+
// never the general engine's already-active source set.
|
|
33
|
+
//
|
|
34
|
+
// Registered in `test/no-dead-modules.test.js`'s ALLOWLIST — a deliberate,
|
|
35
|
+
// staged capability with no consumer yet, not an oversight.
|
|
36
|
+
|
|
37
|
+
import { classifyFieldAgainst } from './privacy-taxonomy.js';
|
|
38
|
+
import { _languageFamilyExtensions } from './catalog.js';
|
|
39
|
+
|
|
40
|
+
export const PRIVACY_SINK_CATALOG = [
|
|
41
|
+
// ── log ──────────────────────────────────────────────────────────────────
|
|
42
|
+
{ kind: 'sink', id: 'privacy-js-console-log', language: 'js', framework: 'node', category: 'log',
|
|
43
|
+
match: { type: 'call', callee: 'log', receiverTypeIn: ['console'] }, argIndex: 'all',
|
|
44
|
+
vuln: { name: 'Privacy Leak (console.log)', severity: 'medium', cwe: 'CWE-359',
|
|
45
|
+
remediation: 'Do not log regulated data (PII/PHI/PCI). Redact or hash the field before logging.' } },
|
|
46
|
+
{ kind: 'sink', id: 'privacy-js-console-error', language: 'js', framework: 'node', category: 'log',
|
|
47
|
+
match: { type: 'call', callee: 'error', receiverTypeIn: ['console'] }, argIndex: 'all',
|
|
48
|
+
vuln: { name: 'Privacy Leak (console.error)', severity: 'medium', cwe: 'CWE-359',
|
|
49
|
+
remediation: 'Do not log regulated data (PII/PHI/PCI). Redact or hash the field before logging.' } },
|
|
50
|
+
{ kind: 'sink', id: 'privacy-js-logger-info', language: 'js', framework: 'node', category: 'log',
|
|
51
|
+
match: { type: 'call', callee: 'info', receiverTypeIn: ['log|logger|Logger'] }, argIndex: 'all',
|
|
52
|
+
vuln: { name: 'Privacy Leak (logger.info)', severity: 'medium', cwe: 'CWE-359',
|
|
53
|
+
remediation: 'Do not log regulated data (PII/PHI/PCI). Redact or hash the field before logging.' } },
|
|
54
|
+
{ kind: 'sink', id: 'privacy-js-logger-warn', language: 'js', framework: 'node', category: 'log',
|
|
55
|
+
match: { type: 'call', callee: 'warn', receiverTypeIn: ['log|logger|Logger'] }, argIndex: 'all',
|
|
56
|
+
vuln: { name: 'Privacy Leak (logger.warn)', severity: 'medium', cwe: 'CWE-359',
|
|
57
|
+
remediation: 'Do not log regulated data (PII/PHI/PCI). Redact or hash the field before logging.' } },
|
|
58
|
+
|
|
59
|
+
// ── response ─────────────────────────────────────────────────────────────
|
|
60
|
+
{ kind: 'sink', id: 'privacy-js-res-send', language: 'js', framework: 'express', category: 'response',
|
|
61
|
+
match: { type: 'call', callee: 'send', receiverTypeIn: ['res|response'] }, argIndex: 0,
|
|
62
|
+
vuln: { name: 'Privacy Leak (response body)', severity: 'high', cwe: 'CWE-359',
|
|
63
|
+
remediation: 'Do not return regulated data (PII/PHI/PCI) in an API response unless the requester is authorized and the field is required.' } },
|
|
64
|
+
{ kind: 'sink', id: 'privacy-js-res-json', language: 'js', framework: 'express', category: 'response',
|
|
65
|
+
match: { type: 'call', callee: 'json', receiverTypeIn: ['res|response'] }, argIndex: 0,
|
|
66
|
+
vuln: { name: 'Privacy Leak (response body)', severity: 'high', cwe: 'CWE-359',
|
|
67
|
+
remediation: 'Do not return regulated data (PII/PHI/PCI) in an API response unless the requester is authorized and the field is required.' } },
|
|
68
|
+
|
|
69
|
+
// ── outboundHttp ─────────────────────────────────────────────────────────
|
|
70
|
+
{ kind: 'sink', id: 'privacy-js-fetch', language: 'js', framework: 'browser', category: 'outboundHttp',
|
|
71
|
+
match: { type: 'call', callee: 'fetch' }, argIndex: 1,
|
|
72
|
+
vuln: { name: 'Privacy Leak (outbound HTTP request)', severity: 'high', cwe: 'CWE-359',
|
|
73
|
+
remediation: 'Do not send regulated data (PII/PHI/PCI) to a third-party endpoint without a documented data-processing agreement.' } },
|
|
74
|
+
{ kind: 'sink', id: 'privacy-js-axios-post', language: 'js', framework: 'axios', category: 'outboundHttp',
|
|
75
|
+
match: { type: 'call', callee: 'post', receiverTypeIn: ['axios'] }, argIndex: 1,
|
|
76
|
+
vuln: { name: 'Privacy Leak (outbound HTTP request)', severity: 'high', cwe: 'CWE-359',
|
|
77
|
+
remediation: 'Do not send regulated data (PII/PHI/PCI) to a third-party endpoint without a documented data-processing agreement.' } },
|
|
78
|
+
|
|
79
|
+
// ── thirdPartySdk (analytics) ────────────────────────────────────────────
|
|
80
|
+
{ kind: 'sink', id: 'privacy-js-analytics-track', language: 'js', framework: 'analytics', category: 'thirdPartySdk',
|
|
81
|
+
match: { type: 'call', callee: 'track', receiverTypeIn: ['stripe|sentry|datadog|segment|amplitude|mixpanel|posthog|braze|intercom|analytics'] }, argIndex: 'all',
|
|
82
|
+
vuln: { name: 'Privacy Leak (third-party analytics)', severity: 'high', cwe: 'CWE-359',
|
|
83
|
+
remediation: 'Do not send regulated data (PII/PHI/PCI) to an analytics/SDK provider — pseudonymize identifiers instead of raw fields.' } },
|
|
84
|
+
{ kind: 'sink', id: 'privacy-js-analytics-identify', language: 'js', framework: 'analytics', category: 'thirdPartySdk',
|
|
85
|
+
match: { type: 'call', callee: 'identify', receiverTypeIn: ['stripe|sentry|datadog|segment|amplitude|mixpanel|posthog|braze|intercom|analytics'] }, argIndex: 'all',
|
|
86
|
+
vuln: { name: 'Privacy Leak (third-party analytics)', severity: 'high', cwe: 'CWE-359',
|
|
87
|
+
remediation: 'Do not send regulated data (PII/PHI/PCI) to an analytics/SDK provider — pseudonymize identifiers instead of raw fields.' } },
|
|
88
|
+
|
|
89
|
+
// ── fileWrite ────────────────────────────────────────────────────────────
|
|
90
|
+
{ kind: 'sink', id: 'privacy-js-fs-writeFile', language: 'js', framework: 'node', category: 'fileWrite',
|
|
91
|
+
match: { type: 'call', callee: 'writeFile', receiverTypeIn: ['fs'] }, argIndex: 1,
|
|
92
|
+
vuln: { name: 'Privacy Leak (file write)', severity: 'medium', cwe: 'CWE-359',
|
|
93
|
+
remediation: 'Do not write regulated data (PII/PHI/PCI) to an unencrypted file without a documented retention and access-control policy.' } },
|
|
94
|
+
{ kind: 'sink', id: 'privacy-js-fs-writeFileSync', language: 'js', framework: 'node', category: 'fileWrite',
|
|
95
|
+
match: { type: 'call', callee: 'writeFileSync', receiverTypeIn: ['fs'] }, argIndex: 1,
|
|
96
|
+
vuln: { name: 'Privacy Leak (file write)', severity: 'medium', cwe: 'CWE-359',
|
|
97
|
+
remediation: 'Do not write regulated data (PII/PHI/PCI) to an unencrypted file without a documented retention and access-control policy.' } },
|
|
98
|
+
|
|
99
|
+
// ── s3Upload (object storage) ────────────────────────────────────────────
|
|
100
|
+
{ kind: 'sink', id: 'privacy-js-s3-putObject', language: 'js', framework: 'aws-sdk', category: 's3Upload',
|
|
101
|
+
match: { type: 'call', callee: 'putObject', receiverTypeIn: ['s3|S3Client|aws'] }, argIndex: 0,
|
|
102
|
+
vuln: { name: 'Privacy Leak (object storage upload)', severity: 'high', cwe: 'CWE-359',
|
|
103
|
+
remediation: 'Do not upload regulated data (PII/PHI/PCI) to object storage without server-side encryption and a documented access policy.' } },
|
|
104
|
+
|
|
105
|
+
// ── emailSend ────────────────────────────────────────────────────────────
|
|
106
|
+
{ kind: 'sink', id: 'privacy-js-sendMail', language: 'js', framework: 'nodemailer', category: 'emailSend',
|
|
107
|
+
match: { type: 'call', callee: 'sendMail' }, argIndex: 0,
|
|
108
|
+
vuln: { name: 'Privacy Leak (outbound email)', severity: 'medium', cwe: 'CWE-359',
|
|
109
|
+
remediation: 'Do not include regulated data (PII/PHI/PCI) in an outbound email body beyond what the recipient is authorized to receive.' } },
|
|
110
|
+
|
|
111
|
+
// ── storage (generic DB — the first of FR-403's 2 previously-uncovered categories) ──
|
|
112
|
+
{ kind: 'sink', id: 'privacy-js-mongo-insertOne', language: 'js', framework: 'mongodb', category: 'storage',
|
|
113
|
+
match: { type: 'call', callee: 'insertOne', receiverTypeIn: ['collection|db|mongo'] }, argIndex: 0,
|
|
114
|
+
vuln: { name: 'Privacy Leak (database write)', severity: 'medium', cwe: 'CWE-359',
|
|
115
|
+
remediation: 'Regulated data (PII/PHI/PCI) written to a database must be covered by a documented retention, access-control, and encryption-at-rest policy.' } },
|
|
116
|
+
{ kind: 'sink', id: 'privacy-js-mongo-insertMany', language: 'js', framework: 'mongodb', category: 'storage',
|
|
117
|
+
match: { type: 'call', callee: 'insertMany', receiverTypeIn: ['collection|db|mongo'] }, argIndex: 0,
|
|
118
|
+
vuln: { name: 'Privacy Leak (database write)', severity: 'medium', cwe: 'CWE-359',
|
|
119
|
+
remediation: 'Regulated data (PII/PHI/PCI) written to a database must be covered by a documented retention, access-control, and encryption-at-rest policy.' } },
|
|
120
|
+
|
|
121
|
+
// ── queues (the second of FR-403's 2 previously-uncovered categories) ────
|
|
122
|
+
{ kind: 'sink', id: 'privacy-js-queue-sendMessage', language: 'js', framework: 'aws-sdk', category: 'queues',
|
|
123
|
+
match: { type: 'call', callee: 'sendMessage', receiverTypeIn: ['queue|sqs|SQS'] }, argIndex: 0,
|
|
124
|
+
vuln: { name: 'Privacy Leak (message queue)', severity: 'medium', cwe: 'CWE-359',
|
|
125
|
+
remediation: 'Regulated data (PII/PHI/PCI) placed on a queue is readable by every consumer with queue access — document who can subscribe before sending it.' } },
|
|
126
|
+
{ kind: 'sink', id: 'privacy-js-queue-publish', language: 'js', framework: 'pubsub', category: 'queues',
|
|
127
|
+
match: { type: 'call', callee: 'publish', receiverTypeIn: ['queue|sns|SNS|topic|pubsub|kafka|producer'] }, argIndex: 0,
|
|
128
|
+
vuln: { name: 'Privacy Leak (message queue)', severity: 'medium', cwe: 'CWE-359',
|
|
129
|
+
remediation: 'Regulated data (PII/PHI/PCI) placed on a queue is readable by every consumer with queue access — document who can subscribe before sending it.' } },
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
// All 9 sink categories FR-403 names (7 already covered by privacy-taint.js's
|
|
133
|
+
// own SINK_PATTERNS, plus storage/queues) — used by this module's own tests
|
|
134
|
+
// to assert coverage is genuinely complete, not just "some entries exist."
|
|
135
|
+
export const PRIVACY_SINK_CATEGORIES = Object.freeze([
|
|
136
|
+
'log', 'response', 'outboundHttp', 'thirdPartySdk', 'fileWrite', 's3Upload', 'emailSend', 'storage', 'queues',
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
// ─── D-0041 STEP 2: a "declared-as" source matcher ─────────────────────────
|
|
140
|
+
//
|
|
141
|
+
// The one genuine gap `catalog.js`'s existing `match.type` shapes (`call`,
|
|
142
|
+
// `member`, `global`, `annotation`) leave uncovered: every one of them
|
|
143
|
+
// matches an EXPRESSION PATTERN (a specific callee, a specific property
|
|
144
|
+
// read, a specific decorator). A privacy source is a different kind of
|
|
145
|
+
// fact entirely — "this declaration's NAME classifies as PII/PHI/PCI/etc."
|
|
146
|
+
// — decided by `privacy-taxonomy.js`'s regex-per-class taxonomy, not by
|
|
147
|
+
// what expression touches it. There is no fixed name to put in a `match`
|
|
148
|
+
// object; the classification must run against WHATEVER name a declaration
|
|
149
|
+
// actually has.
|
|
150
|
+
//
|
|
151
|
+
// Modeled after `matchAnnotationParams` (catalog.js) rather than the CFG-
|
|
152
|
+
// node matchers: that function is also NOT consulted mid-walk against an
|
|
153
|
+
// expression — it runs ONCE PER FUNCTION against the IR's own
|
|
154
|
+
// `fn.paramAnnotations` side-channel, before the walk starts, and its
|
|
155
|
+
// result is unioned into the entry taint-state (see `dataflow/CLAUDE.md`'s
|
|
156
|
+
// own note that annotation matching "is not consulted during the CFG
|
|
157
|
+
// walk"). `matchPrivacyDeclSource(s)` below follow that same shape:
|
|
158
|
+
// pure, pre-walk, name-classification functions a future step 3 will call
|
|
159
|
+
// once per function/file against real declaration names, unioning any hit
|
|
160
|
+
// into a privacy-specific entry taint-state — never against `catalog.js`'s
|
|
161
|
+
// own `CATALOG`, for the exact isolation reason this file's header
|
|
162
|
+
// explains for the sink half.
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Classify one declaration name. Returns `{name, classes}` when it matches
|
|
166
|
+
* at least one taxonomy class, or `null` when it matches none — `null`
|
|
167
|
+
* rather than `{name, classes: []}` so a caller can filter with a plain
|
|
168
|
+
* truthiness check, matching `classifyFieldAgainst`'s own "empty array
|
|
169
|
+
* means no match" contract one level up.
|
|
170
|
+
*/
|
|
171
|
+
export function matchPrivacyDeclSource(declName, compiled) {
|
|
172
|
+
if (!declName) return null;
|
|
173
|
+
const classes = classifyFieldAgainst(declName, compiled);
|
|
174
|
+
return classes.length ? { name: declName, classes } : null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Classify every name in a list of declarations (variable names, function
|
|
179
|
+
* params, object-literal keys — whatever a caller's IR extraction
|
|
180
|
+
* produces) against the taxonomy. Returns a Map<name, classes[]> so a
|
|
181
|
+
* caller gets both "which names are sources" (the key set) and "which
|
|
182
|
+
* classes triggered it" (the value) in one pass, without re-classifying.
|
|
183
|
+
* Never throws on malformed input — a non-array degrades to an empty Map.
|
|
184
|
+
*/
|
|
185
|
+
export function matchPrivacyDeclSources(declNames, compiled) {
|
|
186
|
+
const out = new Map();
|
|
187
|
+
for (const name of Array.isArray(declNames) ? declNames : []) {
|
|
188
|
+
const hit = matchPrivacyDeclSource(name, compiled);
|
|
189
|
+
if (hit) out.set(hit.name, hit.classes);
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// D-0046/D-0047 (FR-403 step 3, grounding): mirrors `catalog.js`'s
|
|
195
|
+
// matchSinkOrSanitizer(calleeExpr, file, receiverType) shape/contract
|
|
196
|
+
// exactly (array of hits, or null) so a future isolated privacy walker can
|
|
197
|
+
// consult it the same way the general walker consults the general catalog
|
|
198
|
+
// -- but reads ONLY PRIVACY_SINK_CATALOG, never CATALOG. This is a linear
|
|
199
|
+
// scan, not `catalog.js`'s CALLEE_INDEX Map lookup: at ~20 entries a scan is
|
|
200
|
+
// plenty fast, and avoiding the shared index machinery (which is private/
|
|
201
|
+
// unexported, and keyed by a shared module-level Map) keeps the two
|
|
202
|
+
// catalogs fully decoupled -- exactly the isolation D-0042 requires, now
|
|
203
|
+
// extended to the sink-matching CODE PATH, not just the entry DATA.
|
|
204
|
+
// `_languageFamilyExtensions` is the one thing imported from catalog.js: a
|
|
205
|
+
// pure, stateless helper with no shared mutable state and no coupling to
|
|
206
|
+
// the general taint engine's behavior, reused to avoid duplicating the
|
|
207
|
+
// language-extension regex table.
|
|
208
|
+
|
|
209
|
+
function _privacyLanguageAllowed(entry, file) {
|
|
210
|
+
if (!file) return true;
|
|
211
|
+
const res = _languageFamilyExtensions(entry.language);
|
|
212
|
+
if (!res.length) return true; // unmapped language stays permissive
|
|
213
|
+
return res.some((re) => re.test(file));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function _privacyReceiverTypeAllowed(entry, receiverType) {
|
|
217
|
+
const pats = entry.match && entry.match.receiverTypeIn;
|
|
218
|
+
if (!pats || !pats.length) return true;
|
|
219
|
+
if (!receiverType) return true;
|
|
220
|
+
return pats.some((p) => new RegExp(p, 'i').test(String(receiverType)));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Duplicated from catalog.js's private (unexported) `_calleeIndexHits`
|
|
224
|
+
// name-extraction logic rather than exporting it from catalog.js -- keeping
|
|
225
|
+
// this file's only catalog.js dependency to the one pure helper above.
|
|
226
|
+
function _privacyCalleeNames(calleeExpr) {
|
|
227
|
+
let last = null;
|
|
228
|
+
let full = null;
|
|
229
|
+
if (typeof calleeExpr === 'string') {
|
|
230
|
+
full = calleeExpr;
|
|
231
|
+
last = calleeExpr.includes('.') ? calleeExpr.slice(calleeExpr.lastIndexOf('.') + 1) : calleeExpr;
|
|
232
|
+
} else if (calleeExpr && calleeExpr.kind === 'member' && calleeExpr.prop) {
|
|
233
|
+
last = calleeExpr.prop;
|
|
234
|
+
if (calleeExpr.object && calleeExpr.object.kind === 'ident') full = `${calleeExpr.object.name}.${calleeExpr.prop}`;
|
|
235
|
+
} else if (calleeExpr && calleeExpr.kind === 'ident') {
|
|
236
|
+
last = calleeExpr.name || null;
|
|
237
|
+
}
|
|
238
|
+
return { last, full };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function matchPrivacySink(calleeExpr, file, receiverType) {
|
|
242
|
+
if (!calleeExpr) return null;
|
|
243
|
+
const { last, full } = _privacyCalleeNames(calleeExpr);
|
|
244
|
+
if (!last && !full) return null;
|
|
245
|
+
const hits = PRIVACY_SINK_CATALOG.filter((e) => {
|
|
246
|
+
if (!e || e.kind !== 'sink') return false;
|
|
247
|
+
const cName = e.match && e.match.callee;
|
|
248
|
+
if (!cName) return false;
|
|
249
|
+
if (cName !== last && cName !== full) return false;
|
|
250
|
+
if (!_privacyLanguageAllowed(e, file)) return false;
|
|
251
|
+
if (!_privacyReceiverTypeAllowed(e, receiverType)) return false;
|
|
252
|
+
return true;
|
|
253
|
+
});
|
|
254
|
+
return hits.length ? hits : null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// FR-403 step 3, item (d): safe-transformation recognition. Mirrors
|
|
258
|
+
// dataflow/CLAUDE.md's own documented philosophy for the general engine's
|
|
259
|
+
// sanitizers EXACTLY -- "Sanitizer entries are RECORDED, never trusted to
|
|
260
|
+
// kill taint" -- for the identical reason: a value hashed/masked/encrypted
|
|
261
|
+
// might still be REVERSIBLE (a weak hash, a partial mask, a home-rolled
|
|
262
|
+
// "encrypt" that's really just base64) or applied on only ONE branch. A
|
|
263
|
+
// privacy-deep finding that passed through one of these callees is
|
|
264
|
+
// DEMOTED (confidence, never severity/existence), not deleted -- the same
|
|
265
|
+
// recall-preserving direction every other precision annotator in this
|
|
266
|
+
// codebase takes. A named list, not exhaustive, mirroring engine.js's own
|
|
267
|
+
// _UNSANITIZER_CALLEES precedent (dataflow/CLAUDE.md: "modelled, but only
|
|
268
|
+
// for a named list... a project-local decoder is not recognised").
|
|
269
|
+
const PRIVACY_TRANSFORM_CALLEES = new Set([
|
|
270
|
+
// Cryptographic hash (one-way, not generally reversible)
|
|
271
|
+
'createHash', 'sha256', 'sha512', 'md5', 'hash', 'hashSync', 'hashPassword',
|
|
272
|
+
// Password/key-derivation hashing
|
|
273
|
+
'bcrypt', 'scrypt', 'pbkdf2', 'pbkdf2Sync', 'argon2',
|
|
274
|
+
// Symmetric/asymmetric encryption
|
|
275
|
+
'createCipher', 'createCipheriv', 'encrypt', 'seal',
|
|
276
|
+
// Masking / redaction / anonymization -- named, not pattern-matched, since
|
|
277
|
+
// a bare substring match on "mask" would over-fire on unrelated code
|
|
278
|
+
// (e.g. a bitmask helper named `applyMask`).
|
|
279
|
+
'mask', 'redact', 'anonymize', 'pseudonymize', 'tokenize',
|
|
280
|
+
]);
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* True when `calleeExpr` is one of the named privacy-transform callees.
|
|
284
|
+
* Mirrors matchPrivacySink's own bare/dotted callee-name matching.
|
|
285
|
+
*/
|
|
286
|
+
export function isPrivacyTransformCallee(calleeExpr) {
|
|
287
|
+
if (!calleeExpr) return false;
|
|
288
|
+
const { last, full } = _privacyCalleeNames(calleeExpr);
|
|
289
|
+
return PRIVACY_TRANSFORM_CALLEES.has(last) || PRIVACY_TRANSFORM_CALLEES.has(full);
|
|
290
|
+
}
|