@blamejs/blamejs-shop 0.4.69 → 0.4.71
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 +4 -0
- package/lib/admin.js +8 -6
- package/lib/asset-manifest.json +1 -1
- package/lib/security-middleware.js +65 -23
- package/lib/shipping-insurance.js +29 -4
- package/lib/sms-dispatcher.js +17 -4
- package/lib/storefront.js +10 -7
- package/lib/vendor/MANIFEST.json +319 -267
- package/lib/vendor/blamejs/CHANGELOG.md +2 -0
- package/lib/vendor/blamejs/api-snapshot.json +58 -5
- package/lib/vendor/blamejs/examples/wiki/README.md +1 -1
- package/lib/vendor/blamejs/examples/wiki/docker-compose.prod.yml +9 -8
- package/lib/vendor/blamejs/examples/wiki/docker-compose.yml +10 -9
- package/lib/vendor/blamejs/examples/wiki/env-snapshot.json +4 -3
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +33 -17
- package/lib/vendor/blamejs/examples/wiki/routes/admin.js +7 -10
- package/lib/vendor/blamejs/examples/wiki/server.js +5 -4
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +5 -0
- package/lib/vendor/blamejs/lib/a2a-tasks.js +38 -6
- package/lib/vendor/blamejs/lib/agent-event-bus.js +13 -0
- package/lib/vendor/blamejs/lib/agent-idempotency.js +5 -1
- package/lib/vendor/blamejs/lib/agent-snapshot.js +32 -2
- package/lib/vendor/blamejs/lib/ai-aedt-bias-audit.js +2 -1
- package/lib/vendor/blamejs/lib/ai-content-detect.js +1 -3
- package/lib/vendor/blamejs/lib/ai-frontier-protocol.js +1 -1
- package/lib/vendor/blamejs/lib/ai-model-manifest.js +1 -1
- package/lib/vendor/blamejs/lib/ai-output.js +16 -7
- package/lib/vendor/blamejs/lib/api-snapshot.js +4 -1
- package/lib/vendor/blamejs/lib/app-shutdown.js +7 -1
- package/lib/vendor/blamejs/lib/archive-gz.js +9 -0
- package/lib/vendor/blamejs/lib/archive-read.js +9 -7
- package/lib/vendor/blamejs/lib/archive-tar-read.js +51 -8
- package/lib/vendor/blamejs/lib/archive.js +4 -2
- package/lib/vendor/blamejs/lib/asn1-der.js +70 -22
- package/lib/vendor/blamejs/lib/atomic-file.js +204 -2
- package/lib/vendor/blamejs/lib/audit-chain.js +54 -5
- package/lib/vendor/blamejs/lib/audit-daily-review.js +12 -2
- package/lib/vendor/blamejs/lib/audit-sign.js +2 -1
- package/lib/vendor/blamejs/lib/audit-tools.js +108 -23
- package/lib/vendor/blamejs/lib/audit.js +7 -2
- package/lib/vendor/blamejs/lib/auth/access-lock.js +2 -2
- package/lib/vendor/blamejs/lib/auth/bot-challenge.js +1 -1
- package/lib/vendor/blamejs/lib/auth/ciba.js +43 -4
- package/lib/vendor/blamejs/lib/auth/dpop.js +6 -1
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +5 -1
- package/lib/vendor/blamejs/lib/auth/jwt.js +2 -2
- package/lib/vendor/blamejs/lib/auth/lockout.js +18 -2
- package/lib/vendor/blamejs/lib/auth/passkey.js +1 -1
- package/lib/vendor/blamejs/lib/auth/password.js +1 -1
- package/lib/vendor/blamejs/lib/auth/saml.js +33 -13
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +24 -4
- package/lib/vendor/blamejs/lib/auth/status-list.js +14 -2
- package/lib/vendor/blamejs/lib/auth/step-up.js +9 -1
- package/lib/vendor/blamejs/lib/auth-bot-challenge.js +21 -2
- package/lib/vendor/blamejs/lib/backup/bundle.js +7 -2
- package/lib/vendor/blamejs/lib/backup/crypto.js +23 -8
- package/lib/vendor/blamejs/lib/backup/index.js +41 -20
- package/lib/vendor/blamejs/lib/backup/manifest.js +7 -1
- package/lib/vendor/blamejs/lib/break-glass.js +41 -22
- package/lib/vendor/blamejs/lib/cbor.js +34 -11
- package/lib/vendor/blamejs/lib/cdn-cache-control.js +7 -3
- package/lib/vendor/blamejs/lib/cert.js +5 -3
- package/lib/vendor/blamejs/lib/cli.js +5 -1
- package/lib/vendor/blamejs/lib/cloud-events.js +3 -2
- package/lib/vendor/blamejs/lib/cluster-storage.js +7 -3
- package/lib/vendor/blamejs/lib/codepoint-class.js +17 -0
- package/lib/vendor/blamejs/lib/compliance-eaa.js +1 -1
- package/lib/vendor/blamejs/lib/compliance-sanctions.js +9 -7
- package/lib/vendor/blamejs/lib/compliance.js +1 -1
- package/lib/vendor/blamejs/lib/config-drift.js +22 -8
- package/lib/vendor/blamejs/lib/content-credentials.js +11 -8
- package/lib/vendor/blamejs/lib/content-digest.js +11 -4
- package/lib/vendor/blamejs/lib/cookies.js +10 -2
- package/lib/vendor/blamejs/lib/cose.js +20 -0
- package/lib/vendor/blamejs/lib/crdt.js +2 -1
- package/lib/vendor/blamejs/lib/crypto-field.js +29 -23
- package/lib/vendor/blamejs/lib/crypto.js +18 -4
- package/lib/vendor/blamejs/lib/csp.js +4 -0
- package/lib/vendor/blamejs/lib/daemon.js +4 -1
- package/lib/vendor/blamejs/lib/data-act.js +27 -4
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +14 -6
- package/lib/vendor/blamejs/lib/db-query.js +69 -9
- package/lib/vendor/blamejs/lib/db.js +32 -15
- package/lib/vendor/blamejs/lib/dora.js +43 -13
- package/lib/vendor/blamejs/lib/dr-runbook.js +1 -1
- package/lib/vendor/blamejs/lib/dsa.js +2 -1
- package/lib/vendor/blamejs/lib/dsr.js +22 -8
- package/lib/vendor/blamejs/lib/early-hints.js +19 -0
- package/lib/vendor/blamejs/lib/eat.js +5 -1
- package/lib/vendor/blamejs/lib/external-db.js +60 -4
- package/lib/vendor/blamejs/lib/fda-21cfr11.js +30 -5
- package/lib/vendor/blamejs/lib/flag-providers.js +6 -2
- package/lib/vendor/blamejs/lib/forms.js +1 -1
- package/lib/vendor/blamejs/lib/gate-contract.js +46 -5
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +18 -9
- package/lib/vendor/blamejs/lib/graphql-federation.js +17 -4
- package/lib/vendor/blamejs/lib/guard-all.js +2 -2
- package/lib/vendor/blamejs/lib/guard-dsn.js +1 -1
- package/lib/vendor/blamejs/lib/guard-envelope.js +1 -1
- package/lib/vendor/blamejs/lib/guard-html.js +9 -11
- package/lib/vendor/blamejs/lib/guard-imap-command.js +1 -1
- package/lib/vendor/blamejs/lib/guard-jmap.js +1 -1
- package/lib/vendor/blamejs/lib/guard-json.js +14 -6
- package/lib/vendor/blamejs/lib/guard-mail-move.js +1 -1
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +1 -1
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +1 -1
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +1 -1
- package/lib/vendor/blamejs/lib/guard-svg.js +8 -9
- package/lib/vendor/blamejs/lib/html-balance.js +7 -3
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +33 -12
- package/lib/vendor/blamejs/lib/http-client.js +225 -53
- package/lib/vendor/blamejs/lib/iab-tcf.js +3 -2
- package/lib/vendor/blamejs/lib/importmap-integrity.js +41 -1
- package/lib/vendor/blamejs/lib/incident-report.js +9 -6
- package/lib/vendor/blamejs/lib/json-patch.js +1 -1
- package/lib/vendor/blamejs/lib/json-path.js +24 -3
- package/lib/vendor/blamejs/lib/jtd.js +2 -2
- package/lib/vendor/blamejs/lib/legal-hold.js +24 -8
- package/lib/vendor/blamejs/lib/log.js +2 -2
- package/lib/vendor/blamejs/lib/mail-agent.js +2 -2
- package/lib/vendor/blamejs/lib/mail-arf.js +1 -1
- package/lib/vendor/blamejs/lib/mail-auth.js +3 -3
- package/lib/vendor/blamejs/lib/mail-bimi.js +16 -16
- package/lib/vendor/blamejs/lib/mail-bounce.js +3 -3
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +71 -6
- package/lib/vendor/blamejs/lib/mail-deploy.js +9 -5
- package/lib/vendor/blamejs/lib/mail-greylist.js +2 -4
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -4
- package/lib/vendor/blamejs/lib/mail-journal.js +11 -8
- package/lib/vendor/blamejs/lib/mail-mdn.js +8 -4
- package/lib/vendor/blamejs/lib/mail-rbl.js +2 -4
- package/lib/vendor/blamejs/lib/mail-scan.js +3 -5
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +4 -1
- package/lib/vendor/blamejs/lib/mail-server-registry.js +1 -1
- package/lib/vendor/blamejs/lib/mail-server-tls.js +9 -2
- package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -4
- package/lib/vendor/blamejs/lib/mail-store-fts.js +1 -1
- package/lib/vendor/blamejs/lib/mail.js +22 -2
- package/lib/vendor/blamejs/lib/markup-tokenizer.js +24 -0
- package/lib/vendor/blamejs/lib/mcp.js +6 -4
- package/lib/vendor/blamejs/lib/mdoc.js +26 -3
- package/lib/vendor/blamejs/lib/metrics.js +14 -3
- package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +2 -2
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +10 -4
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +26 -18
- package/lib/vendor/blamejs/lib/middleware/clear-site-data.js +5 -1
- package/lib/vendor/blamejs/lib/middleware/compression.js +9 -0
- package/lib/vendor/blamejs/lib/middleware/cors.js +32 -23
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +60 -21
- package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +6 -4
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +28 -4
- package/lib/vendor/blamejs/lib/middleware/network-allowlist.js +61 -30
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +25 -16
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +24 -6
- package/lib/vendor/blamejs/lib/middleware/speculation-rules.js +6 -3
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +2 -2
- package/lib/vendor/blamejs/lib/money.js +1 -1
- package/lib/vendor/blamejs/lib/mtls-ca.js +10 -6
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +1 -1
- package/lib/vendor/blamejs/lib/network-dns.js +9 -2
- package/lib/vendor/blamejs/lib/network-dnssec.js +2 -1
- package/lib/vendor/blamejs/lib/network-smtp-policy.js +23 -5
- package/lib/vendor/blamejs/lib/network-tls.js +27 -5
- package/lib/vendor/blamejs/lib/network-tsig.js +2 -2
- package/lib/vendor/blamejs/lib/nis2-report.js +1 -1
- package/lib/vendor/blamejs/lib/nist-crosswalk.js +1 -1
- package/lib/vendor/blamejs/lib/ntp-check.js +28 -0
- package/lib/vendor/blamejs/lib/numeric-bounds.js +9 -0
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +1 -2
- package/lib/vendor/blamejs/lib/object-store/gcs-bucket-ops.js +4 -2
- package/lib/vendor/blamejs/lib/object-store/gcs.js +6 -4
- package/lib/vendor/blamejs/lib/object-store/http-put.js +1 -2
- package/lib/vendor/blamejs/lib/object-store/http-request.js +30 -1
- package/lib/vendor/blamejs/lib/object-store/local.js +37 -17
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +1 -2
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +20 -4
- package/lib/vendor/blamejs/lib/outbox.js +11 -4
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +1 -1
- package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +21 -3
- package/lib/vendor/blamejs/lib/queue-local.js +10 -3
- package/lib/vendor/blamejs/lib/redact.js +7 -3
- package/lib/vendor/blamejs/lib/request-helpers.js +201 -23
- package/lib/vendor/blamejs/lib/resource-access-lock.js +3 -3
- package/lib/vendor/blamejs/lib/restore-bundle.js +46 -18
- package/lib/vendor/blamejs/lib/restore-rollback.js +10 -4
- package/lib/vendor/blamejs/lib/restore.js +19 -0
- package/lib/vendor/blamejs/lib/retention.js +20 -4
- package/lib/vendor/blamejs/lib/router.js +17 -4
- package/lib/vendor/blamejs/lib/safe-ical.js +2 -2
- package/lib/vendor/blamejs/lib/safe-icap.js +1 -1
- package/lib/vendor/blamejs/lib/safe-json.js +44 -0
- package/lib/vendor/blamejs/lib/safe-sieve.js +1 -1
- package/lib/vendor/blamejs/lib/safe-vcard.js +1 -1
- package/lib/vendor/blamejs/lib/sandbox-worker.js +6 -0
- package/lib/vendor/blamejs/lib/sandbox.js +1 -1
- package/lib/vendor/blamejs/lib/scheduler.js +17 -1
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +16 -0
- package/lib/vendor/blamejs/lib/session.js +27 -3
- package/lib/vendor/blamejs/lib/sql.js +3 -3
- package/lib/vendor/blamejs/lib/static.js +65 -13
- package/lib/vendor/blamejs/lib/template.js +7 -5
- package/lib/vendor/blamejs/lib/tenant-quota.js +52 -19
- package/lib/vendor/blamejs/lib/tsa.js +5 -2
- package/lib/vendor/blamejs/lib/vault/index.js +5 -0
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +22 -26
- package/lib/vendor/blamejs/lib/vault/passphrase-source.js +8 -3
- package/lib/vendor/blamejs/lib/vault/rotate.js +13 -18
- package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +4 -1
- package/lib/vendor/blamejs/lib/vc.js +1 -1
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +10 -10
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +23 -10
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5498 -5494
- package/lib/vendor/blamejs/lib/webhook.js +16 -1
- package/lib/vendor/blamejs/lib/websocket.js +1 -1
- package/lib/vendor/blamejs/lib/worm.js +1 -1
- package/lib/vendor/blamejs/lib/ws-client.js +57 -46
- package/lib/vendor/blamejs/lib/x509-chain.js +44 -0
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.15.14.json +122 -0
- package/lib/vendor/blamejs/scripts/check-vendor-currency.js +119 -4
- package/lib/vendor/blamejs/test/00-primitives.js +5 -1
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +18 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +42 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +47 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-gz.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-hardening.test.js +160 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-fd-read.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-open-nofollow.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-write-excl.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-write-stream.test.js +181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-chain-corrupted-anchor.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-chain-incremental-verify.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-daily-review.test.js +48 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +13 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +122 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge.test.js +37 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +5 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-lockout.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/bot-guard.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +62 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +12 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster-storage.test.js +40 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +146 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compression-range.test.js +115 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +17 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/cors.test.js +27 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/data-act.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dora.test.js +20 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-alg-kty.test.js +64 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fda-21cfr11.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +33 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +12 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +72 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-throttle-transform.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/importmap-integrity.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +20 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-header-injection.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +63 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/mdoc.test.js +41 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/network-allowlist.test.js +61 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/object-store-range-header.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +35 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/output-header-hardening.test.js +102 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parser-verify-hardening.test.js +191 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/proto-shadow-allowlist.test.js +120 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +75 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/restore-blob-remap.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-sweep-termination.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-body-validation.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-json-stringify-for-script.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +48 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +23 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +54 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +42 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/step-up.test.js +7 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/template-escape-html.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-default-store.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vendor-currency-classify.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +22 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/x509-chain-ca-enforcement.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +34 -0
- package/package.json +1 -1
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
var nodeCrypto = require("node:crypto");
|
|
32
32
|
var C = require("./constants");
|
|
33
33
|
var bCrypto = require("./crypto");
|
|
34
|
+
var x509Chain = require("./x509-chain");
|
|
34
35
|
var canonicalJson = require("./canonical-json");
|
|
35
36
|
var safeJson = require("./safe-json");
|
|
36
37
|
var validateOpts = require("./validate-opts");
|
|
@@ -1103,7 +1104,7 @@ function attachIdentityAssertion(opts) {
|
|
|
1103
1104
|
validateOpts.requireObject(opts, "contentCredentials.attachIdentityAssertion", ContentCredentialsError);
|
|
1104
1105
|
validateOpts(opts, ["binding", "subject", "referencedAssertions", "privateKeyPem", "audit"],
|
|
1105
1106
|
"contentCredentials.attachIdentityAssertion");
|
|
1106
|
-
if (typeof opts.binding !== "string" || !IDENTITY_BINDINGS
|
|
1107
|
+
if (typeof opts.binding !== "string" || !Object.prototype.hasOwnProperty.call(IDENTITY_BINDINGS, opts.binding)) {
|
|
1107
1108
|
throw ContentCredentialsError.factory("content-credentials/bad-identity-binding",
|
|
1108
1109
|
"attachIdentityAssertion: binding must be one of " + Object.keys(IDENTITY_BINDINGS).join(" / "));
|
|
1109
1110
|
}
|
|
@@ -1217,7 +1218,7 @@ function verifyIdentityAssertion(assertion, publicKeyPem, opts) {
|
|
|
1217
1218
|
return _fail("public-key-required");
|
|
1218
1219
|
}
|
|
1219
1220
|
var sp = assertion.signer_payload;
|
|
1220
|
-
if (!sp || typeof sp !== "object" || typeof sp.binding !== "string" || !IDENTITY_BINDINGS
|
|
1221
|
+
if (!sp || typeof sp !== "object" || typeof sp.binding !== "string" || !Object.prototype.hasOwnProperty.call(IDENTITY_BINDINGS, sp.binding) ||
|
|
1221
1222
|
!Array.isArray(sp.referenced_assertions)) {
|
|
1222
1223
|
return _fail("signer-payload-shape");
|
|
1223
1224
|
}
|
|
@@ -1334,9 +1335,10 @@ function _verifyIdentityX509Chain(certChainPem, trustAnchorsPem) {
|
|
|
1334
1335
|
// only direct-root / self-signed leaves.
|
|
1335
1336
|
for (var li = 0; li < certs.length - 1; li += 1) {
|
|
1336
1337
|
var child = certs[li], parent = certs[li + 1];
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1338
|
+
// issuerValidlyIssued enforces basicConstraints cA:TRUE on the parent in
|
|
1339
|
+
// addition to the issuance + signature linkage — a non-CA cert cannot be
|
|
1340
|
+
// an intermediate issuer (basicConstraints bypass, CVE-2002-0862 class).
|
|
1341
|
+
var linked = x509Chain.issuerValidlyIssued(parent, child);
|
|
1340
1342
|
if (!linked) { return { ok: false, reason: "broken-chain" }; }
|
|
1341
1343
|
}
|
|
1342
1344
|
// The top of the presented chain must chain to (or BE) a trust anchor.
|
|
@@ -1347,8 +1349,9 @@ function _verifyIdentityX509Chain(certChainPem, trustAnchorsPem) {
|
|
|
1347
1349
|
if (top.fingerprint256 === anchor.fingerprint256) {
|
|
1348
1350
|
chained = true; // top of the chain IS the anchor (root-in-chain or self-signed leaf == anchor)
|
|
1349
1351
|
} else {
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
+
// issuerValidlyIssued enforces basicConstraints cA:TRUE on the anchor
|
|
1353
|
+
// in addition to issuance + signature (basicConstraints bypass class).
|
|
1354
|
+
chained = x509Chain.issuerValidlyIssued(anchor, top);
|
|
1352
1355
|
}
|
|
1353
1356
|
if (chained) {
|
|
1354
1357
|
if (now < anchor.validFromDate.getTime() || now > anchor.validToDate.getTime()) {
|
|
@@ -1427,7 +1430,7 @@ function cacImplicitLabel(opts) {
|
|
|
1427
1430
|
throw new ContentCredentialsError("cac-implicit-label/bad-content-id",
|
|
1428
1431
|
"cacImplicitLabel: contentId must match [A-Za-z0-9._:/-]");
|
|
1429
1432
|
}
|
|
1430
|
-
if (typeof opts.contentKind !== "string" || !CAC_KIND_ENUM
|
|
1433
|
+
if (typeof opts.contentKind !== "string" || !Object.prototype.hasOwnProperty.call(CAC_KIND_ENUM, opts.contentKind)) {
|
|
1431
1434
|
throw new ContentCredentialsError("cac-implicit-label/bad-content-kind",
|
|
1432
1435
|
"cacImplicitLabel: contentKind must be one of " +
|
|
1433
1436
|
Object.keys(CAC_KIND_ENUM).join("/"));
|
|
@@ -99,9 +99,12 @@ function create(body, opts) {
|
|
|
99
99
|
if (!Array.isArray(algos) || algos.length === 0) throw new ContentDigestError("content-digest/bad-arg", "contentDigest.create: opts.algorithms must be a non-empty array");
|
|
100
100
|
var members = algos.map(function (a) {
|
|
101
101
|
var name = String(a).toLowerCase();
|
|
102
|
-
|
|
102
|
+
// hasOwnProperty: the algorithm name is operator/caller input; a bracket
|
|
103
|
+
// lookup lets "constructor"/"toString" inherit a truthy value off the
|
|
104
|
+
// prototype and pass the support check (proto shadowing).
|
|
105
|
+
var nodeAlg = Object.prototype.hasOwnProperty.call(ACTIVE, name) ? ACTIVE[name] : undefined;
|
|
103
106
|
if (!nodeAlg) {
|
|
104
|
-
if (DEPRECATED
|
|
107
|
+
if (Object.prototype.hasOwnProperty.call(DEPRECATED, name)) throw new ContentDigestError("content-digest/insecure-algorithm", "contentDigest.create: '" + name + "' is a deprecated/insecure digest algorithm (RFC 9530 §6); use sha-256 or sha-512");
|
|
105
108
|
throw new ContentDigestError("content-digest/unsupported-algorithm", "contentDigest.create: unsupported digest algorithm '" + name + "'");
|
|
106
109
|
}
|
|
107
110
|
var digest = nodeCrypto.createHash(nodeAlg).update(bytes).digest("base64");
|
|
@@ -154,8 +157,12 @@ function verify(fieldValue, body, opts) {
|
|
|
154
157
|
var kvp = structuredFields.parseKeyValuePiece(m);
|
|
155
158
|
var name = kvp.key;
|
|
156
159
|
var raw = kvp.value.trim();
|
|
160
|
+
// hasOwnProperty: `name` is the algorithm token from the untrusted inbound
|
|
161
|
+
// Content-Digest header; a bracket lookup lets "constructor"/"__proto__"
|
|
162
|
+
// inherit a truthy value off the prototype and reach createHash (proto
|
|
163
|
+
// shadowing).
|
|
164
|
+
if (!Object.prototype.hasOwnProperty.call(ACTIVE, name)) continue; // ignore legacy / unknown entries
|
|
157
165
|
var nodeAlg = ACTIVE[name];
|
|
158
|
-
if (!nodeAlg) continue; // ignore legacy / unknown entries
|
|
159
166
|
if (raw.length < 2 || raw.charAt(0) !== ":" || raw.charAt(raw.length - 1) !== ":") {
|
|
160
167
|
throw new ContentDigestError("content-digest/bad-field", "contentDigest.verify: '" + name + "' value is not an RFC 8941 byte sequence (:base64:)");
|
|
161
168
|
}
|
|
@@ -172,7 +179,7 @@ function verify(fieldValue, body, opts) {
|
|
|
172
179
|
if (!Array.isArray(opts.required)) throw new ContentDigestError("content-digest/bad-arg", "contentDigest.verify: opts.required must be an array");
|
|
173
180
|
for (var r = 0; r < opts.required.length; r++) {
|
|
174
181
|
var req = String(opts.required[r]).toLowerCase();
|
|
175
|
-
if (!ACTIVE
|
|
182
|
+
if (!Object.prototype.hasOwnProperty.call(ACTIVE, req)) throw new ContentDigestError("content-digest/unsupported-algorithm", "contentDigest.verify: required algorithm '" + req + "' is not a modern digest");
|
|
176
183
|
if (!seen[req]) throw new ContentDigestError("content-digest/missing-algorithm", "contentDigest.verify: required digest '" + req + "' is not present");
|
|
177
184
|
}
|
|
178
185
|
}
|
|
@@ -96,7 +96,11 @@ function _validateValue(value) {
|
|
|
96
96
|
// Length cap before the regex test (defense in depth — the regex
|
|
97
97
|
// here is a simple character class, but the same discipline that
|
|
98
98
|
// bounds longer regexes elsewhere applies).
|
|
99
|
-
|
|
99
|
+
// Byte length, not value.length (UTF-16 code units): the cap bounds the
|
|
100
|
+
// on-the-wire Set-Cookie size, and a multibyte value is 2-4x larger in bytes
|
|
101
|
+
// than chars, so a char-count check under-enforces and a 4096-char value can
|
|
102
|
+
// emit a ~12 KiB+ header.
|
|
103
|
+
if (Buffer.byteLength(value, "utf8") > MAX_VALUE_LENGTH || FORBIDDEN_VALUE_RE.test(value)) {
|
|
100
104
|
throw new CookieError("cookies/invalid-value",
|
|
101
105
|
"cookie value is too long or contains forbidden control character (CRLF/NUL/;/,)");
|
|
102
106
|
}
|
|
@@ -108,7 +112,11 @@ function _validateValue(value) {
|
|
|
108
112
|
// header — never trust unscrubbed values reach the wire.
|
|
109
113
|
function _scrubAttr(s) {
|
|
110
114
|
if (typeof s !== "string") return s;
|
|
111
|
-
|
|
115
|
+
// `;` joins Set-Cookie attributes, so an unscrubbed `;` in Path/SameSite is
|
|
116
|
+
// attribute injection (e.g. Path=/x; HttpOnly). Domain is regex-validated
|
|
117
|
+
// upstream; Path/SameSite only flow through here, so strip `;` alongside the
|
|
118
|
+
// CR/LF/NUL header-injection set.
|
|
119
|
+
return s.replace(/[\r\n\0;]/g, ""); // allow:duplicate-regex — CR/LF/NUL(+;) header-injection rejection appears in cookies / mail / security-headers; each is the boundary primitive for its domain
|
|
112
120
|
|
|
113
121
|
}
|
|
114
122
|
|
|
@@ -110,6 +110,24 @@ function _algParamsFor(algId) {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
// RFC 9053 §2 binds each ECDSA alg to ONE curve. node:crypto will happily
|
|
114
|
+
// verify a sha512 (ES512-shape) signature against a P-256 key as
|
|
115
|
+
// self-consistent, so without this check a COSE_Sign1 declaring ES512 but
|
|
116
|
+
// carrying a P-256 key passes when ES512 is allowlisted — an alg/curve
|
|
117
|
+
// confusion (the COSE sibling of jwt-external._assertAlgKtyMatch). EdDSA /
|
|
118
|
+
// ML-DSA carry their parameters in the KeyObject and need no binding.
|
|
119
|
+
var _ES_ALG_CURVE = { ES256: "prime256v1", ES384: "secp384r1", ES512: "secp521r1" };
|
|
120
|
+
function _assertEcAlgCurve(algName, key) {
|
|
121
|
+
var want = _ES_ALG_CURVE[algName];
|
|
122
|
+
if (want === undefined) return; // not an ECDSA alg — nothing to bind
|
|
123
|
+
var got = (key && key.asymmetricKeyDetails && key.asymmetricKeyDetails.namedCurve) || null;
|
|
124
|
+
if (key.asymmetricKeyType !== "ec" || got !== want) {
|
|
125
|
+
throw new CoseError("cose/alg-curve-mismatch",
|
|
126
|
+
"cose: alg '" + algName + "' requires an EC key on " + want +
|
|
127
|
+
", got " + (key.asymmetricKeyType === "ec" ? got : key.asymmetricKeyType));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
113
131
|
function _bstr(x) {
|
|
114
132
|
return safeBuffer.toBuffer(x, {
|
|
115
133
|
errorFactory: function (code, msg) { return new CoseError(code, msg); },
|
|
@@ -169,6 +187,7 @@ async function sign(payload, opts) {
|
|
|
169
187
|
var algId = ALG_NAME_TO_ID[opts.alg];
|
|
170
188
|
var params = _algParamsFor(algId);
|
|
171
189
|
var key = _toKeyObject(opts.privateKey, "private");
|
|
190
|
+
_assertEcAlgCurve(opts.alg, key); // RFC 9053 alg↔curve binding — refuse e.g. ES512 over a P-256 key
|
|
172
191
|
|
|
173
192
|
var protMap = new Map();
|
|
174
193
|
protMap.set(HDR_ALG, algId);
|
|
@@ -352,6 +371,7 @@ async function verify(coseSign1, opts) {
|
|
|
352
371
|
var key = opts.publicKey
|
|
353
372
|
? _toKeyObject(opts.publicKey, "public")
|
|
354
373
|
: _toKeyObject(opts.keyResolver(protMap, unprotected), "public");
|
|
374
|
+
_assertEcAlgCurve(algName, key); // RFC 9053 alg↔curve binding
|
|
355
375
|
|
|
356
376
|
var externalAad = opts.externalAad == null ? Buffer.alloc(0) : _bstr(opts.externalAad);
|
|
357
377
|
var toBeSigned = _toBeSigned(protectedBstr, externalAad, payload);
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
var bCrypto = require("./crypto");
|
|
43
43
|
var safeJson = require("./safe-json");
|
|
44
|
+
var numericBounds = require("./numeric-bounds");
|
|
44
45
|
var { defineClass } = require("./framework-error");
|
|
45
46
|
|
|
46
47
|
var CrdtError = defineClass("CrdtError", { alwaysPermanent: true });
|
|
@@ -52,7 +53,7 @@ function _replicaId(opts) {
|
|
|
52
53
|
return id;
|
|
53
54
|
}
|
|
54
55
|
function _posInt(n, label) {
|
|
55
|
-
if (
|
|
56
|
+
if (!numericBounds.isNonNegativeFiniteInt(n)) throw new CrdtError("crdt/bad-value", "crdt: " + label + " must be a non-negative integer");
|
|
56
57
|
return n;
|
|
57
58
|
}
|
|
58
59
|
function _maxMerge(a, b) {
|
|
@@ -62,7 +62,6 @@ var vaultAad = require("./vault-aad");
|
|
|
62
62
|
var validateOpts = require("./validate-opts");
|
|
63
63
|
var numericBounds = require("./numeric-bounds");
|
|
64
64
|
var safeJson = require("./safe-json");
|
|
65
|
-
var frameworkSchema = require("./framework-schema");
|
|
66
65
|
var sql = require("./sql");
|
|
67
66
|
var { defineClass } = require("./framework-error");
|
|
68
67
|
var { sha3Hash, kdf, generateBytes, encryptPacked, decryptPacked, generateToken } = require("./crypto");
|
|
@@ -226,14 +225,19 @@ var PER_ROW_KEYS_TABLE = "_blamejs_per_row_keys"; // allow:hand-rolled-sql
|
|
|
226
225
|
var PER_ROW_KEYS_COLUMN = "wrappedKey";
|
|
227
226
|
var PER_ROW_KEYS_SCHEMA_VERSION = "1";
|
|
228
227
|
|
|
229
|
-
// The per-row-key registry
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
228
|
+
// The per-row-key registry ALWAYS lives in the LOCAL sqlite — reconcile creates
|
|
229
|
+
// it under its RAW schema name, and per-row keys are never dispatched to an
|
|
230
|
+
// external backend (see _kRowOnce's db() fallback). It is read/written against
|
|
231
|
+
// the local db() / dbHandle directly, NOT through clusterStorage, so it must use
|
|
232
|
+
// the RAW table name. frameworkSchema.tableName() resolves to the cluster-mode
|
|
233
|
+
// EXTERNAL prefixed name (the prefix applies only to the external backend, via
|
|
234
|
+
// resolveTables — local DDL stays raw); under a custom tablePrefix that names a
|
|
235
|
+
// table that does not exist locally, which SILENTLY breaks crypto-shred:
|
|
236
|
+
// destroyPerRowKey deletes 0 rows and sealed cells stay decryptable after
|
|
237
|
+
// eraseHard. quoteName so b.sql emits the quoted identifier the local path expects.
|
|
234
238
|
var _PER_ROW_SQL_OPTS = { dialect: "sqlite", quoteName: true };
|
|
235
239
|
function _perRowKeysTableName() {
|
|
236
|
-
return
|
|
240
|
+
return PER_ROW_KEYS_TABLE;
|
|
237
241
|
}
|
|
238
242
|
|
|
239
243
|
// Build the canonical AAD parts for a row-secret wrap in
|
|
@@ -1271,19 +1275,19 @@ function unsealRow(table, row, actor, dbHandle) {
|
|
|
1271
1275
|
unsealed = out[field];
|
|
1272
1276
|
}
|
|
1273
1277
|
} catch (e) {
|
|
1274
|
-
// A
|
|
1275
|
-
//
|
|
1276
|
-
//
|
|
1277
|
-
//
|
|
1278
|
-
//
|
|
1279
|
-
//
|
|
1280
|
-
//
|
|
1281
|
-
//
|
|
1282
|
-
|
|
1278
|
+
// A crypto-shredded (or never-materialized) per-row key is an EXPECTED
|
|
1279
|
+
// absence, not a decryption-oracle attack: the wrapped secret is gone,
|
|
1280
|
+
// so there is no oracle to brute-force. Reading such a row must read as
|
|
1281
|
+
// "no value" WITHOUT counting against the rate cap — otherwise a bulk
|
|
1282
|
+
// read over a table with many erased rows (GDPR eraseHard) trips the
|
|
1283
|
+
// cap and DoS's the live rows (self-DoS, CWE-307 mis-applied). It is
|
|
1284
|
+
// audited under a distinct, non-failure action so operators don't alert
|
|
1285
|
+
// on routine post-erasure reads as forged-ciphertext bursts.
|
|
1286
|
+
var _shredded = e && e.code === "crypto-field/row-key-unavailable";
|
|
1283
1287
|
try {
|
|
1284
1288
|
audit().safeEmit({
|
|
1285
|
-
action: "system.crypto.unseal_failed",
|
|
1286
|
-
outcome: "failure",
|
|
1289
|
+
action: _shredded ? "system.crypto.shredded_read" : "system.crypto.unseal_failed",
|
|
1290
|
+
outcome: _shredded ? "success" : "failure",
|
|
1287
1291
|
metadata: {
|
|
1288
1292
|
table: table,
|
|
1289
1293
|
field: field,
|
|
@@ -1293,11 +1297,13 @@ function unsealRow(table, row, actor, dbHandle) {
|
|
|
1293
1297
|
},
|
|
1294
1298
|
});
|
|
1295
1299
|
} catch (_e) { /* drop-silent */ }
|
|
1296
|
-
// Default-on rate cap: account
|
|
1297
|
-
//
|
|
1298
|
-
//
|
|
1299
|
-
//
|
|
1300
|
-
|
|
1300
|
+
// Default-on rate cap: account a genuine decryption / AEAD-verify /
|
|
1301
|
+
// AAD-downgrade failure (a possible forged-ciphertext attack) against
|
|
1302
|
+
// the (actor, table, column) tuple. A shredded-key read is exempt (see
|
|
1303
|
+
// above). When the cap trips the threshold, arm the cooldown + emit the
|
|
1304
|
+
// distinct rate-exceeded audit once on the transition. No-op when the
|
|
1305
|
+
// cap is disabled.
|
|
1306
|
+
if (!_shredded && _rateNoteFailure(capActor, table, field)) {
|
|
1301
1307
|
_emitRateAudit({
|
|
1302
1308
|
table: table, field: field, actor: capActor, shape: shape,
|
|
1303
1309
|
threshold: _rateCap.threshold, windowMs: _rateCap.windowMs, cooldownMs: _rateCap.cooldownMs,
|
|
@@ -963,7 +963,7 @@ var SRI_ALGORITHMS = { "sha256": "sha256", "sha384": "sha384", "sha512": "sha512
|
|
|
963
963
|
function sri(content, opts) {
|
|
964
964
|
opts = opts || {};
|
|
965
965
|
var algorithm = (opts.algorithm || "sha384").toLowerCase();
|
|
966
|
-
if (!SRI_ALGORITHMS
|
|
966
|
+
if (!Object.prototype.hasOwnProperty.call(SRI_ALGORITHMS, algorithm)) {
|
|
967
967
|
throw new Error("crypto.sri: unsupported algorithm '" + algorithm +
|
|
968
968
|
"' (W3C SRI 1.0 §3.2 supports sha256/sha384/sha512)");
|
|
969
969
|
}
|
|
@@ -1356,7 +1356,21 @@ function decrypt(ciphertext, privateKeys, opts) {
|
|
|
1356
1356
|
opts && opts.raw === true ? { raw: true } : undefined);
|
|
1357
1357
|
}
|
|
1358
1358
|
|
|
1359
|
+
// Bounds-checked 2-byte length read — a bare _envU16(packed, pos) on a
|
|
1360
|
+
// truncated envelope throws a raw Node RangeError that escapes the documented
|
|
1361
|
+
// "Invalid envelope: ..." error contract (untrusted ciphertext must fail with
|
|
1362
|
+
// the typed envelope error, not a stack-trace-leaking RangeError).
|
|
1363
|
+
function _envU16(buf, at) {
|
|
1364
|
+
if (at + 2 > buf.length) {
|
|
1365
|
+
throw new Error("Invalid envelope: truncated (expected a 2-byte length at offset " + at + ")");
|
|
1366
|
+
}
|
|
1367
|
+
return buf.readUInt16BE(at);
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1359
1370
|
function decryptEnvelope(packed, privateKeys, internalOpts) {
|
|
1371
|
+
if (!Buffer.isBuffer(packed) || packed.length < 4) {
|
|
1372
|
+
throw new Error("Invalid envelope: too short (need at least the 4-byte suite header)");
|
|
1373
|
+
}
|
|
1360
1374
|
var kemId = packed[1], cipherId = packed[2], kdfId = packed[3], pos = 4;
|
|
1361
1375
|
|
|
1362
1376
|
// The legacy 0xE1 envelope predates the FixedInfo / suite-binding
|
|
@@ -1372,7 +1386,7 @@ function decryptEnvelope(packed, privateKeys, internalOpts) {
|
|
|
1372
1386
|
throw new Error("Invalid envelope: unsupported KDF (only SHAKE256 supported)");
|
|
1373
1387
|
}
|
|
1374
1388
|
|
|
1375
|
-
var kemCtLen = packed
|
|
1389
|
+
var kemCtLen = _envU16(packed, pos); pos += 2;
|
|
1376
1390
|
var kemCt = packed.subarray(pos, pos + kemCtLen); pos += kemCtLen;
|
|
1377
1391
|
|
|
1378
1392
|
var mlkemPriv = nodeCrypto.createPrivateKey(
|
|
@@ -1383,7 +1397,7 @@ function decryptEnvelope(packed, privateKeys, internalOpts) {
|
|
|
1383
1397
|
var fixedInfo = omitFixedInfo ? Buffer.alloc(0) : _suiteFixedInfo(kemId, cipherId, kdfId);
|
|
1384
1398
|
|
|
1385
1399
|
if (kemId === C.KEM_IDS.ML_KEM_1024_P384) {
|
|
1386
|
-
var ecEphLen = packed
|
|
1400
|
+
var ecEphLen = _envU16(packed, pos); pos += 2;
|
|
1387
1401
|
var ecEphDer = packed.subarray(pos, pos + ecEphLen); pos += ecEphLen;
|
|
1388
1402
|
var ecPrivPem = typeof privateKeys === "string" ? null : privateKeys.ecPrivateKey;
|
|
1389
1403
|
if (!ecPrivPem) throw new Error("Hybrid KEM requires EC private key");
|
|
@@ -1400,7 +1414,7 @@ function decryptEnvelope(packed, privateKeys, internalOpts) {
|
|
|
1400
1414
|
// the correct keypair via privateKeys when the envelope was sealed
|
|
1401
1415
|
// with this algorithm. Same length-prefixed shape as the P-384
|
|
1402
1416
|
// hybrid: 2-byte ec-eph-len + DER X25519 pubkey + nonce + ct.
|
|
1403
|
-
var x25519EphLen = packed
|
|
1417
|
+
var x25519EphLen = _envU16(packed, pos); pos += 2;
|
|
1404
1418
|
var x25519EphDer = packed.subarray(pos, pos + x25519EphLen); pos += x25519EphLen;
|
|
1405
1419
|
var x25519PrivPem = typeof privateKeys === "string" ? null : privateKeys.x25519PrivateKey;
|
|
1406
1420
|
if (!x25519PrivPem) throw new Error("ML-KEM-768 + X25519 hybrid envelope requires x25519PrivateKey");
|
|
@@ -416,6 +416,10 @@ function mergeDirectives(base, additions, opts) {
|
|
|
416
416
|
? directives[name].slice()
|
|
417
417
|
: (directives["default-src"] ? directives["default-src"].slice() : ["'self'"]);
|
|
418
418
|
var added = additions[name];
|
|
419
|
+
// A directive value of 'none' MUST stand alone (CSP3 §2.3.1) — appending a
|
|
420
|
+
// host to it emits the malformed "'none' https://x". When merging real
|
|
421
|
+
// sources in, the added sources supersede 'none', so drop it first.
|
|
422
|
+
if (added.length && existing.length === 1 && existing[0] === "'none'") existing = [];
|
|
419
423
|
for (var si = 0; si < added.length; si += 1) {
|
|
420
424
|
if (existing.indexOf(added[si]) === -1) existing.push(added[si]);
|
|
421
425
|
}
|
|
@@ -73,7 +73,10 @@ function _isLivePid(pid) {
|
|
|
73
73
|
|
|
74
74
|
function _readPidFile(pidFile) {
|
|
75
75
|
try {
|
|
76
|
-
|
|
76
|
+
// Same fd-safe + capped + symlink-refusing read as app-shutdown's lockfile
|
|
77
|
+
// reader (one shape): a PID file is never a legit symlink mount, so
|
|
78
|
+
// refuseSymlink is safe; any throw → null ("nothing live there").
|
|
79
|
+
var raw = atomicFile.fdSafeReadSync(pidFile, { maxBytes: C.BYTES.kib(1), refuseSymlink: true, encoding: "utf8" });
|
|
77
80
|
var pid = parseInt(String(raw).trim(), 10);
|
|
78
81
|
return isFinite(pid) && pid > 0 ? pid : null;
|
|
79
82
|
} catch (_e) { return null; }
|
|
@@ -89,6 +89,32 @@ var DESIGNATED_GATEKEEPERS = Object.freeze([
|
|
|
89
89
|
"booking",
|
|
90
90
|
]);
|
|
91
91
|
|
|
92
|
+
// Is `recipient` a designated DMA gatekeeper? Match the gatekeeper token as a
|
|
93
|
+
// DNS LABEL of the recipient's host authority — any position, including the
|
|
94
|
+
// FINAL label. A substring `indexOf(g + ".")` test missed a gatekeeper that is
|
|
95
|
+
// the last label (e.g. "blog.google" / "drive.google" on the real ".google"
|
|
96
|
+
// TLD, or "data.amazon"), letting an Art 32 §1 third-party share through — and
|
|
97
|
+
// also false-flagged a non-gatekeeper whose name merely contained the token
|
|
98
|
+
// ("notgoogle.com"). Label matching closes the bypass and drops the false
|
|
99
|
+
// positive. A bare name ("Google") still matches exactly. URL / userinfo / port
|
|
100
|
+
// forms are normalized to the host authority first.
|
|
101
|
+
function _isGatekeeperRecipient(recipient) {
|
|
102
|
+
var s = String(recipient).toLowerCase().trim();
|
|
103
|
+
var scheme = s.indexOf("://");
|
|
104
|
+
if (scheme !== -1) s = s.slice(scheme + 3);
|
|
105
|
+
s = s.split("/")[0].split("?")[0].split("#")[0]; // drop path / query / fragment
|
|
106
|
+
var at = s.lastIndexOf("@");
|
|
107
|
+
if (at !== -1) s = s.slice(at + 1); // drop userinfo
|
|
108
|
+
s = s.split(":")[0]; // drop port
|
|
109
|
+
if (s.length === 0) return false;
|
|
110
|
+
var labels = s.split(".");
|
|
111
|
+
return DESIGNATED_GATEKEEPERS.some(function (g) {
|
|
112
|
+
if (s === g) return true;
|
|
113
|
+
for (var i = 0; i < labels.length; i++) { if (labels[i] === g) return true; }
|
|
114
|
+
return false;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
92
118
|
/**
|
|
93
119
|
* @primitive b.dataAct.declareProduct
|
|
94
120
|
* @signature b.dataAct.declareProduct(opts)
|
|
@@ -229,10 +255,7 @@ function shareWithThirdParty(opts) {
|
|
|
229
255
|
acceptGatekeeper: "optional-plain-object",
|
|
230
256
|
}, "dataAct.shareWithThirdParty", DataActError, "dataact/bad-opts");
|
|
231
257
|
|
|
232
|
-
var
|
|
233
|
-
var isGatekeeper = DESIGNATED_GATEKEEPERS.some(function (g) {
|
|
234
|
-
return recipientLower === g || recipientLower.indexOf(g + ".") !== -1;
|
|
235
|
-
});
|
|
258
|
+
var isGatekeeper = _isGatekeeperRecipient(opts.recipient);
|
|
236
259
|
if (isGatekeeper) {
|
|
237
260
|
if (!opts.acceptGatekeeper || typeof opts.acceptGatekeeper !== "object" ||
|
|
238
261
|
typeof opts.acceptGatekeeper.reason !== "string" ||
|
|
@@ -172,8 +172,11 @@ function fileLifecycle(opts) {
|
|
|
172
172
|
: nodePath.join(opts.dataDir, frameworkFiles.fileName("dbKeyEnc"));
|
|
173
173
|
var flushIntervalMs = opts.flushIntervalMs || DEFAULT_FLUSH_INTERVAL_MS;
|
|
174
174
|
var tmpDir = _resolveTmpDir(opts.tmpDir, opts.allowDiskFallback === true);
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
// 0o700 — these hold the DB encryption tmpfs scratch + the data dir
|
|
176
|
+
// (db.key.enc, the decrypted DB while open); a default-mode mkdir leaves
|
|
177
|
+
// them group/other-traversable.
|
|
178
|
+
if (!nodeFs.existsSync(tmpDir)) nodeFs.mkdirSync(tmpDir, { recursive: true, mode: 0o700 });
|
|
179
|
+
if (!nodeFs.existsSync(opts.dataDir)) nodeFs.mkdirSync(opts.dataDir, { recursive: true, mode: 0o700 });
|
|
177
180
|
|
|
178
181
|
var dbPath = null;
|
|
179
182
|
var encKey = null;
|
|
@@ -183,7 +186,9 @@ function fileLifecycle(opts) {
|
|
|
183
186
|
function _loadOrGenerateKey() {
|
|
184
187
|
if (encKey) return encKey;
|
|
185
188
|
if (nodeFs.existsSync(keyPath)) {
|
|
186
|
-
|
|
189
|
+
// Cap + fd-bound. NO refuseSymlink: dbKeyPath may be an operator-absolute
|
|
190
|
+
// KMS-fronted volume that legitimately symlinks the sealed key.
|
|
191
|
+
var sealedKey = atomicFile.fdSafeReadSync(keyPath, { maxBytes: C.BYTES.kib(64), encoding: "utf8" });
|
|
187
192
|
var keyB64;
|
|
188
193
|
try { keyB64 = opts.vault.unseal(sealedKey); }
|
|
189
194
|
catch (e) {
|
|
@@ -212,7 +217,10 @@ function fileLifecycle(opts) {
|
|
|
212
217
|
dbPath = nodePath.join(tmpDir, "blamejs-fl-" + label + "-" +
|
|
213
218
|
generateToken(TMP_NAME_BYTES) + ".db");
|
|
214
219
|
if (nodeFs.existsSync(encPath)) {
|
|
215
|
-
|
|
220
|
+
// Cap + fd-bound (deployment-tunable ceiling). NO refuseSymlink: the data
|
|
221
|
+
// dir may be a symlink-mounted volume (k8s PVC). db.enc is AEAD-checked
|
|
222
|
+
// downstream, so the cap is the OOM-before-cap defense.
|
|
223
|
+
var packed = atomicFile.fdSafeReadSync(encPath, { maxBytes: (opts.maxDbBytes) || C.BYTES.gib(2) });
|
|
216
224
|
if (packed.length < 26) { // minimum envelope length
|
|
217
225
|
throw new DbFileLifecycleError("db-file-lifecycle/short-envelope",
|
|
218
226
|
"fileLifecycle: " + encPath + " too short to be a valid envelope (" + packed.length + " bytes)");
|
|
@@ -248,7 +256,7 @@ function fileLifecycle(opts) {
|
|
|
248
256
|
catch (_e) { /* best-effort — operators on read-only handles or pre-init still flush */ }
|
|
249
257
|
}
|
|
250
258
|
if (!nodeFs.existsSync(dbPath)) return null;
|
|
251
|
-
var plain =
|
|
259
|
+
var plain = atomicFile.fdSafeReadSync(dbPath, { maxBytes: (opts.maxDbBytes) || C.BYTES.gib(2) });
|
|
252
260
|
var packed = encryptPacked(plain, encKey, _aad(opts.dataDir, label));
|
|
253
261
|
atomicFile.writeSync(encPath, packed);
|
|
254
262
|
_emitAudit("flushed", "success", { label: label, bytes: plain.length });
|
|
@@ -269,7 +277,7 @@ function fileLifecycle(opts) {
|
|
|
269
277
|
throw new DbFileLifecycleError("db-file-lifecycle/no-source",
|
|
270
278
|
"fileLifecycle.snapshot: " + dbPath + " is missing");
|
|
271
279
|
}
|
|
272
|
-
var plain =
|
|
280
|
+
var plain = atomicFile.fdSafeReadSync(dbPath, { maxBytes: (opts.maxDbBytes) || C.BYTES.gib(2) });
|
|
273
281
|
return encryptPacked(plain, encKey, _aad(opts.dataDir, label));
|
|
274
282
|
}
|
|
275
283
|
|
|
@@ -1378,12 +1378,62 @@ function _unquoteIdent(s) {
|
|
|
1378
1378
|
return s;
|
|
1379
1379
|
}
|
|
1380
1380
|
|
|
1381
|
+
// Strip LEADING SQL comments + whitespace so the ^-anchored write-detection
|
|
1382
|
+
// regexes see the real statement head. Without this, a residency write smuggled
|
|
1383
|
+
// behind a leading "/* x */" or "-- x\n" comment is NOT recognized as a write →
|
|
1384
|
+
// the residency gate is skipped entirely (a cross-border-write BYPASS). The
|
|
1385
|
+
// executed SQL is unchanged; this normalized copy is only for the gate's parse.
|
|
1386
|
+
// Each replace is ^-anchored single-pass; the loop terminates when nothing more
|
|
1387
|
+
// is stripped (an unterminated /* leaves the head intact → write still detected
|
|
1388
|
+
// or fails closed downstream).
|
|
1389
|
+
function _stripLeadingSqlComments(sql) {
|
|
1390
|
+
var s = String(sql), prev;
|
|
1391
|
+
do {
|
|
1392
|
+
prev = s;
|
|
1393
|
+
s = s.replace(/^\s+/, ""); // allow:regex-no-length-cap — anchored, single leading run
|
|
1394
|
+
s = s.replace(/^--[^\n]*\r?\n?/, ""); // allow:regex-no-length-cap — anchored leading line comment
|
|
1395
|
+
s = s.replace(/^\/\*[\s\S]*?\*\//, ""); // allow:regex-no-length-cap — anchored leading block comment (lazy, single scan)
|
|
1396
|
+
} while (s !== prev);
|
|
1397
|
+
return s;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// Non-anchored write-target scan for the writable-CTE / EXPLAIN-prefixed case.
|
|
1401
|
+
// A SQLite `WITH c AS (...) INSERT INTO residents ...` / `WITH ... UPDATE residents
|
|
1402
|
+
// SET ...` is a real write, but its effective verb is hidden behind the prefix so
|
|
1403
|
+
// the ^-anchored _RAW_WRITE_KEYWORD_RE misses it. This matches every INSERT/REPLACE
|
|
1404
|
+
// INTO, MERGE INTO, and UPDATE ... SET target token anywhere in the statement and
|
|
1405
|
+
// returns the first that names a residency table — so such a write still ENGAGES
|
|
1406
|
+
// the residency gate (_assertRawWriteResidency then fails CLOSED: the ^-anchored
|
|
1407
|
+
// body parsers can't read a CTE body, so it throws row-residency-raw-unparseable
|
|
1408
|
+
// directing the operator to b.db.from().insertOne/.updateOne). DELETE moves no
|
|
1409
|
+
// residency value across a border, so it is not a residency write. Linear scan.
|
|
1410
|
+
var _CTE_WRITE_TARGET_RE = /(?:\b(?:INSERT|REPLACE)\s+(?:OR\s+[A-Za-z]+\s+)?INTO|\bMERGE\s+INTO)\s+(?:[\x22\x27\x60]?[A-Za-z_]\w*[\x22\x27\x60]?\s*\.\s*){0,3}[\x22\x27\x60]?([A-Za-z_]\w*)[\x22\x27\x60]?|\bUPDATE\s+(?:[\x22\x27\x60]?[A-Za-z_]\w*[\x22\x27\x60]?\s*\.\s*){0,3}[\x22\x27\x60]?([A-Za-z_]\w*)[\x22\x27\x60]?\s+SET\b/ig; // allow:regex-no-length-cap — alternation, no nested quantifiers; linear
|
|
1411
|
+
function _firstResidencyWriteTarget(s) {
|
|
1412
|
+
_CTE_WRITE_TARGET_RE.lastIndex = 0;
|
|
1413
|
+
var m;
|
|
1414
|
+
while ((m = _CTE_WRITE_TARGET_RE.exec(s)) !== null) { // allow:regex-no-length-cap
|
|
1415
|
+
var t = _unquoteIdent(m[1] || m[2]);
|
|
1416
|
+
if (t && (cryptoField.getPerRowResidency(t) || cryptoField.getColumnResidency(t))) return t;
|
|
1417
|
+
}
|
|
1418
|
+
return null;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1381
1421
|
function _rawWriteTable(sql) {
|
|
1382
|
-
//
|
|
1383
|
-
//
|
|
1384
|
-
if (typeof sql !== "string"
|
|
1385
|
-
var
|
|
1386
|
-
|
|
1422
|
+
// The ^-anchored regexes scan only the statement head (constant-time). Strip
|
|
1423
|
+
// leading comments first so a commented-out head can't hide the write.
|
|
1424
|
+
if (typeof sql !== "string") return null;
|
|
1425
|
+
var s = _stripLeadingSqlComments(sql);
|
|
1426
|
+
if (_RAW_WRITE_KEYWORD_RE.test(s)) { // allow:regex-no-length-cap
|
|
1427
|
+
var m = _RAW_TABLE_RE.exec(s); // allow:regex-no-length-cap
|
|
1428
|
+
return m ? _unquoteIdent(m[1] || m[2]) : null;
|
|
1429
|
+
}
|
|
1430
|
+
// Writable-CTE / EXPLAIN-prefixed write: the effective write verb is hidden
|
|
1431
|
+
// behind the prefix the ^-anchored test misses. If it writes a residency table,
|
|
1432
|
+
// return it so the gate engages and then fails closed on the unparseable body.
|
|
1433
|
+
if (/^\s*(?:WITH|EXPLAIN)\b/i.test(s)) { // allow:regex-no-length-cap
|
|
1434
|
+
return _firstResidencyWriteTarget(s);
|
|
1435
|
+
}
|
|
1436
|
+
return null;
|
|
1387
1437
|
}
|
|
1388
1438
|
|
|
1389
1439
|
// Cheap prepare-time pre-check so only writes to a residency table get wrapped.
|
|
@@ -1463,17 +1513,23 @@ function _assertRawWriteResidency(sql, boundParams) {
|
|
|
1463
1513
|
if (!cryptoField.getPerRowResidency(table) && !cryptoField.getColumnResidency(table)) return;
|
|
1464
1514
|
boundParams = _flattenRunParams(boundParams);
|
|
1465
1515
|
|
|
1516
|
+
// Parse the comment-stripped head: a leading "/* x */" / "-- x" comment must
|
|
1517
|
+
// not hide the INSERT/UPDATE body from the ^-anchored regexes below (that would
|
|
1518
|
+
// let a residency-restricted write through the gate). The executed SQL is
|
|
1519
|
+
// unchanged; this normalized copy is only for residency parsing.
|
|
1520
|
+
var norm = _stripLeadingSqlComments(sql);
|
|
1521
|
+
|
|
1466
1522
|
// The INSERT/UPDATE body regexes below scan with [\s\S]+; bound the input
|
|
1467
1523
|
// first and fail CLOSED on an over-long statement - a residency write the
|
|
1468
1524
|
// framework cannot safely parse must be refused, never let past the gate.
|
|
1469
|
-
if (
|
|
1525
|
+
if (norm.length > 100000) {
|
|
1470
1526
|
throw new DbQueryError("db-query/row-residency-raw-unparseable",
|
|
1471
1527
|
"raw write to residency table '" + table + "' exceeds the parse limit (" +
|
|
1472
|
-
|
|
1528
|
+
norm.length + " chars) - use b.db.from(\"" + table + "\") so residency is validated", true);
|
|
1473
1529
|
}
|
|
1474
1530
|
|
|
1475
|
-
var mi = _RAW_INSERT_RE.exec(
|
|
1476
|
-
var mu = mi ? null : _RAW_UPDATE_RE.exec(
|
|
1531
|
+
var mi = _RAW_INSERT_RE.exec(norm); // allow:regex-no-length-cap — input length-capped above
|
|
1532
|
+
var mu = mi ? null : _RAW_UPDATE_RE.exec(norm); // allow:regex-no-length-cap — input length-capped above
|
|
1477
1533
|
if (!mi && !mu) {
|
|
1478
1534
|
throw new DbQueryError("db-query/row-residency-raw-unparseable",
|
|
1479
1535
|
"raw write to residency table '" + table + "' cannot be parsed to validate its " +
|
|
@@ -1509,4 +1565,8 @@ module.exports = {
|
|
|
1509
1565
|
Query: Query,
|
|
1510
1566
|
_isRawWriteToResidencyTable: _isRawWriteToResidencyTable,
|
|
1511
1567
|
_assertRawWriteResidency: _assertRawWriteResidency,
|
|
1568
|
+
// Shared leading-comment stripper so db.js's storage-low write-gate sees the
|
|
1569
|
+
// real statement head (a `/* x */ INSERT` / WITH-prefixed write must not slip
|
|
1570
|
+
// the ENOSPC gate the way it slipped the residency gate).
|
|
1571
|
+
_stripLeadingSqlComments: _stripLeadingSqlComments,
|
|
1512
1572
|
};
|