@blamejs/blamejs-shop 0.5.12 → 0.5.13
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 +2 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +632 -416
- package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +21 -10
- package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +30 -9
- package/lib/vendor/blamejs/.github/dependabot.yml +8 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/ci.yml +21 -21
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +4 -4
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +7 -7
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
- package/lib/vendor/blamejs/.gitignore +2 -0
- package/lib/vendor/blamejs/CHANGELOG.md +122 -0
- package/lib/vendor/blamejs/NOTICE +26 -1
- package/lib/vendor/blamejs/SECURITY.md +1 -1
- package/lib/vendor/blamejs/api-snapshot.json +207 -5
- package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +29 -0
- package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +1 -0
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +31 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +109 -0
- package/lib/vendor/blamejs/examples/wiki/nginx.conf +106 -0
- package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +92 -0
- package/lib/vendor/blamejs/fuzz/asn1-der.fuzz.js +28 -0
- package/lib/vendor/blamejs/fuzz/cms-codec.fuzz.js +17 -0
- package/lib/vendor/blamejs/fuzz/link-header.fuzz.js +18 -0
- package/lib/vendor/blamejs/index.js +6 -0
- package/lib/vendor/blamejs/lib/a2a.js +4 -4
- package/lib/vendor/blamejs/lib/agent-idempotency.js +18 -2
- package/lib/vendor/blamejs/lib/agent-saga.js +36 -8
- package/lib/vendor/blamejs/lib/agent-snapshot.js +63 -6
- package/lib/vendor/blamejs/lib/app-shutdown.js +23 -20
- package/lib/vendor/blamejs/lib/archive-read.js +8 -0
- package/lib/vendor/blamejs/lib/archive-tar.js +12 -2
- package/lib/vendor/blamejs/lib/arg-parser.js +49 -5
- package/lib/vendor/blamejs/lib/asn1-der.js +12 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +47 -1
- package/lib/vendor/blamejs/lib/audit.js +71 -8
- package/lib/vendor/blamejs/lib/auth/ciba.js +14 -2
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +58 -47
- package/lib/vendor/blamejs/lib/auth/jwt.js +34 -0
- package/lib/vendor/blamejs/lib/auth/oauth.js +90 -13
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +20 -17
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +214 -23
- package/lib/vendor/blamejs/lib/auth/saml.js +38 -20
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +10 -4
- package/lib/vendor/blamejs/lib/backup/bundle.js +13 -3
- package/lib/vendor/blamejs/lib/backup/index.js +16 -5
- package/lib/vendor/blamejs/lib/backup/manifest.js +18 -5
- package/lib/vendor/blamejs/lib/base32.js +21 -0
- package/lib/vendor/blamejs/lib/break-glass.js +19 -10
- package/lib/vendor/blamejs/lib/bundler.js +5 -1
- package/lib/vendor/blamejs/lib/cache-status.js +3 -7
- package/lib/vendor/blamejs/lib/calendar.js +21 -1
- package/lib/vendor/blamejs/lib/cbor.js +12 -1
- package/lib/vendor/blamejs/lib/cert.js +29 -11
- package/lib/vendor/blamejs/lib/cli.js +94 -24
- package/lib/vendor/blamejs/lib/cloud-events.js +2 -2
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +13 -3
- package/lib/vendor/blamejs/lib/codepoint-class.js +85 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +8 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +19 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act.js +6 -8
- package/lib/vendor/blamejs/lib/content-credentials.js +1 -1
- package/lib/vendor/blamejs/lib/content-digest.js +2 -1
- package/lib/vendor/blamejs/lib/cookies.js +13 -3
- package/lib/vendor/blamejs/lib/cose.js +31 -16
- package/lib/vendor/blamejs/lib/crypto-field.js +32 -0
- package/lib/vendor/blamejs/lib/crypto-oprf.js +11 -2
- package/lib/vendor/blamejs/lib/crypto-xwing.js +22 -4
- package/lib/vendor/blamejs/lib/crypto.js +314 -19
- package/lib/vendor/blamejs/lib/csp.js +11 -3
- package/lib/vendor/blamejs/lib/daemon.js +270 -24
- package/lib/vendor/blamejs/lib/dark-patterns.js +5 -2
- package/lib/vendor/blamejs/lib/db-declare-view.js +2 -2
- package/lib/vendor/blamejs/lib/db-query.js +34 -7
- package/lib/vendor/blamejs/lib/db.js +18 -6
- package/lib/vendor/blamejs/lib/dbsc.js +17 -0
- package/lib/vendor/blamejs/lib/dev.js +21 -2
- package/lib/vendor/blamejs/lib/dsr.js +157 -102
- package/lib/vendor/blamejs/lib/external-db.js +41 -2
- package/lib/vendor/blamejs/lib/file-upload.js +25 -7
- package/lib/vendor/blamejs/lib/flag-providers.js +27 -15
- package/lib/vendor/blamejs/lib/forms.js +113 -27
- package/lib/vendor/blamejs/lib/gate-contract.js +23 -10
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +8 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +1 -1
- package/lib/vendor/blamejs/lib/guard-archive.js +2 -2
- package/lib/vendor/blamejs/lib/guard-cidr.js +27 -1
- package/lib/vendor/blamejs/lib/guard-graphql.js +30 -11
- package/lib/vendor/blamejs/lib/guard-html.js +26 -53
- package/lib/vendor/blamejs/lib/guard-image.js +24 -5
- package/lib/vendor/blamejs/lib/guard-imap-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-jmap.js +19 -3
- package/lib/vendor/blamejs/lib/guard-jwt.js +18 -2
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -2
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-markdown.js +11 -24
- package/lib/vendor/blamejs/lib/guard-pdf.js +18 -2
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-regex.js +8 -5
- package/lib/vendor/blamejs/lib/guard-svg.js +31 -32
- package/lib/vendor/blamejs/lib/guard-time.js +1 -1
- package/lib/vendor/blamejs/lib/guard-uuid.js +2 -2
- package/lib/vendor/blamejs/lib/html-balance.js +3 -3
- package/lib/vendor/blamejs/lib/http-client-cache.js +82 -4
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +16 -1
- package/lib/vendor/blamejs/lib/http-client.js +39 -7
- package/lib/vendor/blamejs/lib/http-message-signature.js +35 -1
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +31 -6
- package/lib/vendor/blamejs/lib/i18n.js +35 -21
- package/lib/vendor/blamejs/lib/incident-report.js +8 -1
- package/lib/vendor/blamejs/lib/ip-utils.js +11 -1
- package/lib/vendor/blamejs/lib/json-path.js +23 -1
- package/lib/vendor/blamejs/lib/jwk.js +20 -3
- package/lib/vendor/blamejs/lib/link-header.js +2 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream.js +8 -3
- package/lib/vendor/blamejs/lib/mail-auth.js +123 -22
- package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
- package/lib/vendor/blamejs/lib/mail-bounce.js +8 -0
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +46 -10
- package/lib/vendor/blamejs/lib/mail-dav.js +22 -3
- package/lib/vendor/blamejs/lib/mail-dkim.js +96 -30
- package/lib/vendor/blamejs/lib/mail-helo.js +6 -1
- package/lib/vendor/blamejs/lib/mail-scan.js +23 -16
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +34 -8
- package/lib/vendor/blamejs/lib/mail-server-imap.js +3 -4
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +7 -11
- package/lib/vendor/blamejs/lib/mail-server-mx.js +93 -7
- package/lib/vendor/blamejs/lib/mail-server-registry.js +8 -2
- package/lib/vendor/blamejs/lib/mail.js +15 -5
- package/lib/vendor/blamejs/lib/metrics.js +268 -63
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +7 -1
- package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +19 -4
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +5 -9
- package/lib/vendor/blamejs/lib/middleware/csp-report.js +40 -8
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +24 -10
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -5
- package/lib/vendor/blamejs/lib/middleware/index.js +6 -1
- package/lib/vendor/blamejs/lib/middleware/nel.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +12 -1
- package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +6 -2
- package/lib/vendor/blamejs/lib/mtls-ca.js +18 -2
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +11 -1
- package/lib/vendor/blamejs/lib/network-dns.js +21 -2
- package/lib/vendor/blamejs/lib/network-tls.js +69 -7
- package/lib/vendor/blamejs/lib/nonce-store.js +9 -1
- package/lib/vendor/blamejs/lib/object-store/gcs.js +7 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +31 -0
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +1 -1
- package/lib/vendor/blamejs/lib/outbox.js +1 -1
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +9 -1
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +10 -1
- package/lib/vendor/blamejs/lib/permissions.js +6 -3
- package/lib/vendor/blamejs/lib/pid-probe.js +55 -0
- package/lib/vendor/blamejs/lib/pqc-agent.js +8 -1
- package/lib/vendor/blamejs/lib/pqc-software.js +3 -3
- package/lib/vendor/blamejs/lib/pubsub.js +20 -9
- package/lib/vendor/blamejs/lib/queue-local.js +8 -1
- package/lib/vendor/blamejs/lib/queue-redis.js +5 -0
- package/lib/vendor/blamejs/lib/redact.js +54 -0
- package/lib/vendor/blamejs/lib/restore-bundle.js +10 -2
- package/lib/vendor/blamejs/lib/restore.js +19 -0
- package/lib/vendor/blamejs/lib/retention.js +12 -4
- package/lib/vendor/blamejs/lib/router.js +60 -6
- package/lib/vendor/blamejs/lib/safe-buffer.js +69 -0
- package/lib/vendor/blamejs/lib/safe-decompress.js +8 -2
- package/lib/vendor/blamejs/lib/safe-dns.js +25 -20
- package/lib/vendor/blamejs/lib/safe-ical.js +7 -3
- package/lib/vendor/blamejs/lib/safe-mime.js +12 -1
- package/lib/vendor/blamejs/lib/safe-object.js +80 -0
- package/lib/vendor/blamejs/lib/safe-path.js +52 -4
- package/lib/vendor/blamejs/lib/safe-redirect.js +10 -2
- package/lib/vendor/blamejs/lib/safe-schema.js +4 -1
- package/lib/vendor/blamejs/lib/safe-sieve.js +33 -1
- package/lib/vendor/blamejs/lib/safe-sql.js +88 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +7 -3
- package/lib/vendor/blamejs/lib/scheduler.js +8 -1
- package/lib/vendor/blamejs/lib/security-assert.js +6 -6
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +74 -27
- package/lib/vendor/blamejs/lib/self-update.js +517 -87
- package/lib/vendor/blamejs/lib/server-timing.js +2 -4
- package/lib/vendor/blamejs/lib/session.js +34 -12
- package/lib/vendor/blamejs/lib/sql.js +15 -1
- package/lib/vendor/blamejs/lib/ssrf-guard.js +52 -0
- package/lib/vendor/blamejs/lib/static.js +55 -13
- package/lib/vendor/blamejs/lib/storage.js +8 -2
- package/lib/vendor/blamejs/lib/subject.js +6 -1
- package/lib/vendor/blamejs/lib/tenant-quota.js +52 -13
- package/lib/vendor/blamejs/lib/tls-exporter.js +3 -3
- package/lib/vendor/blamejs/lib/tsa.js +34 -13
- package/lib/vendor/blamejs/lib/uri-template.js +8 -1
- package/lib/vendor/blamejs/lib/vault/index.js +13 -0
- package/lib/vendor/blamejs/lib/vc.js +44 -5
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +26 -26
- package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +792 -1
- package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +3443 -1
- package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +2737 -1
- package/lib/vendor/blamejs/lib/vendor/pki.cjs +39550 -13
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +55 -53
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5551 -5554
- package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +22459 -32
- package/lib/vendor/blamejs/lib/watcher.js +89 -17
- package/lib/vendor/blamejs/lib/webhook-dispatcher.js +25 -3
- package/lib/vendor/blamejs/lib/webhook.js +12 -2
- package/lib/vendor/blamejs/lib/ws-client.js +32 -1
- package/lib/vendor/blamejs/lib/xml-c14n.js +29 -1
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +1 -1
- package/lib/vendor/blamejs/package-lock.json +2 -2
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.x.json +1462 -0
- package/lib/vendor/blamejs/release-notes/v0.17.0.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.1.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.10.json +30 -0
- package/lib/vendor/blamejs/release-notes/v0.17.11.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.12.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.13.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.17.14.json +40 -0
- package/lib/vendor/blamejs/release-notes/v0.17.15.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.16.json +27 -0
- package/lib/vendor/blamejs/release-notes/v0.17.17.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.18.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.19.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.2.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.20.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.21.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.22.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.3.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.4.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.5.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.6.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.7.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.8.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.9.json +26 -0
- package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +4 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +293 -9
- package/lib/vendor/blamejs/socket.yml +94 -0
- package/lib/vendor/blamejs/test/00-primitives.js +605 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +12 -9
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +90 -1
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +122 -1
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +30 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +29 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-mysql.test.js +479 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-pg.test.js +487 -0
- package/lib/vendor/blamejs/test/integration/federation-auth.test.js +313 -5
- package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +80 -0
- package/lib/vendor/blamejs/test/integration/openid-federation-chain.test.js +275 -0
- package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +1649 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +33 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +60 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +891 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +128 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-adapters.test.js +271 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +628 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-read.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-wrap.test.js +227 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +141 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-tools.test.js +624 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit.test.js +769 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth.test.js +1697 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oid4vp.test.js +495 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-password.test.js +500 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml.test.js +1809 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +243 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index.test.js +2226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/base32.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +693 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +603 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +557 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/case-insensitive-security-token-guard.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cbor.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +843 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba-authreqid-binding.test.js +77 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli.test.js +2790 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster-lease-renewal-ttl.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster.test.js +1032 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +739 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +8 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +194 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-lifecycle-proto-key.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +254 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +613 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/cookies.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +407 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-adversarial.test.js +352 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-spki-pin.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-xwing.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto.test.js +345 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +459 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query.test.js +1019 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db.test.js +1621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +462 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-verify.test.js +562 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +901 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +249 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +561 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db.test.js +1214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +46 -20
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +147 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +907 -38
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload.test.js +1135 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/forms.test.js +747 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract.test.js +1855 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-auth.test.js +117 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-cidr.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-domain.test.js +422 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +424 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-gate-disposition-coverage.test.js → guard-gate-disposition.test.js} +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-graphql.test.js +340 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +519 -45
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jsonpath.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jwt.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +16 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mime.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-oauth.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-regex.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-shell.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-sql-coverage.test.js → guard-sql.test.js} +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-template.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-time.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-uuid.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache-authorization.test.js +347 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +908 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client.test.js +2874 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +643 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +1153 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +494 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jsdoc-example-execution.test.js +177 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-path.test.js +103 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-schema.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwk.test.js +41 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +276 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream.test.js +241 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +441 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +2136 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +759 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +811 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +401 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +751 -8
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +864 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +830 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +635 -86
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +686 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +778 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +1438 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +449 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +773 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +623 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +13 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +877 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +326 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +242 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +1596 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +440 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics.test.js +1230 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-assetlinks.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-body-parser.test.js +1169 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-bot-disclose.test.js +123 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-cookies.test.js +108 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-gpc.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-headers.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-host-allowlist.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-require-content-type.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{middleware-tus-upload-coverage.test.js → middleware-tus-upload.test.js} +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-web-app-manifest.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-crl-fingerprint-only.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +1727 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/network-nts.test.js +699 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy.test.js +1181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +1876 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-enforce-replay-nonboolean.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ntp-check.test.js +122 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +378 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oid4vci.test.js +1016 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/openid-federation.test.js +946 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/own-property-membership-guards.test.js +109 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-toml.test.js +543 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{parsers-safe-yaml-coverage.test.js → parsers-safe-yaml.test.js} +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +649 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +22 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +295 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact.test.js +1031 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-bound-key.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/restore-empty-manifest-wipe.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention.test.js +600 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-host-path-injection.test.js +231 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router.test.js +1277 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +245 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ini.test.js +376 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-json.test.js +175 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +518 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-object.test.js +62 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-schema.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-sql.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +26 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scheduler.test.js +157 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +396 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +930 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +755 -82
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +1053 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +200 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-strict-binding-missing-failclosed.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +636 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-offset-without-limit.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +1190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +850 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage.test.js +621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/subject.test.js +47 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/template.test.js +582 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +587 -25
- package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +11 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tsa.test.js +524 -63
- package/lib/vendor/blamejs/test/layer-0-primitives/uri-template.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-rotate.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +215 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +550 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +388 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +262 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket.test.js +1294 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +629 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/xml-c14n-attr-normalization.test.js +164 -0
- package/lib/vendor/blamejs/test/smoke.js +2 -0
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.0.json +0 -44
- package/lib/vendor/blamejs/release-notes/v0.16.1.json +0 -36
- package/lib/vendor/blamejs/release-notes/v0.16.2.json +0 -71
- package/lib/vendor/blamejs/release-notes/v0.16.3.json +0 -71
- package/lib/vendor/blamejs/test/layer-0-primitives/acme-coverage.test.js +0 -441
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth-coverage.test.js +0 -482
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml-coverage.test.js +0 -671
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index-coverage.test.js +0 -690
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-coverage.test.js +0 -238
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +0 -319
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain-coverage.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth-coverage.test.js +0 -437
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +0 -443
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +0 -395
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy-coverage.test.js +0 -565
- package/lib/vendor/blamejs/test/layer-0-primitives/router-coverage.test.js +0 -592
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-coverage.test.js +0 -422
- package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +0 -573
|
@@ -453,6 +453,55 @@ async function testGrantExhaustion() {
|
|
|
453
453
|
}
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
+
// ---- Concurrent unsealRow — a 1-row grant must not double-claim ----
|
|
457
|
+
|
|
458
|
+
async function testConcurrentUnsealRowDoubleClaim() {
|
|
459
|
+
var tmpDir = _tmp();
|
|
460
|
+
await setupTestDb(tmpDir);
|
|
461
|
+
try {
|
|
462
|
+
b.breakGlass.init();
|
|
463
|
+
b.queue.init({ backends: { primary: { protocol: "local" } } });
|
|
464
|
+
// Two distinct rows in the glass-locked table.
|
|
465
|
+
var j1 = await b.queue.enqueue("bg-dc-q", { secret: "row-one-secret" });
|
|
466
|
+
var j2 = await b.queue.enqueue("bg-dc-q", { secret: "row-two-secret" });
|
|
467
|
+
|
|
468
|
+
await b.breakGlass.policy.set("_blamejs_jobs", {
|
|
469
|
+
columns: ["payload"],
|
|
470
|
+
factors: ["totp"],
|
|
471
|
+
maxRowsPerGrant: 1, // row-by-row auth: ONE PHI read per grant
|
|
472
|
+
});
|
|
473
|
+
var totp = _validTotp();
|
|
474
|
+
var grant = await b.breakGlass.grant({
|
|
475
|
+
req: _fakeReq(),
|
|
476
|
+
table: "_blamejs_jobs",
|
|
477
|
+
reason: "concurrent double-claim regression on a 1-row grant",
|
|
478
|
+
factor: { type: "totp", code: totp.code, secret: totp.secret },
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// Two concurrent redemptions of the SAME 1-row grant against DIFFERENT
|
|
482
|
+
// rows. The claim is an atomic compare-and-increment; exactly ONE caller
|
|
483
|
+
// may win the single row slot. The loser must be refused as exhausted —
|
|
484
|
+
// inferring the claim from a re-read of rowsConsumed (a concurrent
|
|
485
|
+
// winner's increment is visible to the loser) let BOTH pass, unsealing two
|
|
486
|
+
// rows under a maxRowsPerGrant:1 grant and defeating row-by-row auth.
|
|
487
|
+
var results = await Promise.allSettled([
|
|
488
|
+
b.breakGlass.unsealRow(grant, "_blamejs_jobs", j1.jobId, { req: _fakeReq() }),
|
|
489
|
+
b.breakGlass.unsealRow(grant, "_blamejs_jobs", j2.jobId, { req: _fakeReq() }),
|
|
490
|
+
]);
|
|
491
|
+
var ok = results.filter(function (r) { return r.status === "fulfilled"; });
|
|
492
|
+
var bad = results.filter(function (r) { return r.status === "rejected"; });
|
|
493
|
+
check("concurrent unsealRow: exactly one row unsealed under a 1-row grant",
|
|
494
|
+
ok.length === 1);
|
|
495
|
+
check("concurrent unsealRow: the losing read is refused as exhausted",
|
|
496
|
+
bad.length === 1 &&
|
|
497
|
+
/breakglass\/grant-exhausted/.test((bad[0].reason && bad[0].reason.code) || ""));
|
|
498
|
+
|
|
499
|
+
try { await b.queue.shutdown({ timeoutMs: 200 }); } catch (_e) {}
|
|
500
|
+
} finally {
|
|
501
|
+
await teardownTestDb(tmpDir);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
456
505
|
// ---- Revoke ----
|
|
457
506
|
|
|
458
507
|
async function testGrantRevoke() {
|
|
@@ -1167,9 +1216,636 @@ async function testListActiveAllAndRevokeAll() {
|
|
|
1167
1216
|
}
|
|
1168
1217
|
}
|
|
1169
1218
|
|
|
1219
|
+
// ---- Not-initialized guard — every primitive fails closed before init() ----
|
|
1220
|
+
|
|
1221
|
+
async function testRequireInitGuards() {
|
|
1222
|
+
// Every public entry point calls _requireInit() first. After a reset
|
|
1223
|
+
// (no init), each must throw breakglass/not-initialized — the fail-closed
|
|
1224
|
+
// guard that keeps an un-wired framework from minting grants or unsealing
|
|
1225
|
+
// PHI. No DB needed: the guard is the first statement in each function.
|
|
1226
|
+
b.breakGlass._resetForTest();
|
|
1227
|
+
var re = /breakglass\/not-initialized/;
|
|
1228
|
+
async function guard(label, fn) {
|
|
1229
|
+
var threw = null;
|
|
1230
|
+
try { await fn(); } catch (e) { threw = e; }
|
|
1231
|
+
check("require-init: " + label + " throws not-initialized",
|
|
1232
|
+
threw && re.test(threw.code || ""));
|
|
1233
|
+
}
|
|
1234
|
+
await guard("policy.get", function () { return b.breakGlass.policy.get("t"); });
|
|
1235
|
+
await guard("policy.set", function () { return b.breakGlass.policy.set("t", { columns: ["c"], factors: ["totp"] }); });
|
|
1236
|
+
await guard("policy.list", function () { return b.breakGlass.policy.list(); });
|
|
1237
|
+
await guard("policy.delete", function () { return b.breakGlass.policy.delete("t"); });
|
|
1238
|
+
await guard("grant", function () { return b.breakGlass.grant({ req: _fakeReq(), table: "t", reason: "x", factor: {} }); });
|
|
1239
|
+
await guard("unsealRow", function () { return b.breakGlass.unsealRow({ id: "bg-x" }, "t", "1"); });
|
|
1240
|
+
await guard("revoke", function () { return b.breakGlass.revoke("bg-x"); });
|
|
1241
|
+
await guard("listActive", function () { return b.breakGlass.listActive({ req: _fakeReq() }); });
|
|
1242
|
+
await guard("listActiveAll", function () { return b.breakGlass.listActiveAll(); });
|
|
1243
|
+
await guard("revokeAll", function () { return b.breakGlass.revokeAll({ table: "t" }); });
|
|
1244
|
+
await guard("encryptCell", function () { return b.breakGlass.encryptCell("x", { table: "t", rowId: "1", column: "c" }); });
|
|
1245
|
+
await guard("decryptCell", function () { return b.breakGlass.decryptCell("bgcell:1:AAAA", { table: "t", rowId: "1", column: "c" }); });
|
|
1246
|
+
await guard("migrate", function () { return b.breakGlass.migrate("t"); });
|
|
1247
|
+
await guard("unsealRowAsService", function () { return b.breakGlass.unsealRowAsService(_fakeReq(), "t", "1"); });
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
// ---- Adversarial policy.set validation (identifier shape / opts shape) ----
|
|
1251
|
+
|
|
1252
|
+
async function testPolicyValidationAdversarial() {
|
|
1253
|
+
var tmpDir = _tmp();
|
|
1254
|
+
await setupTestDb(tmpDir);
|
|
1255
|
+
try {
|
|
1256
|
+
b.breakGlass.init();
|
|
1257
|
+
function reject(label, table, opts, codeRe) {
|
|
1258
|
+
return b.breakGlass.policy.set(table, opts).then(
|
|
1259
|
+
function () { check("policy.validate.adv: " + label + " (should throw)", false); },
|
|
1260
|
+
function (e) { check("policy.validate.adv: " + label, codeRe.test(e.code || "")); }
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
// opts entirely absent → "opts is required" (distinct from the missing-field
|
|
1264
|
+
// branches the happy-path validation test already covers).
|
|
1265
|
+
await reject("null opts rejected", "t", null, /breakglass\/bad-policy/);
|
|
1266
|
+
// Table name that is not a safe SQL identifier — it flows into migrate() /
|
|
1267
|
+
// unsealRowAsService() SQL, so a name with an embedded quote must be refused.
|
|
1268
|
+
await reject("table with embedded quote rejected", 'pa"tients',
|
|
1269
|
+
{ columns: ["ssn"], factors: ["totp"] }, /breakglass\/bad-policy/);
|
|
1270
|
+
await reject("table with space rejected", "pat ients",
|
|
1271
|
+
{ columns: ["ssn"], factors: ["totp"] }, /breakglass\/bad-policy/);
|
|
1272
|
+
// Column entries: non-string / empty / bad-identifier shape.
|
|
1273
|
+
await reject("non-string column rejected", "t",
|
|
1274
|
+
{ columns: [123], factors: ["totp"] }, /breakglass\/bad-policy/);
|
|
1275
|
+
await reject("empty-string column rejected", "t",
|
|
1276
|
+
{ columns: [""], factors: ["totp"] }, /breakglass\/bad-policy/);
|
|
1277
|
+
await reject("column with embedded quote rejected", "t",
|
|
1278
|
+
{ columns: ['ss"n'], factors: ["totp"] }, /breakglass\/bad-policy/);
|
|
1279
|
+
} finally {
|
|
1280
|
+
await teardownTestDb(tmpDir);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// ---- Adversarial encrypt/decrypt/migrate/revoke/policy-delete inputs ----
|
|
1285
|
+
|
|
1286
|
+
async function testCellAndAdminInputValidation() {
|
|
1287
|
+
var tmpDir = _tmp();
|
|
1288
|
+
await setupTestDb(tmpDir);
|
|
1289
|
+
try {
|
|
1290
|
+
b.breakGlass.init();
|
|
1291
|
+
await b.breakGlass.policy.set("patients", {
|
|
1292
|
+
columns: ["ssn"], factors: ["totp"], cryptographic: true,
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
async function rejects(label, fn, codeRe) {
|
|
1296
|
+
var threw = null;
|
|
1297
|
+
try { await fn(); } catch (e) { threw = e; }
|
|
1298
|
+
check("input.validate: " + label, threw && codeRe.test(threw.code || ""));
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// encryptCell bad ctx (missing / wrong-typed fields)
|
|
1302
|
+
await rejects("encryptCell null ctx",
|
|
1303
|
+
function () { return b.breakGlass.encryptCell("x", null); }, /breakglass\/bad-cell-ctx/);
|
|
1304
|
+
await rejects("encryptCell ctx missing rowId",
|
|
1305
|
+
function () { return b.breakGlass.encryptCell("x", { table: "patients", column: "ssn" }); }, /breakglass\/bad-cell-ctx/);
|
|
1306
|
+
|
|
1307
|
+
// decryptCell — bad ciphertext format, then bad ctx
|
|
1308
|
+
await rejects("decryptCell non-bgcell format",
|
|
1309
|
+
function () { return b.breakGlass.decryptCell("not-a-cell", { table: "patients", rowId: "1", column: "ssn" }); },
|
|
1310
|
+
/breakglass\/bad-ciphertext/);
|
|
1311
|
+
await rejects("decryptCell null ctx",
|
|
1312
|
+
function () { return b.breakGlass.decryptCell("bgcell:1:AAAA", null); }, /breakglass\/bad-cell-ctx/);
|
|
1313
|
+
|
|
1314
|
+
// migrate — no policy, then non-cryptographic policy
|
|
1315
|
+
await rejects("migrate no policy",
|
|
1316
|
+
function () { return b.breakGlass.migrate("no-such-glass-table"); }, /breakglass\/policy-not-set/);
|
|
1317
|
+
await b.breakGlass.policy.set("plain", { columns: ["ssn"], factors: ["totp"] }); // Model A
|
|
1318
|
+
await rejects("migrate on Model-A policy refused",
|
|
1319
|
+
function () { return b.breakGlass.migrate("plain"); }, /breakglass\/bad-policy/);
|
|
1320
|
+
|
|
1321
|
+
// revoke — empty grantId
|
|
1322
|
+
await rejects("revoke empty grantId",
|
|
1323
|
+
function () { return b.breakGlass.revoke(""); }, /breakglass\/bad-grant-opts/);
|
|
1324
|
+
|
|
1325
|
+
// policy.delete — empty table
|
|
1326
|
+
await rejects("policy.delete empty table",
|
|
1327
|
+
function () { return b.breakGlass.policy.delete(""); }, /breakglass\/bad-policy/);
|
|
1328
|
+
|
|
1329
|
+
// revokeAll — non-object criteria (distinct from the empty-criteria branch)
|
|
1330
|
+
await rejects("revokeAll null criteria",
|
|
1331
|
+
function () { return b.breakGlass.revokeAll(null); }, /breakglass\/bad-revoke-criteria/);
|
|
1332
|
+
|
|
1333
|
+
// grant — no opts object
|
|
1334
|
+
await rejects("grant no opts",
|
|
1335
|
+
function () { return b.breakGlass.grant(); }, /breakglass\/bad-grant-opts/);
|
|
1336
|
+
} finally {
|
|
1337
|
+
await teardownTestDb(tmpDir);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// ---- Migrate on an empty policy'd table (paging break on first page) ----
|
|
1342
|
+
|
|
1343
|
+
async function testMigrateEmptyTable() {
|
|
1344
|
+
var tmpDir = _tmp();
|
|
1345
|
+
await setupTestDb(tmpDir);
|
|
1346
|
+
try {
|
|
1347
|
+
b.breakGlass.init();
|
|
1348
|
+
b.queue.init({ backends: { primary: { protocol: "local" } } }); // creates _blamejs_jobs, no rows
|
|
1349
|
+
await b.breakGlass.policy.set("_blamejs_jobs", {
|
|
1350
|
+
columns: ["payload"], factors: ["totp"], cryptographic: true,
|
|
1351
|
+
});
|
|
1352
|
+
// No jobs enqueued → the first keyset page is empty → the loop breaks
|
|
1353
|
+
// immediately with zero rows and emits the completion audit.
|
|
1354
|
+
var summary = await b.breakGlass.migrate("_blamejs_jobs", { batchSize: 25 });
|
|
1355
|
+
check("migrate empty: totalRows 0", summary.totalRows === 0);
|
|
1356
|
+
check("migrate empty: migratedRows 0", summary.migratedRows === 0);
|
|
1357
|
+
check("migrate empty: table echoed", summary.table === "_blamejs_jobs");
|
|
1358
|
+
try { await b.queue.shutdown({ timeoutMs: 200 }); } catch (_e) {}
|
|
1359
|
+
} finally {
|
|
1360
|
+
await teardownTestDb(tmpDir);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
// ---- DEK survives an in-memory cache reset (re-unseal from the policy row) ----
|
|
1365
|
+
|
|
1366
|
+
async function testDekReUnsealFromPolicyRow() {
|
|
1367
|
+
var tmpDir = _tmp();
|
|
1368
|
+
await setupTestDb(tmpDir);
|
|
1369
|
+
try {
|
|
1370
|
+
b.breakGlass.init();
|
|
1371
|
+
await b.breakGlass.policy.set("patients", {
|
|
1372
|
+
columns: ["ssn"], factors: ["totp"], cryptographic: true,
|
|
1373
|
+
});
|
|
1374
|
+
// First use generates + vault-seals the DEK into the policy row and
|
|
1375
|
+
// caches it in-process.
|
|
1376
|
+
var ct = await b.breakGlass.encryptCell("123-45-6789",
|
|
1377
|
+
{ table: "patients", rowId: "p-1", column: "ssn" });
|
|
1378
|
+
// Reset drops the in-memory DEK cache but leaves the sealed DEK in the DB.
|
|
1379
|
+
// Re-init, then decrypt: _ensureDek must re-unseal the stored DEK (the
|
|
1380
|
+
// `sealed` branch) rather than mint a fresh one, or the round-trip breaks.
|
|
1381
|
+
b.breakGlass._resetForTest();
|
|
1382
|
+
b.breakGlass.init();
|
|
1383
|
+
var pt = await b.breakGlass.decryptCell(ct,
|
|
1384
|
+
{ table: "patients", rowId: "p-1", column: "ssn" });
|
|
1385
|
+
check("dek-reuse: sealed DEK re-unseals after cache reset", pt === "123-45-6789");
|
|
1386
|
+
} finally {
|
|
1387
|
+
await teardownTestDb(tmpDir);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// ---- unsealRow argument-shape guards + not-found grant ----
|
|
1392
|
+
|
|
1393
|
+
async function testUnsealRowArgGuards() {
|
|
1394
|
+
var tmpDir = _tmp();
|
|
1395
|
+
await setupTestDb(tmpDir);
|
|
1396
|
+
try {
|
|
1397
|
+
b.breakGlass.init();
|
|
1398
|
+
async function rejects(label, fn, codeRe) {
|
|
1399
|
+
var threw = null;
|
|
1400
|
+
try { await fn(); } catch (e) { threw = e; }
|
|
1401
|
+
check("unsealRow.guard: " + label, threw && codeRe.test(threw.code || ""));
|
|
1402
|
+
}
|
|
1403
|
+
await rejects("null grant handle",
|
|
1404
|
+
function () { return b.breakGlass.unsealRow(null, "t", "1"); }, /breakglass\/bad-grant-opts/);
|
|
1405
|
+
await rejects("handle without id",
|
|
1406
|
+
function () { return b.breakGlass.unsealRow({}, "t", "1"); }, /breakglass\/bad-grant-opts/);
|
|
1407
|
+
await rejects("empty table",
|
|
1408
|
+
function () { return b.breakGlass.unsealRow({ id: "bg-x" }, "", "1"); }, /breakglass\/bad-grant-opts/);
|
|
1409
|
+
await rejects("missing rowId",
|
|
1410
|
+
function () { return b.breakGlass.unsealRow({ id: "bg-x" }, "t", null); }, /breakglass\/bad-grant-opts/);
|
|
1411
|
+
await rejects("empty rowId",
|
|
1412
|
+
function () { return b.breakGlass.unsealRow({ id: "bg-x" }, "t", ""); }, /breakglass\/bad-grant-opts/);
|
|
1413
|
+
// A well-shaped handle whose id doesn't exist in the grants table reads
|
|
1414
|
+
// as revoked/never-issued (fail-closed).
|
|
1415
|
+
await rejects("unknown grant id reads as revoked",
|
|
1416
|
+
function () { return b.breakGlass.unsealRow({ id: "bg-does-not-exist" }, "t", "1"); },
|
|
1417
|
+
/breakglass\/grant-revoked/);
|
|
1418
|
+
} finally {
|
|
1419
|
+
await teardownTestDb(tmpDir);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
// ---- unsealRow: expired grant + row-not-found ----
|
|
1424
|
+
|
|
1425
|
+
async function testUnsealRowExpiredAndRowNotFound() {
|
|
1426
|
+
var tmpDir = _tmp();
|
|
1427
|
+
await setupTestDb(tmpDir);
|
|
1428
|
+
try {
|
|
1429
|
+
b.breakGlass.init();
|
|
1430
|
+
b.queue.init({ backends: { primary: { protocol: "local" } } });
|
|
1431
|
+
var jid = await b.queue.enqueue("bg-exp-q", { secret: "row-exp" });
|
|
1432
|
+
|
|
1433
|
+
// Expired-grant path: a short-TTL grant whose expiry check trips before
|
|
1434
|
+
// any row read.
|
|
1435
|
+
await b.breakGlass.policy.set("patients", {
|
|
1436
|
+
columns: ["ssn"], factors: ["totp"], grantTtl: 20,
|
|
1437
|
+
});
|
|
1438
|
+
var totpA = _validTotp();
|
|
1439
|
+
var expiring = await b.breakGlass.grant({
|
|
1440
|
+
req: _fakeReq(),
|
|
1441
|
+
table: "patients",
|
|
1442
|
+
reason: "expired-grant coverage: short ttl grant",
|
|
1443
|
+
factor: { type: "totp", code: totpA.code, secret: totpA.secret },
|
|
1444
|
+
});
|
|
1445
|
+
await helpers.waitUntil(function () { return Date.now() > expiring.expiresAt; },
|
|
1446
|
+
{ label: "break-glass.unsealRow: 20ms grant reaches expiry" });
|
|
1447
|
+
var threwExpired = null;
|
|
1448
|
+
try { await b.breakGlass.unsealRow(expiring, "patients", "any-row", { req: _fakeReq() }); }
|
|
1449
|
+
catch (e) { threwExpired = e; }
|
|
1450
|
+
check("unsealRow: expired grant refused",
|
|
1451
|
+
threwExpired && /breakglass\/grant-expired/.test(threwExpired.code));
|
|
1452
|
+
|
|
1453
|
+
// Row-not-found path: a valid, live grant against a real table but a
|
|
1454
|
+
// rowId that isn't present. The grant is NOT consumed (SELECT-before-
|
|
1455
|
+
// increment ordering), so the error is row-not-found, not exhausted.
|
|
1456
|
+
await b.breakGlass.policy.set("_blamejs_jobs", {
|
|
1457
|
+
columns: ["payload"], factors: ["totp"], maxRowsPerGrant: 3,
|
|
1458
|
+
});
|
|
1459
|
+
var totpB = _validTotp();
|
|
1460
|
+
var liveGrant = await b.breakGlass.grant({
|
|
1461
|
+
req: _fakeReq(),
|
|
1462
|
+
table: "_blamejs_jobs",
|
|
1463
|
+
reason: "row-not-found coverage: live grant on real table",
|
|
1464
|
+
factor: { type: "totp", code: totpB.code, secret: totpB.secret },
|
|
1465
|
+
});
|
|
1466
|
+
var threwMissing = null;
|
|
1467
|
+
try { await b.breakGlass.unsealRow(liveGrant, "_blamejs_jobs", "no-such-row-id", { req: _fakeReq() }); }
|
|
1468
|
+
catch (e) { threwMissing = e; }
|
|
1469
|
+
check("unsealRow: missing row refused with row-not-found",
|
|
1470
|
+
threwMissing && /breakglass\/row-not-found/.test(threwMissing.code));
|
|
1471
|
+
// The failed lookup did not consume the grant — a real row still reads.
|
|
1472
|
+
var ok = await b.breakGlass.unsealRow(liveGrant, "_blamejs_jobs", jid.jobId, { req: _fakeReq() });
|
|
1473
|
+
check("unsealRow: row-not-found did not consume the grant",
|
|
1474
|
+
ok && ok.payload && ok.payload.indexOf("row-exp") !== -1);
|
|
1475
|
+
|
|
1476
|
+
try { await b.queue.shutdown({ timeoutMs: 200 }); } catch (_e) {}
|
|
1477
|
+
} finally {
|
|
1478
|
+
await teardownTestDb(tmpDir);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
// ---- unsealRow Model B: a corrupt / wrong-context cell fails closed ----
|
|
1483
|
+
|
|
1484
|
+
async function testUnsealRowCellDecryptFailed() {
|
|
1485
|
+
var tmpDir = _tmp();
|
|
1486
|
+
await setupTestDb(tmpDir);
|
|
1487
|
+
try {
|
|
1488
|
+
b.breakGlass.init();
|
|
1489
|
+
b.queue.init({ backends: { primary: { protocol: "local" } } });
|
|
1490
|
+
var jid = await b.queue.enqueue("bg-cd-q", { secret: "outer" });
|
|
1491
|
+
await b.breakGlass.policy.set("_blamejs_jobs", {
|
|
1492
|
+
columns: ["payload"], factors: ["totp"], cryptographic: true, maxRowsPerGrant: 3,
|
|
1493
|
+
});
|
|
1494
|
+
// Write a ciphertext encrypted for a DIFFERENT rowId into this row. The
|
|
1495
|
+
// AAD is bound to (table, rowId, column), so decrypting under the real
|
|
1496
|
+
// rowId fails the AEAD verify — unsealRow must surface cell-decrypt-failed
|
|
1497
|
+
// rather than return garbage or leak the wrong row's value.
|
|
1498
|
+
var wrongCtx = await b.breakGlass.encryptCell("someone-elses-ssn",
|
|
1499
|
+
{ table: "_blamejs_jobs", rowId: "a-different-row", column: "payload" });
|
|
1500
|
+
await b.clusterStorage.execute(
|
|
1501
|
+
"UPDATE _blamejs_jobs SET payload = ? WHERE _id = ?", [wrongCtx, jid.jobId]);
|
|
1502
|
+
|
|
1503
|
+
var totp = _validTotp();
|
|
1504
|
+
var grant = await b.breakGlass.grant({
|
|
1505
|
+
req: _fakeReq(),
|
|
1506
|
+
table: "_blamejs_jobs",
|
|
1507
|
+
reason: "cell-decrypt-failed coverage: swapped ciphertext",
|
|
1508
|
+
factor: { type: "totp", code: totp.code, secret: totp.secret },
|
|
1509
|
+
});
|
|
1510
|
+
var threw = null;
|
|
1511
|
+
try { await b.breakGlass.unsealRow(grant, "_blamejs_jobs", jid.jobId, { req: _fakeReq() }); }
|
|
1512
|
+
catch (e) { threw = e; }
|
|
1513
|
+
check("unsealRow Model B: wrong-context cell fails closed",
|
|
1514
|
+
threw && /breakglass\/cell-decrypt-failed/.test(threw.code));
|
|
1515
|
+
|
|
1516
|
+
try { await b.queue.shutdown({ timeoutMs: 200 }); } catch (_e) {}
|
|
1517
|
+
} finally {
|
|
1518
|
+
await teardownTestDb(tmpDir);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
// ---- _enforceGrantPins: session pin with a null binding fails closed ----
|
|
1523
|
+
|
|
1524
|
+
async function testSessionPinFailClosedOnNullBinding() {
|
|
1525
|
+
var tmpDir = _tmp();
|
|
1526
|
+
await setupTestDb(tmpDir);
|
|
1527
|
+
try {
|
|
1528
|
+
b.breakGlass.init();
|
|
1529
|
+
b.queue.init({ backends: { primary: { protocol: "local" } } });
|
|
1530
|
+
var jid = await b.queue.enqueue("bg-sess-fc-q", { secret: "row-sess-fc" });
|
|
1531
|
+
await b.breakGlass.policy.set("_blamejs_jobs", {
|
|
1532
|
+
columns: ["payload"], factors: ["totp"], maxRowsPerGrant: 5,
|
|
1533
|
+
pinIp: false, sessionPin: true,
|
|
1534
|
+
});
|
|
1535
|
+
// Mint from a request carrying NO session id → grantRow.sessionId is null.
|
|
1536
|
+
// With sessionPin on, redemption must refuse rather than skip the pin.
|
|
1537
|
+
var noSessReq = {
|
|
1538
|
+
user: { id: "user-test-1" },
|
|
1539
|
+
socket: { remoteAddress: "127.0.0.1" },
|
|
1540
|
+
headers: { "user-agent": "test-agent" },
|
|
1541
|
+
method: "POST",
|
|
1542
|
+
url: "/admin/break-glass",
|
|
1543
|
+
};
|
|
1544
|
+
var totp = _validTotp();
|
|
1545
|
+
var grant = await b.breakGlass.grant({
|
|
1546
|
+
req: noSessReq,
|
|
1547
|
+
table: "_blamejs_jobs",
|
|
1548
|
+
reason: "session-pin fail-closed: minting with no session id",
|
|
1549
|
+
factor: { type: "totp", code: totp.code, secret: totp.secret },
|
|
1550
|
+
});
|
|
1551
|
+
var threw = null;
|
|
1552
|
+
try {
|
|
1553
|
+
await b.breakGlass.unsealRow(grant, "_blamejs_jobs", jid.jobId,
|
|
1554
|
+
{ req: _fakeReq({ session: { id: "sess-anything" } }) });
|
|
1555
|
+
} catch (e) { threw = e; }
|
|
1556
|
+
check("session-pin fail-closed: null binding refuses redemption",
|
|
1557
|
+
threw && /breakglass\/grant-session-mismatch/.test(threw.code));
|
|
1558
|
+
|
|
1559
|
+
try { await b.queue.shutdown({ timeoutMs: 200 }); } catch (_e) {}
|
|
1560
|
+
} finally {
|
|
1561
|
+
await teardownTestDb(tmpDir);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// ---- grant: factor lockout after repeated failures ----
|
|
1566
|
+
|
|
1567
|
+
async function testGrantFactorLockout() {
|
|
1568
|
+
var tmpDir = _tmp();
|
|
1569
|
+
await setupTestDb(tmpDir);
|
|
1570
|
+
try {
|
|
1571
|
+
b.breakGlass.init();
|
|
1572
|
+
await b.breakGlass.policy.set("patients", { columns: ["ssn"], factors: ["totp"] });
|
|
1573
|
+
var totp = _validTotp();
|
|
1574
|
+
var badCode = totp.code === "000000" ? "000001" : "000000";
|
|
1575
|
+
function attempt(reason) {
|
|
1576
|
+
return b.breakGlass.grant({
|
|
1577
|
+
req: _fakeReq(),
|
|
1578
|
+
table: "patients",
|
|
1579
|
+
reason: reason,
|
|
1580
|
+
factor: { type: "totp", secret: totp.secret, code: badCode },
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
// Five failing factor verifications trip the lockout primitive
|
|
1584
|
+
// (maxAttempts 5). Each of the five is a plain bad-factor rejection.
|
|
1585
|
+
for (var i = 0; i < 5; i++) {
|
|
1586
|
+
var threw = null;
|
|
1587
|
+
try { await attempt("lockout coverage: failing attempt " + (i + 1)); }
|
|
1588
|
+
catch (e) { threw = e; }
|
|
1589
|
+
check("lockout: attempt " + (i + 1) + " rejected",
|
|
1590
|
+
threw && /breakglass\/(bad-factor|factor-rate-limited)/.test(threw.code || ""));
|
|
1591
|
+
}
|
|
1592
|
+
// The sixth attempt is refused at the lockout gate BEFORE factor
|
|
1593
|
+
// verification — a brute-forcer is shut out.
|
|
1594
|
+
var locked = null;
|
|
1595
|
+
try { await attempt("lockout coverage: should be rate-limited"); }
|
|
1596
|
+
catch (e) { locked = e; }
|
|
1597
|
+
check("lockout: sixth attempt refused as factor-rate-limited",
|
|
1598
|
+
locked && /breakglass\/factor-rate-limited/.test(locked.code || ""));
|
|
1599
|
+
} finally {
|
|
1600
|
+
await teardownTestDb(tmpDir);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// ---- grant: requireScope satisfied via req.apiKey.scopes fallback ----
|
|
1605
|
+
|
|
1606
|
+
async function testGrantScopeViaApiKey() {
|
|
1607
|
+
var tmpDir = _tmp();
|
|
1608
|
+
await setupTestDb(tmpDir);
|
|
1609
|
+
try {
|
|
1610
|
+
b.breakGlass.init();
|
|
1611
|
+
await b.breakGlass.policy.set("patients", {
|
|
1612
|
+
columns: ["ssn"], factors: ["totp"], requireScope: "phi:admin",
|
|
1613
|
+
});
|
|
1614
|
+
// No req.user — the actor is an apiKey. Both the actorId resolution and
|
|
1615
|
+
// the requireScope check must fall back to req.apiKey (id + scopes).
|
|
1616
|
+
var totp = _validTotp();
|
|
1617
|
+
var apiKeyReq = {
|
|
1618
|
+
apiKey: { id: "ak-phi-admin", scopes: ["phi:admin"] },
|
|
1619
|
+
session: { id: "sess-svc-1" },
|
|
1620
|
+
socket: { remoteAddress: "127.0.0.1" },
|
|
1621
|
+
headers: { "user-agent": "svc-agent" },
|
|
1622
|
+
method: "POST",
|
|
1623
|
+
url: "/admin/break-glass",
|
|
1624
|
+
};
|
|
1625
|
+
var grant = await b.breakGlass.grant({
|
|
1626
|
+
req: apiKeyReq,
|
|
1627
|
+
table: "patients",
|
|
1628
|
+
reason: "apikey-scope coverage: service actor with phi:admin scope",
|
|
1629
|
+
factor: { type: "totp", code: totp.code, secret: totp.secret },
|
|
1630
|
+
});
|
|
1631
|
+
check("apikey-scope: grant minted for apiKey actor carrying required scope",
|
|
1632
|
+
grant && typeof grant.id === "string");
|
|
1633
|
+
} finally {
|
|
1634
|
+
await teardownTestDb(tmpDir);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// ---- grant: TOTP factor missing secret / missing code ----
|
|
1639
|
+
|
|
1640
|
+
async function testGrantTotpFactorShapeRejections() {
|
|
1641
|
+
var tmpDir = _tmp();
|
|
1642
|
+
await setupTestDb(tmpDir);
|
|
1643
|
+
try {
|
|
1644
|
+
b.breakGlass.init();
|
|
1645
|
+
await b.breakGlass.policy.set("patients", { columns: ["ssn"], factors: ["totp"] });
|
|
1646
|
+
async function rejects(label, factor) {
|
|
1647
|
+
var threw = null;
|
|
1648
|
+
try {
|
|
1649
|
+
await b.breakGlass.grant({
|
|
1650
|
+
req: _fakeReq(),
|
|
1651
|
+
table: "patients",
|
|
1652
|
+
reason: "totp factor-shape coverage: " + label,
|
|
1653
|
+
factor: factor,
|
|
1654
|
+
});
|
|
1655
|
+
} catch (e) { threw = e; }
|
|
1656
|
+
check("totp-shape: " + label, threw && /breakglass\/bad-factor/.test(threw.code || ""));
|
|
1657
|
+
}
|
|
1658
|
+
// factor.type is "totp" (accepted), but the credential material is
|
|
1659
|
+
// incomplete — _verifyTotpFactor returns ok:false and grant refuses.
|
|
1660
|
+
await rejects("missing secret", { type: "totp", code: "123456" });
|
|
1661
|
+
await rejects("missing code", { type: "totp", secret: "AAAAAAAAAAAAAAAA" });
|
|
1662
|
+
} finally {
|
|
1663
|
+
await teardownTestDb(tmpDir);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
// ---- grant: audit reason stored as HMAC (not cleartext) ----
|
|
1668
|
+
|
|
1669
|
+
async function testGrantAuditReasonHmac() {
|
|
1670
|
+
var tmpDir = _tmp();
|
|
1671
|
+
await setupTestDb(tmpDir);
|
|
1672
|
+
try {
|
|
1673
|
+
b.breakGlass.init();
|
|
1674
|
+
await b.breakGlass.policy.set("patients", {
|
|
1675
|
+
columns: ["ssn"], factors: ["totp"], auditReasonStorage: "hmac",
|
|
1676
|
+
});
|
|
1677
|
+
var totp = _validTotp();
|
|
1678
|
+
var grant = await b.breakGlass.grant({
|
|
1679
|
+
req: _fakeReq(),
|
|
1680
|
+
table: "patients",
|
|
1681
|
+
reason: "hmac-audit coverage: reason should hash, not store cleartext",
|
|
1682
|
+
factor: { type: "totp", code: totp.code, secret: totp.secret },
|
|
1683
|
+
});
|
|
1684
|
+
// The grant still mints; the branch under test is the audit-reason
|
|
1685
|
+
// derivation (hmac mode → cleartext suppressed, hmac digest emitted).
|
|
1686
|
+
check("audit-reason hmac: grant minted with hmac reason storage",
|
|
1687
|
+
grant && typeof grant.id === "string");
|
|
1688
|
+
} finally {
|
|
1689
|
+
await teardownTestDb(tmpDir);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
// ---- unsealRowAsService: input guards + Model B decrypt + roles fallback ----
|
|
1694
|
+
|
|
1695
|
+
async function testUnsealRowAsServiceGuardsAndModelB() {
|
|
1696
|
+
var tmpDir = _tmp();
|
|
1697
|
+
await setupTestDb(tmpDir);
|
|
1698
|
+
try {
|
|
1699
|
+
b.breakGlass.init();
|
|
1700
|
+
b.queue.init({ backends: { primary: { protocol: "local" } } });
|
|
1701
|
+
var jid = await b.queue.enqueue("bg-svc-b-q", { secret: "outer" });
|
|
1702
|
+
|
|
1703
|
+
async function rejects(label, fn, codeRe) {
|
|
1704
|
+
var threw = null;
|
|
1705
|
+
try { await fn(); } catch (e) { threw = e; }
|
|
1706
|
+
check("svc.guard: " + label, threw && codeRe.test(threw.code || ""));
|
|
1707
|
+
}
|
|
1708
|
+
// Argument-shape + no-policy guards (before any bypass check).
|
|
1709
|
+
await rejects("null req",
|
|
1710
|
+
function () { return b.breakGlass.unsealRowAsService(null, "_blamejs_jobs", "1"); },
|
|
1711
|
+
/breakglass\/bad-grant-opts/);
|
|
1712
|
+
await rejects("empty table",
|
|
1713
|
+
function () { return b.breakGlass.unsealRowAsService({ apiKey: { id: "ak" } }, "", "1"); },
|
|
1714
|
+
/breakglass\/bad-grant-opts/);
|
|
1715
|
+
await rejects("no policy for table",
|
|
1716
|
+
function () { return b.breakGlass.unsealRowAsService({ apiKey: { id: "ak" } }, "no-policy-table", "1"); },
|
|
1717
|
+
/breakglass\/policy-not-set/);
|
|
1718
|
+
|
|
1719
|
+
// Cryptographic-mode service bypass whose apiKey authorizes via `roles`
|
|
1720
|
+
// (not `scopes`) — the role-check fallback. The Model B cell decrypts
|
|
1721
|
+
// through the same encryption-context binding as the operator path.
|
|
1722
|
+
await b.breakGlass.policy.set("_blamejs_jobs", {
|
|
1723
|
+
columns: ["payload"], factors: ["totp"], cryptographic: true,
|
|
1724
|
+
serviceAccountBypass: {
|
|
1725
|
+
enabled: true, apiKeyIds: ["ak-svc-b"], requireRole: "service:phi-reader",
|
|
1726
|
+
},
|
|
1727
|
+
});
|
|
1728
|
+
var cell = await b.breakGlass.encryptCell("alice's diagnosis (svc Model B)",
|
|
1729
|
+
{ table: "_blamejs_jobs", rowId: jid.jobId, column: "payload" });
|
|
1730
|
+
await b.clusterStorage.execute(
|
|
1731
|
+
"UPDATE _blamejs_jobs SET payload = ? WHERE _id = ?", [cell, jid.jobId]);
|
|
1732
|
+
|
|
1733
|
+
var serviceReq = {
|
|
1734
|
+
apiKey: { id: "ak-svc-b", roles: ["service:phi-reader"] }, // roles, not scopes
|
|
1735
|
+
socket: { remoteAddress: "10.0.0.5" },
|
|
1736
|
+
headers: { "user-agent": "blamejs-svc" },
|
|
1737
|
+
method: "GET",
|
|
1738
|
+
url: "/cron/deid",
|
|
1739
|
+
};
|
|
1740
|
+
var row = await b.breakGlass.unsealRowAsService(serviceReq, "_blamejs_jobs", jid.jobId,
|
|
1741
|
+
{ reason: "svc Model B coverage: nightly de-identification" });
|
|
1742
|
+
check("svc Model B: roles-fallback authorized bypass decrypts the cell",
|
|
1743
|
+
row && row.payload === "alice's diagnosis (svc Model B)");
|
|
1744
|
+
|
|
1745
|
+
// Authorized bypass but a rowId that doesn't exist → row-not-found.
|
|
1746
|
+
await rejects("authorized bypass, missing row",
|
|
1747
|
+
function () {
|
|
1748
|
+
return b.breakGlass.unsealRowAsService(serviceReq, "_blamejs_jobs", "no-such-svc-row",
|
|
1749
|
+
{ reason: "svc row-not-found coverage" });
|
|
1750
|
+
}, /breakglass\/row-not-found/);
|
|
1751
|
+
|
|
1752
|
+
try { await b.queue.shutdown({ timeoutMs: 200 }); } catch (_e) {}
|
|
1753
|
+
} finally {
|
|
1754
|
+
await teardownTestDb(tmpDir);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
// ---- Passkey factor: full-field assertion drives the verify path ----
|
|
1759
|
+
|
|
1760
|
+
async function testPasskeyFullFieldFactorPath() {
|
|
1761
|
+
// The existing passkey test omits every field, so _verifyPasskeyFactor
|
|
1762
|
+
// short-circuits at the presence check. Supplying all five fields drives
|
|
1763
|
+
// the real WebAuthn verify call — a well-shaped but bogus assertion fails
|
|
1764
|
+
// verification (returns/raises), and grant refuses as bad-factor.
|
|
1765
|
+
var tmpDir = _tmp();
|
|
1766
|
+
await setupTestDb(tmpDir);
|
|
1767
|
+
try {
|
|
1768
|
+
b.breakGlass.init();
|
|
1769
|
+
await b.breakGlass.policy.set("patients", { columns: ["ssn"], factors: ["passkey"] });
|
|
1770
|
+
var threw = null;
|
|
1771
|
+
try {
|
|
1772
|
+
await b.breakGlass.grant({
|
|
1773
|
+
req: _fakeReq(),
|
|
1774
|
+
table: "patients",
|
|
1775
|
+
reason: "passkey verify-path coverage: bogus but well-shaped assertion",
|
|
1776
|
+
factor: {
|
|
1777
|
+
type: "passkey",
|
|
1778
|
+
response: {
|
|
1779
|
+
id: "cred-id",
|
|
1780
|
+
rawId: "Y3JlZC1pZA",
|
|
1781
|
+
type: "public-key",
|
|
1782
|
+
response: {
|
|
1783
|
+
clientDataJSON: "e30",
|
|
1784
|
+
authenticatorData: "AAAA",
|
|
1785
|
+
signature: "AAAA",
|
|
1786
|
+
},
|
|
1787
|
+
},
|
|
1788
|
+
expectedChallenge: "Y2hhbGxlbmdl",
|
|
1789
|
+
expectedOrigin: "https://example.com",
|
|
1790
|
+
expectedRPID: "example.com",
|
|
1791
|
+
credential: { id: "cred-id", publicKey: "AAAA", counter: 0 },
|
|
1792
|
+
},
|
|
1793
|
+
});
|
|
1794
|
+
} catch (e) { threw = e; }
|
|
1795
|
+
check("passkey full-field: bogus assertion refused as bad-factor",
|
|
1796
|
+
threw && /breakglass\/bad-factor/.test(threw.code || ""));
|
|
1797
|
+
} finally {
|
|
1798
|
+
await teardownTestDb(tmpDir);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
// ---- listActive / listActiveAll / revokeAll edge branches ----
|
|
1803
|
+
|
|
1804
|
+
async function testListAndRevokeEdgeBranches() {
|
|
1805
|
+
var tmpDir = _tmp();
|
|
1806
|
+
await setupTestDb(tmpDir);
|
|
1807
|
+
try {
|
|
1808
|
+
b.breakGlass.init();
|
|
1809
|
+
// Unauthenticated listActive (no actor on req) → empty array, no leak.
|
|
1810
|
+
var none = await b.breakGlass.listActive({ req: { socket: { remoteAddress: "127.0.0.1" }, headers: {} } });
|
|
1811
|
+
check("listActive: unauthenticated caller gets []", Array.isArray(none) && none.length === 0);
|
|
1812
|
+
// No opts at all → still []
|
|
1813
|
+
var noneBare = await b.breakGlass.listActive();
|
|
1814
|
+
check("listActive: no-opts caller gets []", Array.isArray(noneBare) && noneBare.length === 0);
|
|
1815
|
+
|
|
1816
|
+
await b.breakGlass.policy.set("t_edge", { columns: ["c"], factors: ["totp"], maxRowsPerGrant: 3 });
|
|
1817
|
+
var totp = _validTotp();
|
|
1818
|
+
var g = await b.breakGlass.grant({
|
|
1819
|
+
req: _fakeReq(),
|
|
1820
|
+
table: "t_edge",
|
|
1821
|
+
reason: "list/revoke edge-branch coverage grant",
|
|
1822
|
+
factor: { type: "totp", code: totp.code, secret: totp.secret },
|
|
1823
|
+
});
|
|
1824
|
+
void g;
|
|
1825
|
+
|
|
1826
|
+
// listActiveAll `since` filter branch (existing tests only exercise the
|
|
1827
|
+
// table filter). A floor in the past keeps the grant; a floor in the
|
|
1828
|
+
// future filters it out.
|
|
1829
|
+
var sincePast = await b.breakGlass.listActiveAll({ since: Date.now() - C.TIME.minutes(5) });
|
|
1830
|
+
check("listActiveAll: since=past keeps the grant", sincePast.length === 1);
|
|
1831
|
+
var sinceFuture = await b.breakGlass.listActiveAll({ since: Date.now() + C.TIME.minutes(5) });
|
|
1832
|
+
check("listActiveAll: since=future filters it out", sinceFuture.length === 0);
|
|
1833
|
+
|
|
1834
|
+
// revokeAll by actorId (the computeDerived predicate branch; existing
|
|
1835
|
+
// tests only revoke by table).
|
|
1836
|
+
var result = await b.breakGlass.revokeAll({ actorId: "user-test-1", reason: "ir-actor-scope" });
|
|
1837
|
+
check("revokeAll: by actorId revokes the actor's grant", result && result.revokedCount === 1);
|
|
1838
|
+
var after = await b.breakGlass.listActiveAll();
|
|
1839
|
+
check("revokeAll: actor's grant gone after revoke", after.length === 0);
|
|
1840
|
+
} finally {
|
|
1841
|
+
await teardownTestDb(tmpDir);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1170
1845
|
async function run() {
|
|
1171
1846
|
testSurface();
|
|
1172
1847
|
testInitOptsValidation();
|
|
1848
|
+
await testRequireInitGuards();
|
|
1173
1849
|
await testPolicyCRUD();
|
|
1174
1850
|
await testPolicyValidation();
|
|
1175
1851
|
await testGrantHappyPath();
|
|
@@ -1178,6 +1854,7 @@ async function run() {
|
|
|
1178
1854
|
await testConcurrentTotpGrantReplay();
|
|
1179
1855
|
await testUnsealRowLifecycle();
|
|
1180
1856
|
await testGrantExhaustion();
|
|
1857
|
+
await testConcurrentUnsealRowDoubleClaim();
|
|
1181
1858
|
await testGrantRevoke();
|
|
1182
1859
|
await testTableMismatch();
|
|
1183
1860
|
await testSweepExpiredGrants();
|
|
@@ -1202,6 +1879,22 @@ async function run() {
|
|
|
1202
1879
|
await testServiceAccountBypassHappyPath();
|
|
1203
1880
|
await testServiceAccountBypassRefusalPaths();
|
|
1204
1881
|
await testListActiveAllAndRevokeAll();
|
|
1882
|
+
// Uncovered error / adversarial / defensive branch coverage
|
|
1883
|
+
await testPolicyValidationAdversarial();
|
|
1884
|
+
await testCellAndAdminInputValidation();
|
|
1885
|
+
await testMigrateEmptyTable();
|
|
1886
|
+
await testDekReUnsealFromPolicyRow();
|
|
1887
|
+
await testUnsealRowArgGuards();
|
|
1888
|
+
await testUnsealRowExpiredAndRowNotFound();
|
|
1889
|
+
await testUnsealRowCellDecryptFailed();
|
|
1890
|
+
await testSessionPinFailClosedOnNullBinding();
|
|
1891
|
+
await testGrantFactorLockout();
|
|
1892
|
+
await testGrantScopeViaApiKey();
|
|
1893
|
+
await testGrantTotpFactorShapeRejections();
|
|
1894
|
+
await testGrantAuditReasonHmac();
|
|
1895
|
+
await testUnsealRowAsServiceGuardsAndModelB();
|
|
1896
|
+
await testPasskeyFullFieldFactorPath();
|
|
1897
|
+
await testListAndRevokeEdgeBranches();
|
|
1205
1898
|
}
|
|
1206
1899
|
|
|
1207
1900
|
module.exports = { run: run };
|