@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
|
@@ -57,7 +57,7 @@ var { generateToken, generateBytes, encryptPacked, decryptPacked, sha3Hash } = r
|
|
|
57
57
|
var cryptoField = require("./crypto-field");
|
|
58
58
|
var dbDeclareRowPolicy = require("./db-declare-row-policy");
|
|
59
59
|
var dbDeclareView = require("./db-declare-view");
|
|
60
|
-
var { Query, _isRawWriteToResidencyTable, _assertRawWriteResidency } = require("./db-query");
|
|
60
|
+
var { Query, _isRawWriteToResidencyTable, _assertRawWriteResidency, _stripLeadingSqlComments } = require("./db-query");
|
|
61
61
|
var dbSchema = require("./db-schema");
|
|
62
62
|
var { defineClass } = require("./framework-error");
|
|
63
63
|
var frameworkFiles = require("./framework-files");
|
|
@@ -748,7 +748,7 @@ function loadOrCreateDbKey(dataDirPath, keyPathOverride) {
|
|
|
748
748
|
var keyPath = keyPathOverride || nodePath.join(dataDirPath, frameworkFiles.fileName("dbKeyEnc"));
|
|
749
749
|
var aad = _dbKeyAad(dataDirPath, keyPath);
|
|
750
750
|
if (nodeFs.existsSync(keyPath)) {
|
|
751
|
-
var sealed = atomicFile.readSync(keyPath, { encoding: "utf8" }).trim();
|
|
751
|
+
var sealed = atomicFile.readSync(keyPath, { encoding: "utf8", maxBytes: C.BYTES.kib(64) }).trim();
|
|
752
752
|
var b64;
|
|
753
753
|
// isAadSealed is checked FIRST and is load-bearing: AAD_PREFIX
|
|
754
754
|
// ("vault.aad:") is NOT a prefix of VAULT_PREFIX ("vault:"), so a
|
|
@@ -811,7 +811,10 @@ function decryptToTmp() {
|
|
|
811
811
|
try { nodeFs.unlinkSync(dbPath + "-shm"); } catch (_e) { /* may not exist */ }
|
|
812
812
|
}
|
|
813
813
|
}
|
|
814
|
-
|
|
814
|
+
// Cap + fd-bound db.enc read at db.init. NO refuseSymlink: the data dir may be
|
|
815
|
+
// a symlink-mounted volume (k8s PVC). db.enc is AEAD-verified downstream, so
|
|
816
|
+
// the cap is the OOM-before-cap defense (was an uncapped read).
|
|
817
|
+
var packed = atomicFile.fdSafeReadSync(encPath, { maxBytes: C.BYTES.gib(2) });
|
|
815
818
|
if (packed.length < 26) return; // too short to be a valid envelope
|
|
816
819
|
// AAD binds the envelope to this deployment's data dir so two
|
|
817
820
|
// installs sharing the same operator passphrase can't swap each
|
|
@@ -890,11 +893,27 @@ function _probeStorageHeadroom() {
|
|
|
890
893
|
// DELETE, PRAGMA, and DDL pass through ungated. Called once in init() after
|
|
891
894
|
// schema setup so init's own writes are never gated (writesRefused is false
|
|
892
895
|
// until the first probe anyway).
|
|
896
|
+
// A growth write for the storage-low gate: INSERT/UPDATE/REPLACE at the head
|
|
897
|
+
// AFTER stripping leading comments, OR a writable-CTE / EXPLAIN-prefixed write
|
|
898
|
+
// (`WITH c AS (...) INSERT INTO t ...`) whose effective verb places rows. The
|
|
899
|
+
// leading-keyword-only test missed both a `/* x */ INSERT` and a WITH-prefixed
|
|
900
|
+
// growth write, letting them proceed into a near-full tmpfs (the ENOSPC the gate
|
|
901
|
+
// exists to prevent). Matches write SYNTAX (INTO / UPDATE..SET) so a WITH..SELECT
|
|
902
|
+
// carrying the word "insert" in a value is not refused.
|
|
903
|
+
function _isGrowthWrite(sql) {
|
|
904
|
+
if (typeof sql !== "string") return false;
|
|
905
|
+
var s = _stripLeadingSqlComments(sql);
|
|
906
|
+
if (/^\s*(?:INSERT|UPDATE|REPLACE)\b/i.test(s)) return true;
|
|
907
|
+
if (/^\s*(?:WITH|EXPLAIN)\b/i.test(s) &&
|
|
908
|
+
/\b(?:(?:INSERT|REPLACE|MERGE)\s+(?:OR\s+[A-Za-z]+\s+)?INTO|UPDATE\s+[\w".`]+\s+SET)\b/i.test(s)) return true;
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
|
|
893
912
|
function _installWriteGate() {
|
|
894
913
|
var rawPrepare = database.prepare.bind(database);
|
|
895
914
|
database.prepare = function (sql) {
|
|
896
915
|
var stmt = rawPrepare(sql);
|
|
897
|
-
if (
|
|
916
|
+
if (_isGrowthWrite(sql)) {
|
|
898
917
|
var rawRun = stmt.run.bind(stmt);
|
|
899
918
|
stmt.run = function () {
|
|
900
919
|
if (writesRefused) {
|
|
@@ -915,7 +934,7 @@ function encryptToDisk() {
|
|
|
915
934
|
// Force WAL checkpoint so the .db file holds all committed transactions.
|
|
916
935
|
try { runSql(database, "PRAGMA wal_checkpoint(TRUNCATE)"); } catch (_e) { /* best effort */ }
|
|
917
936
|
if (!nodeFs.existsSync(dbPath)) return;
|
|
918
|
-
atomicFile.writeSync(encPath, encryptPacked(
|
|
937
|
+
atomicFile.writeSync(encPath, encryptPacked(atomicFile.fdSafeReadSync(dbPath, { maxBytes: C.BYTES.gib(2) }), encKey, _dbEncAad(dataDir)));
|
|
919
938
|
}
|
|
920
939
|
|
|
921
940
|
/**
|
|
@@ -952,7 +971,7 @@ function snapshot() {
|
|
|
952
971
|
throw _dbErr("db/snapshot-no-source",
|
|
953
972
|
"snapshot: plaintext DB at " + dbPath + " is missing — did init complete?");
|
|
954
973
|
}
|
|
955
|
-
var plain =
|
|
974
|
+
var plain = atomicFile.fdSafeReadSync(dbPath, { maxBytes: C.BYTES.gib(2) });
|
|
956
975
|
if (!encPath || !encKey) {
|
|
957
976
|
// atRest: 'plain' — return the raw bytes. Operators wanting an
|
|
958
977
|
// encrypted snapshot under plain mode wrap with their own
|
|
@@ -2611,14 +2630,12 @@ function declareRequireDualControl(args) {
|
|
|
2611
2630
|
}
|
|
2612
2631
|
var m = args.m === undefined ? 2 : args.m;
|
|
2613
2632
|
var n = args.n === undefined ? Math.max(2, m) : args.n;
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
"declareRequireDualControl: n must be an integer >= m");
|
|
2621
|
-
}
|
|
2633
|
+
// m >= 2, n >= m, both integers — via numericBounds (matches db.js's existing
|
|
2634
|
+
// inline numeric-bounds usage; throws DbError("db/dual-control-bad-quorum")).
|
|
2635
|
+
require("./numeric-bounds").requirePositiveFiniteInt(m,
|
|
2636
|
+
"declareRequireDualControl: m", DbError, "db/dual-control-bad-quorum", { min: 2 });
|
|
2637
|
+
require("./numeric-bounds").requirePositiveFiniteInt(n,
|
|
2638
|
+
"declareRequireDualControl: n", DbError, "db/dual-control-bad-quorum", { min: m });
|
|
2622
2639
|
if (args.posture !== undefined && args.posture !== null &&
|
|
2623
2640
|
(typeof args.posture !== "string" || args.posture.length === 0)) {
|
|
2624
2641
|
throw new DbError("db/dual-control-bad-posture",
|
|
@@ -2785,7 +2802,7 @@ function _checkRollback(dataDirPath) {
|
|
|
2785
2802
|
}
|
|
2786
2803
|
var tip;
|
|
2787
2804
|
try {
|
|
2788
|
-
tip = safeJson.parse(atomicFile.readSync(tipPath), { schema: AUDIT_TIP_SCHEMA });
|
|
2805
|
+
tip = safeJson.parse(atomicFile.readSync(tipPath, { maxBytes: C.BYTES.kib(64) }), { schema: AUDIT_TIP_SCHEMA });
|
|
2789
2806
|
} catch (e) {
|
|
2790
2807
|
throw _dbErr("db/audit-tip-unreadable",
|
|
2791
2808
|
"FATAL: audit.tip unreadable or schema-invalid at " + tipPath + " — " + e.message +
|
|
@@ -62,12 +62,19 @@ var SIGNIFICANT_INCIDENT_THRESHOLDS = Object.freeze({
|
|
|
62
62
|
durationCriticalProcessMs: C.TIME.hours(2), // 2h
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
// DORA major-incident reporting timeline (Art. 19 + RTS (EU) 2024/1772 Art. 5):
|
|
66
|
+
// - Initial notification: as early as possible and WITHIN 4 HOURS of
|
|
67
|
+
// classifying the incident as major (the outer bound is 24h from first
|
|
68
|
+
// awareness). classify() returns mustReportInitialByMs as a duration FROM
|
|
69
|
+
// classification, so the operative window encoded here is 4h — the
|
|
70
|
+
// 24h-from-awareness outer bound is enforced separately via
|
|
71
|
+
// INITIAL_REPORT_OUTER_DEADLINE_MS below.
|
|
72
|
+
// - Intermediate update: within 72h of the INITIAL notification's submission.
|
|
73
|
+
// - Final report: within 1 month of the intermediate report's submission.
|
|
74
|
+
var INITIAL_REPORT_DEADLINE_MS = C.TIME.hours(4);
|
|
75
|
+
var INITIAL_REPORT_OUTER_DEADLINE_MS = C.TIME.hours(24);
|
|
76
|
+
var INTERMEDIATE_REPORT_DEADLINE_MS = C.TIME.hours(72);
|
|
77
|
+
var FINAL_REPORT_DEADLINE_MS = C.TIME.days(30);
|
|
71
78
|
|
|
72
79
|
// Adjacent-regulation incident-reporting deadlines — operators wiring
|
|
73
80
|
// NIS2 / CRA / HIPAA breach notification reach for these constants
|
|
@@ -124,14 +131,26 @@ function _classifyImpl(input) {
|
|
|
124
131
|
reasons.push("severity-high");
|
|
125
132
|
}
|
|
126
133
|
|
|
127
|
-
// 2. Affected clients
|
|
134
|
+
// 2. Affected clients — absolute count OR share of the client base. The
|
|
135
|
+
// percentile thresholds (RTS 2024/1772 Art. 1(1)(a): >10% major, ESA-
|
|
136
|
+
// guideline 1% significant) were declared but never evaluated, so a
|
|
137
|
+
// large-share-but-small-absolute incident (e.g. 16% of a 50k client base)
|
|
138
|
+
// under-classified. Operators pass clientBase (total clients) to enable
|
|
139
|
+
// the percentage criterion; absolute-only behavior is unchanged when it
|
|
140
|
+
// is omitted.
|
|
128
141
|
if (typeof input.affectedClients === "number" && input.affectedClients > 0) {
|
|
129
|
-
|
|
142
|
+
var clientBase = (typeof input.clientBase === "number" && input.clientBase > 0) ? input.clientBase : null;
|
|
143
|
+
var clientPct = clientBase ? (input.affectedClients / clientBase) : 0;
|
|
144
|
+
var majorByClients = input.affectedClients >= MAJOR_INCIDENT_THRESHOLDS.affectedClientsAbsolute ||
|
|
145
|
+
(clientBase !== null && clientPct >= MAJOR_INCIDENT_THRESHOLDS.affectedClientsPercentile);
|
|
146
|
+
var significantByClients = input.affectedClients >= SIGNIFICANT_INCIDENT_THRESHOLDS.affectedClientsAbsolute ||
|
|
147
|
+
(clientBase !== null && clientPct >= SIGNIFICANT_INCIDENT_THRESHOLDS.affectedClientsPercentile);
|
|
148
|
+
if (majorByClients) {
|
|
130
149
|
hitsMajor += 1;
|
|
131
|
-
reasons.push("clients-major
|
|
132
|
-
} else if (
|
|
150
|
+
reasons.push("clients-major");
|
|
151
|
+
} else if (significantByClients) {
|
|
133
152
|
hitsSignificant += 1;
|
|
134
|
-
reasons.push("clients-significant
|
|
153
|
+
reasons.push("clients-significant");
|
|
135
154
|
}
|
|
136
155
|
}
|
|
137
156
|
|
|
@@ -188,7 +207,11 @@ function _classifyImpl(input) {
|
|
|
188
207
|
return {
|
|
189
208
|
classification: classification,
|
|
190
209
|
mustReport: mustReport,
|
|
210
|
+
// Operative initial-notification window: 4h from this classification.
|
|
191
211
|
mustReportInitialByMs: mustReport ? INITIAL_REPORT_DEADLINE_MS : null,
|
|
212
|
+
// Outer bound: no later than 24h from first awareness — surfaced so an
|
|
213
|
+
// operator scheduling from `detectedAt` can enforce min(class+4h, aware+24h).
|
|
214
|
+
mustReportInitialOuterByMs: mustReport ? INITIAL_REPORT_OUTER_DEADLINE_MS : null,
|
|
192
215
|
reasons: reasons,
|
|
193
216
|
};
|
|
194
217
|
}
|
|
@@ -343,10 +366,16 @@ function create(opts) {
|
|
|
343
366
|
// Article 19 deadline — operator-side scheduler uses this.
|
|
344
367
|
nextStageDueAt: null,
|
|
345
368
|
};
|
|
369
|
+
// Anchor each downstream deadline on the SUBMISSION time of the report
|
|
370
|
+
// being filed (record.reportedAt), not on detectedAt: the intermediate is
|
|
371
|
+
// due 72h after the initial is *submitted*, and the final one month after
|
|
372
|
+
// the intermediate is *submitted* (DORA RTS 2024/1772 reporting timeline).
|
|
373
|
+
// Anchoring on detectedAt understates the deadline whenever a report is
|
|
374
|
+
// filed later than detection.
|
|
346
375
|
if (input.stage === "initial") {
|
|
347
|
-
record.nextStageDueAt =
|
|
376
|
+
record.nextStageDueAt = record.reportedAt + INTERMEDIATE_REPORT_DEADLINE_MS;
|
|
348
377
|
} else if (input.stage === "intermediate") {
|
|
349
|
-
record.nextStageDueAt =
|
|
378
|
+
record.nextStageDueAt = record.reportedAt + FINAL_REPORT_DEADLINE_MS;
|
|
350
379
|
}
|
|
351
380
|
_emit("dora.incident.reported", {
|
|
352
381
|
metadata: {
|
|
@@ -396,6 +425,7 @@ module.exports = {
|
|
|
396
425
|
MAJOR_INCIDENT_THRESHOLDS: MAJOR_INCIDENT_THRESHOLDS,
|
|
397
426
|
SIGNIFICANT_INCIDENT_THRESHOLDS: SIGNIFICANT_INCIDENT_THRESHOLDS,
|
|
398
427
|
INITIAL_REPORT_DEADLINE_MS: INITIAL_REPORT_DEADLINE_MS,
|
|
428
|
+
INITIAL_REPORT_OUTER_DEADLINE_MS: INITIAL_REPORT_OUTER_DEADLINE_MS,
|
|
399
429
|
INTERMEDIATE_REPORT_DEADLINE_MS: INTERMEDIATE_REPORT_DEADLINE_MS,
|
|
400
430
|
FINAL_REPORT_DEADLINE_MS: FINAL_REPORT_DEADLINE_MS,
|
|
401
431
|
DEADLINES_NIS2: DEADLINES_NIS2,
|
|
@@ -275,7 +275,7 @@ async function emit(opts) {
|
|
|
275
275
|
"drRunbook.emit: outDir", DrRunbookError, "dr-runbook/no-outdir");
|
|
276
276
|
validateOpts.requireNonEmptyString(opts.posture,
|
|
277
277
|
"drRunbook.emit: posture", DrRunbookError, "dr-runbook/no-posture");
|
|
278
|
-
if (!POSTURE_BLOCKS
|
|
278
|
+
if (!Object.prototype.hasOwnProperty.call(POSTURE_BLOCKS, opts.posture)) {
|
|
279
279
|
throw new DrRunbookError("dr-runbook/unknown-posture",
|
|
280
280
|
"drRunbook.emit: posture '" + opts.posture + "' not in supported list (" +
|
|
281
281
|
Object.keys(POSTURE_BLOCKS).join(", ") + ")");
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
var validateOpts = require("./validate-opts");
|
|
38
38
|
var lazyRequire = require("./lazy-require");
|
|
39
|
+
var numericBounds = require("./numeric-bounds");
|
|
39
40
|
var C = require("./constants");
|
|
40
41
|
var { DsaError } = require("./framework-error");
|
|
41
42
|
|
|
@@ -414,7 +415,7 @@ function transparencyReport(opts) {
|
|
|
414
415
|
METRIC_FIELDS.forEach(function (field) {
|
|
415
416
|
var v = supplied[field];
|
|
416
417
|
if (v === undefined || v === null) { metrics[field] = 0; return; }
|
|
417
|
-
if (
|
|
418
|
+
if (!numericBounds.isNonNegativeFiniteInt(v)) {
|
|
418
419
|
throw new DsaError("dsa/bad-metric-value",
|
|
419
420
|
"b.dsa.transparencyReport: metrics." + field +
|
|
420
421
|
" must be a non-negative integer, got " +
|
|
@@ -210,7 +210,7 @@ var POSTURE_DEADLINE_MS = Object.freeze({
|
|
|
210
210
|
// + extension). Read-only at module scope.
|
|
211
211
|
function _deadlineForPosture(posture) {
|
|
212
212
|
if (typeof posture !== "string") return POSTURE_DEADLINE_MS["default"];
|
|
213
|
-
return POSTURE_DEADLINE_MS[posture]
|
|
213
|
+
return Object.prototype.hasOwnProperty.call(POSTURE_DEADLINE_MS, posture) ? POSTURE_DEADLINE_MS[posture] : POSTURE_DEADLINE_MS["default"];
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
function _now() { return Date.now(); }
|
|
@@ -1198,12 +1198,26 @@ function dbTicketStore(opts) {
|
|
|
1198
1198
|
SUBJECT_FILTER_SPEC.forEach(function (spec) {
|
|
1199
1199
|
var supplied = filter.subject[spec.key];
|
|
1200
1200
|
if (!supplied) return;
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1201
|
+
if (!vaulted) {
|
|
1202
|
+
conds.push(spec.plainCol + " = " + spec.param);
|
|
1203
|
+
params[spec.param] = supplied;
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
// Vaulted: match BOTH the active keyed-MAC digest AND the legacy
|
|
1207
|
+
// salted-sha3 digest via the dual-read, so a ticket written before the
|
|
1208
|
+
// keyed-MAC default flip is still FOUND. A single-value equality on the
|
|
1209
|
+
// keyed-MAC digest silently skips those pre-flip rows, so an Art.17
|
|
1210
|
+
// erasure purge (list-by-subject → delete) would leave the subject's
|
|
1211
|
+
// legacy-hashed PII-bearing tickets behind. Mirrors subject.eraseHard.
|
|
1212
|
+
var cand = cryptoField().lookupHashCandidates(DSR_SEAL_TABLE, spec.sealField, supplied);
|
|
1213
|
+
var values = cand && cand.values ? cand.values : [];
|
|
1214
|
+
if (values.length === 0) return;
|
|
1215
|
+
var placeholders = values.map(function (v, i) {
|
|
1216
|
+
var p = spec.param + "_" + i;
|
|
1217
|
+
params[p] = v;
|
|
1218
|
+
return p;
|
|
1219
|
+
});
|
|
1220
|
+
conds.push(spec.hashCol + " IN (" + placeholders.join(", ") + ")");
|
|
1207
1221
|
});
|
|
1208
1222
|
}
|
|
1209
1223
|
|
|
@@ -1455,7 +1469,7 @@ var STATE_RULES = Object.freeze({
|
|
|
1455
1469
|
function stateRules(state) {
|
|
1456
1470
|
if (typeof state !== "string" || state.length === 0) return null;
|
|
1457
1471
|
// Direct posture-name lookup first
|
|
1458
|
-
if (STATE_RULES
|
|
1472
|
+
if (Object.prototype.hasOwnProperty.call(STATE_RULES, state)) return Object.assign({}, STATE_RULES[state]);
|
|
1459
1473
|
// 2-letter state abbreviation lookup (case-insensitive)
|
|
1460
1474
|
var u = state.toUpperCase();
|
|
1461
1475
|
var keys = Object.keys(STATE_RULES);
|
|
@@ -170,6 +170,13 @@ function send(res, opts) {
|
|
|
170
170
|
throw new EarlyHintsError("early-hints/bad-header-value",
|
|
171
171
|
"earlyHints.send: header '" + name + "' must be a string or string[]", true);
|
|
172
172
|
}
|
|
173
|
+
var vals = Array.isArray(canonical[name]) ? canonical[name] : [canonical[name]];
|
|
174
|
+
for (var vi = 0; vi < vals.length; vi += 1) {
|
|
175
|
+
if (typeof vals[vi] === "string" && _hasHeaderInjection(vals[vi])) {
|
|
176
|
+
throw new EarlyHintsError("early-hints/bad-header-value",
|
|
177
|
+
"earlyHints.send: header '" + name + "' value contains a CR/LF/NUL header-injection character", true);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
173
180
|
headers[name] = canonical[name];
|
|
174
181
|
}
|
|
175
182
|
|
|
@@ -182,9 +189,21 @@ function send(res, opts) {
|
|
|
182
189
|
}
|
|
183
190
|
}
|
|
184
191
|
|
|
192
|
+
// A header value reaching res.writeEarlyHints must carry no CR/LF/NUL — those
|
|
193
|
+
// split the interim response into attacker-chosen headers. Node rejects them
|
|
194
|
+
// too, but screen defensively (matching the cookies / security-headers
|
|
195
|
+
// boundary) so the framework surfaces a typed error, not a raw Node throw.
|
|
196
|
+
function _hasHeaderInjection(s) {
|
|
197
|
+
return s.indexOf("\r") !== -1 || s.indexOf("\n") !== -1 || s.indexOf("\0") !== -1;
|
|
198
|
+
}
|
|
199
|
+
|
|
185
200
|
function _validateLink(linkValue, idx) {
|
|
186
201
|
validateOpts.requireNonEmptyString(linkValue, "earlyHints.send.link[" + idx + "]",
|
|
187
202
|
EarlyHintsError, "early-hints/bad-link");
|
|
203
|
+
if (_hasHeaderInjection(linkValue)) {
|
|
204
|
+
throw new EarlyHintsError("early-hints/bad-link",
|
|
205
|
+
"link[" + idx + "] contains a CR/LF/NUL header-injection character");
|
|
206
|
+
}
|
|
188
207
|
if (linkValue.length > LINK_MAX_BYTES) {
|
|
189
208
|
throw new EarlyHintsError("early-hints/bad-link",
|
|
190
209
|
"link[" + idx + "] exceeds " + LINK_MAX_BYTES + " bytes");
|
|
@@ -205,7 +205,11 @@ async function verify(eat, opts) {
|
|
|
205
205
|
// Debug status — refuse a token that can't prove debug is disabled.
|
|
206
206
|
if (opts.requireDebugDisabled === true) {
|
|
207
207
|
var ds = raw.get(EAT.dbgstat);
|
|
208
|
-
|
|
208
|
+
// Accept ONLY a defined disabled enum value (1..4). `ds < disabled` alone
|
|
209
|
+
// let any out-of-range value >= 1 (5, 99, non-integers) pass as "disabled"
|
|
210
|
+
// — a fail-open on an undefined dbgstat (RFC 9711 §3.3 defines 0..4 only).
|
|
211
|
+
if (typeof ds !== "number" || !Number.isInteger(ds) ||
|
|
212
|
+
ds < DBGSTAT.disabled || ds > DBGSTAT["disabled-fully-and-permanently"]) {
|
|
209
213
|
throw new EatError("eat/debug-not-disabled",
|
|
210
214
|
"eat.verify: requireDebugDisabled — dbgstat is " +
|
|
211
215
|
(ds === undefined ? "absent" : (DBGSTAT_BY_VALUE[ds] || ds)) + ", not a disabled state");
|
|
@@ -339,6 +339,50 @@ function _classifyStatement(sql) {
|
|
|
339
339
|
return _STATEMENT_CLASS_MAP[kw] || "OTHER";
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
+
// True when a top-level (paren-depth 0, outside any string / comment / quoted-
|
|
343
|
+
// identifier / dollar-quoted span) RETURNING keyword is present — a DML
|
|
344
|
+
// statement with RETURNING yields rows to the caller. Quote-aware so the literal
|
|
345
|
+
// word inside a value ('see RETURNING docs') is not mistaken for a clause, and
|
|
346
|
+
// depth-aware so a RETURNING buried in a CTE / subquery paren does not count for
|
|
347
|
+
// the OUTER statement. Reuses the shared opaque-span scanner; single linear pass.
|
|
348
|
+
function _hasTopLevelReturning(sql) {
|
|
349
|
+
var n = sql.length, i = 0, depth = 0;
|
|
350
|
+
while (i < n) {
|
|
351
|
+
var skipped = _skipOpaqueSpan(sql, i);
|
|
352
|
+
if (skipped === -1) return false; // unterminated span — nothing top-level beyond
|
|
353
|
+
if (skipped !== i) { i = skipped; continue; }
|
|
354
|
+
var ch = sql.charAt(i);
|
|
355
|
+
if (ch === "(") { depth += 1; i += 1; continue; }
|
|
356
|
+
if (ch === ")") { depth -= 1; i += 1; continue; }
|
|
357
|
+
if (_isIdentStart(ch)) {
|
|
358
|
+
var we = i + 1;
|
|
359
|
+
while (we < n && _isIdentChar(sql.charAt(we))) we += 1;
|
|
360
|
+
if (depth === 0 && sql.slice(i, we).toUpperCase() === "RETURNING") return true;
|
|
361
|
+
i = we;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
i += 1;
|
|
365
|
+
}
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Does this statement return a row set to the caller? Distinct from the
|
|
370
|
+
// residency gate's read/write split: an `EXPLAIN ANALYZE INSERT` is a WRITE for
|
|
371
|
+
// the gate yet returns plan rows; a DML `... RETURNING` is a write that yields
|
|
372
|
+
// rows. Consumers (e.g. cluster-storage's local exec choosing .all() vs .run())
|
|
373
|
+
// must use .all() for any row-returning statement, including a WITH (CTE) read
|
|
374
|
+
// that the leading keyword alone would mis-route to .run() and SILENTLY DROP.
|
|
375
|
+
// Statement classes that produce a row set for the caller (SELECT and the
|
|
376
|
+
// READ_INFO family — SHOW / DESCRIBE / PRAGMA). Keyed by class name, mirroring
|
|
377
|
+
// _STATEMENT_CLASS_MAP / _RESIDENCY_READ_CLASS.
|
|
378
|
+
var _ROW_RETURNING_CLASS = Object.freeze({ SELECT: true, READ_INFO: true });
|
|
379
|
+
|
|
380
|
+
function statementReturnsRows(sql) {
|
|
381
|
+
if (typeof sql !== "string" || sql.length === 0) return false;
|
|
382
|
+
if (_ROW_RETURNING_CLASS[_classifyStatement(sql)] === true) return true;
|
|
383
|
+
return _hasTopLevelReturning(sql);
|
|
384
|
+
}
|
|
385
|
+
|
|
342
386
|
// Statement classes that place no rows on the backend, so the cross-
|
|
343
387
|
// border residency write gate lets them pass without a row tag. Every
|
|
344
388
|
// other class — DML, ROUTINE (CALL / EXECUTE / DO), a COPY ... FROM
|
|
@@ -1810,12 +1854,19 @@ async function _readQuery(sql, params, opts) {
|
|
|
1810
1854
|
// was explicitly configured allowCrossBorder (which is audited).
|
|
1811
1855
|
var _readPosture = _activePosture();
|
|
1812
1856
|
var _tagPresent = opts.rowResidencyTag && typeof opts.rowResidencyTag === "string";
|
|
1857
|
+
// A replica only carries a real residency CONSTRAINT when its tag names a
|
|
1858
|
+
// specific region. "unrestricted" / "global" mean "no constraint" (the tag
|
|
1859
|
+
// defaults to "unrestricted" when none is configured), so such a replica may
|
|
1860
|
+
// serve any region's rows and must NOT require opts.rowResidencyTag — gating
|
|
1861
|
+
// it would over-reject every regulated read to an untagged replica.
|
|
1862
|
+
var _replicaConstrained = replica.residencyTag &&
|
|
1863
|
+
replica.residencyTag !== "unrestricted" && replica.residencyTag !== "global";
|
|
1813
1864
|
// Fail CLOSED when the row's region is not identified: a regulated read to a
|
|
1814
|
-
// residency-
|
|
1815
|
-
// residency-restricted rows to an arbitrary-region replica with no check
|
|
1816
|
-
// all (symmetric with the write gate's RESIDENCY_GATE_REQUIRED).
|
|
1865
|
+
// residency-CONSTRAINED replica without opts.rowResidencyTag would otherwise
|
|
1866
|
+
// route residency-restricted rows to an arbitrary-region replica with no check
|
|
1867
|
+
// at all (symmetric with the write gate's RESIDENCY_GATE_REQUIRED).
|
|
1817
1868
|
if (!_tagPresent && _crossBorderRegulated(_readPosture) &&
|
|
1818
|
-
|
|
1869
|
+
_replicaConstrained && !replica.allowCrossBorder) {
|
|
1819
1870
|
_emit("db.residency.replica.tag_required", "denied", {
|
|
1820
1871
|
backend: b.name, replicaIdx: replica.index,
|
|
1821
1872
|
replicaTag: replica.residencyTag, posture: _readPosture,
|
|
@@ -2574,6 +2625,11 @@ module.exports = {
|
|
|
2574
2625
|
// lock tables live on the externalDb side. See lib/external-db-migrate.js.
|
|
2575
2626
|
migrate: externalDbMigrate,
|
|
2576
2627
|
Pool: Pool,
|
|
2628
|
+
// Shared SQL statement classifier (CTE / EXPLAIN aware): does the statement
|
|
2629
|
+
// return a row set? Internal — consumed by cluster-storage's local exec to
|
|
2630
|
+
// choose .all() vs .run() so a WITH (CTE) read is not mis-routed and its rows
|
|
2631
|
+
// lost. Underscore-prefixed: framework-internal, not an operator API surface.
|
|
2632
|
+
_statementReturnsRows: statementReturnsRows,
|
|
2577
2633
|
_resetForTest: _resetForTest,
|
|
2578
2634
|
_extractTargetRelation: _extractTargetRelation,
|
|
2579
2635
|
};
|
|
@@ -91,6 +91,18 @@ var DEFAULT_SIGNATURE_MEANINGS = Object.freeze([
|
|
|
91
91
|
// must carry §11.10(e) shape. Operators add more via opts.gxpNamespaces.
|
|
92
92
|
var DEFAULT_GXP_NAMESPACES = Object.freeze(["subject", "consent", "db", "breakglass"]);
|
|
93
93
|
|
|
94
|
+
// Non-mutating verb segments. A GxP-namespace event whose final action segment
|
|
95
|
+
// is NOT one of these is treated as a record MODIFICATION and must carry the
|
|
96
|
+
// §11.10(e) before/after/reason shape (fail-closed — an unknown verb is assumed
|
|
97
|
+
// to mutate). hasOwnProperty-guarded lookup (untrusted action key).
|
|
98
|
+
var READ_VERBS = Object.freeze({
|
|
99
|
+
read: 1, viewed: 1, view: 1, get: 1, got: 1, list: 1, listed: 1, query: 1,
|
|
100
|
+
queried: 1, access: 1, accessed: 1, export: 1, exported: 1, history: 1,
|
|
101
|
+
isgranted: 1, check: 1, checked: 1, verify: 1, verified: 1, lookup: 1,
|
|
102
|
+
search: 1, searched: 1, fetch: 1, fetched: 1, render: 1, rendered: 1,
|
|
103
|
+
download: 1, downloaded: 1,
|
|
104
|
+
});
|
|
105
|
+
|
|
94
106
|
// §11.10(e) — every modification audit must carry timestamp, actor,
|
|
95
107
|
// before/after pair, and a reason. The framework's audit row shape
|
|
96
108
|
// already carries `recordedAt` (timestamp) + `actorUserId` + `reason`;
|
|
@@ -114,11 +126,15 @@ function _hasRequiredAuditShape(row) {
|
|
|
114
126
|
if (!row.action || typeof row.action !== "string") {
|
|
115
127
|
return { ok: false, reason: "row missing action verb (§11.10(e))" };
|
|
116
128
|
}
|
|
117
|
-
//
|
|
118
|
-
// "
|
|
129
|
+
// §11.10(e) requires before/after on every record MODIFICATION. Inferring
|
|
130
|
+
// "is this a modification" from a denylist of mutating verbs silently let
|
|
131
|
+
// off-list verbs (anonymize / revoke / overwrite / replace / merge /
|
|
132
|
+
// withdraw / restrict / objection …) bypass the requirement. Fail CLOSED
|
|
133
|
+
// instead: a GxP-namespace event is treated as a modification UNLESS its
|
|
134
|
+
// final verb segment is on an explicit non-mutating READ allowlist.
|
|
119
135
|
var verb = row.action.toLowerCase();
|
|
120
|
-
var
|
|
121
|
-
|
|
136
|
+
var lastSeg = verb.indexOf(".") === -1 ? verb : verb.slice(verb.lastIndexOf(".") + 1);
|
|
137
|
+
var modShape = !Object.prototype.hasOwnProperty.call(READ_VERBS, lastSeg);
|
|
122
138
|
if (modShape) {
|
|
123
139
|
var meta = row.metadata;
|
|
124
140
|
// Audit chain stores metadata as a JSON string when read back —
|
|
@@ -263,7 +279,16 @@ function posture(opts) {
|
|
|
263
279
|
}, "denied");
|
|
264
280
|
return { ok: false, reason: "record-hash-mismatch" };
|
|
265
281
|
}
|
|
266
|
-
if (verifyWith
|
|
282
|
+
if (verifyWith) {
|
|
283
|
+
// A configured verifier means a signature is REQUIRED — a null/empty
|
|
284
|
+
// signature must fail closed (recordHash alone is self-consistency, not
|
|
285
|
+
// authentication; accepting it is alg:none-style signature stripping).
|
|
286
|
+
if (!signed.signature) {
|
|
287
|
+
_emit("fda21cfr11.signature.verified", {
|
|
288
|
+
printedName: signed.printedName, ok: false, reason: "signature-required",
|
|
289
|
+
}, "denied");
|
|
290
|
+
return { ok: false, reason: "signature-required" };
|
|
291
|
+
}
|
|
267
292
|
var sigBuf = Buffer.from(signed.signature, "base64");
|
|
268
293
|
var payload = JSON.stringify({
|
|
269
294
|
printedName: signed.printedName,
|
|
@@ -41,6 +41,7 @@ var validateOpts = require("./validate-opts");
|
|
|
41
41
|
var lazyRequire = require("./lazy-require");
|
|
42
42
|
var safeJson = require("./safe-json");
|
|
43
43
|
var C = require("./constants");
|
|
44
|
+
var atomicFile = require("./atomic-file");
|
|
44
45
|
var { defineClass } = require("./framework-error");
|
|
45
46
|
var FlagError = defineClass("FlagError", { alwaysPermanent: true });
|
|
46
47
|
|
|
@@ -128,7 +129,10 @@ function localFile(opts) {
|
|
|
128
129
|
validateOpts.requireNonEmptyString(opts.path,
|
|
129
130
|
"providers.localFile: path", FlagError, "flag/bad-provider");
|
|
130
131
|
var raw;
|
|
131
|
-
|
|
132
|
+
// Capped fd-bound read; the cap precedes the alloc so an oversized flag file
|
|
133
|
+
// can't OOM boot. refuseSymlink stays OFF: a flag file is commonly a k8s
|
|
134
|
+
// ConfigMap mount, which is a symlink chain — refusing it would break that.
|
|
135
|
+
try { raw = atomicFile.fdSafeReadSync(opts.path, { maxBytes: C.BYTES.mib(1), encoding: "utf8" }); }
|
|
132
136
|
catch (e) {
|
|
133
137
|
throw new FlagError("flag/bad-provider",
|
|
134
138
|
"providers.localFile: cannot read file " + JSON.stringify(opts.path) +
|
|
@@ -154,7 +158,7 @@ function localFile(opts) {
|
|
|
154
158
|
try {
|
|
155
159
|
nodeFs.watch(opts.path, { persistent: false }, function () {
|
|
156
160
|
try {
|
|
157
|
-
var nextRaw =
|
|
161
|
+
var nextRaw = atomicFile.fdSafeReadSync(opts.path, { maxBytes: C.BYTES.mib(1), encoding: "utf8" });
|
|
158
162
|
var nextParsed = safeJson.parse(nextRaw, { maxBytes: C.BYTES.mib(1) });
|
|
159
163
|
if (nextParsed && nextParsed.flags) {
|
|
160
164
|
for (var k in nextParsed.flags) {
|
|
@@ -252,7 +252,7 @@ function _renderField(field) {
|
|
|
252
252
|
var control;
|
|
253
253
|
if (type === "textarea") control = _renderTextarea(field);
|
|
254
254
|
else if (type === "select") control = _renderSelect(field);
|
|
255
|
-
else if (INPUT_TYPES
|
|
255
|
+
else if (Object.prototype.hasOwnProperty.call(INPUT_TYPES, type)) control = _renderInput(field);
|
|
256
256
|
else throw new Error("forms.render: unsupported field type: " + type);
|
|
257
257
|
|
|
258
258
|
// Hidden + submit fields don't need a label wrapper
|
|
@@ -1095,13 +1095,49 @@ function makeProfileBuilder(profiles) {
|
|
|
1095
1095
|
* posture.piiPolicy; // → "redact"
|
|
1096
1096
|
*/
|
|
1097
1097
|
function lookupCompliancePosture(name, postures, errorFactory, codePrefix) {
|
|
1098
|
-
|
|
1098
|
+
// hasOwnProperty: `name` is caller input; a bracket lookup would let a
|
|
1099
|
+
// prototype key like "constructor" pass the presence check and reach
|
|
1100
|
+
// Object.assign (proto shadowing).
|
|
1101
|
+
if (!postures || !Object.prototype.hasOwnProperty.call(postures, name)) {
|
|
1099
1102
|
throw errorFactory(codePrefix + ".bad-posture",
|
|
1100
1103
|
"unknown compliancePosture " + JSON.stringify(name));
|
|
1101
1104
|
}
|
|
1102
1105
|
return Object.assign({}, postures[name]);
|
|
1103
1106
|
}
|
|
1104
1107
|
|
|
1108
|
+
/**
|
|
1109
|
+
* @primitive b.gateContract.makePostureAccessor
|
|
1110
|
+
* @signature b.gateContract.makePostureAccessor(postures, opts?)
|
|
1111
|
+
* @since 0.15.14
|
|
1112
|
+
* @status stable
|
|
1113
|
+
* @related b.gateContract.lookupCompliancePosture, b.gateContract.resolveProfileName
|
|
1114
|
+
*
|
|
1115
|
+
* Build the public `compliancePosture(name)` accessor a guard exposes — maps a
|
|
1116
|
+
* compliance-posture name through the guard's own `postures` table to the
|
|
1117
|
+
* profile it selects, returning `opts.fallback` (default `null`) for an
|
|
1118
|
+
* unknown name. Folds the one-line lookup the mail-scanner / content-detect
|
|
1119
|
+
* factories each redefined verbatim (`return POSTURES[name] || null`) into one
|
|
1120
|
+
* proto-shadow-safe helper: the membership test is `hasOwnProperty.call` so a
|
|
1121
|
+
* prototype key (constructor / __proto__ / toString) resolves to the fallback
|
|
1122
|
+
* rather than an inherited Function. Unlike `lookupCompliancePosture` it does
|
|
1123
|
+
* NOT throw and returns the raw mapped value (a profile name string), not an
|
|
1124
|
+
* object copy.
|
|
1125
|
+
*
|
|
1126
|
+
* @opts
|
|
1127
|
+
* fallback: any, // value returned for an unknown / proto-key name (default null)
|
|
1128
|
+
*
|
|
1129
|
+
* @example
|
|
1130
|
+
* var compliancePosture = b.gateContract.makePostureAccessor(COMPLIANCE_POSTURES);
|
|
1131
|
+
* compliancePosture("hipaa"); // → "strict"
|
|
1132
|
+
* compliancePosture("constructor"); // → null (proto key, not an own posture)
|
|
1133
|
+
*/
|
|
1134
|
+
function makePostureAccessor(postures, opts) {
|
|
1135
|
+
var fallback = (opts && Object.prototype.hasOwnProperty.call(opts, "fallback")) ? opts.fallback : null;
|
|
1136
|
+
return function (name) {
|
|
1137
|
+
return Object.prototype.hasOwnProperty.call(postures, name) ? postures[name] : fallback;
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1105
1141
|
// "GuardCidrError" -> "guardCidr" — the guard's audit/message identity, derived
|
|
1106
1142
|
// once from its error class name. Used for the default gate's audit/metric
|
|
1107
1143
|
// prefix AND the profile resolver's error message, so neither re-cases the name.
|
|
@@ -1199,9 +1235,13 @@ function makeProfileResolver(cfg) {
|
|
|
1199
1235
|
*/
|
|
1200
1236
|
function resolveProfileName(opts, postures, defaultProfile) {
|
|
1201
1237
|
opts = opts || {};
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1238
|
+
// hasOwnProperty: opts.posture is caller/operator input; a bracket lookup
|
|
1239
|
+
// would let a prototype key like "constructor" resolve to an inherited
|
|
1240
|
+
// truthy value and become the profile name (proto shadowing).
|
|
1241
|
+
var postureProfile = (opts.posture && postures &&
|
|
1242
|
+
Object.prototype.hasOwnProperty.call(postures, opts.posture))
|
|
1243
|
+
? postures[opts.posture] : null;
|
|
1244
|
+
return opts.profile || postureProfile || defaultProfile;
|
|
1205
1245
|
}
|
|
1206
1246
|
|
|
1207
1247
|
/**
|
|
@@ -2072,7 +2112,7 @@ Object.freeze(INPUT_CONTRACTS);
|
|
|
2072
2112
|
|
|
2073
2113
|
function resolveInputContract(contract) {
|
|
2074
2114
|
if (typeof contract === "function") return contract;
|
|
2075
|
-
return INPUT_CONTRACTS[contract]
|
|
2115
|
+
return Object.prototype.hasOwnProperty.call(INPUT_CONTRACTS, contract) ? INPUT_CONTRACTS[contract] : INPUT_CONTRACTS.text;
|
|
2076
2116
|
}
|
|
2077
2117
|
|
|
2078
2118
|
/**
|
|
@@ -3125,6 +3165,7 @@ module.exports = {
|
|
|
3125
3165
|
charThreatDisposition: charThreatDisposition,
|
|
3126
3166
|
extractBytesAsText: extractBytesAsText,
|
|
3127
3167
|
lookupCompliancePosture: lookupCompliancePosture,
|
|
3168
|
+
makePostureAccessor: makePostureAccessor,
|
|
3128
3169
|
ALL_STRICT_POSTURES: ALL_STRICT_POSTURES,
|
|
3129
3170
|
CHAR_THREATS_REJECT_ALL: CHAR_THREATS_REJECT_ALL,
|
|
3130
3171
|
DANGEROUS_URL_SCHEMES: DANGEROUS_URL_SCHEMES,
|