@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
|
@@ -1505,6 +1505,868 @@ async function testApiEncryptEnvelopeMetadataAadBound() {
|
|
|
1505
1505
|
check("aad: genuine response still decrypts after refused forgery", plain.ok === 1);
|
|
1506
1506
|
}
|
|
1507
1507
|
|
|
1508
|
+
// ---- error / adversarial / option-default branch coverage ----
|
|
1509
|
+
|
|
1510
|
+
// A per-request bodyReq carrying a preset, already-parsed body.
|
|
1511
|
+
function _encReq(headers, body) {
|
|
1512
|
+
var req = _bodyReq("POST", headers || { "content-type": "application/json" }, "");
|
|
1513
|
+
req.body = body;
|
|
1514
|
+
return req;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
// A session store whose get() re-shapes the stored sessionKey through
|
|
1518
|
+
// `transform` — models an operator store that JSON/base64-serialised the
|
|
1519
|
+
// key (or a corrupt one). set() keeps a shallow copy so the bootstrap
|
|
1520
|
+
// write persists a real Buffer; the reshape only bites on the subsequent
|
|
1521
|
+
// get(), exercising the middleware's key-coercion branches.
|
|
1522
|
+
function _coercingSessionStore(transform) {
|
|
1523
|
+
var rows = new Map();
|
|
1524
|
+
return {
|
|
1525
|
+
get: async function (sid) {
|
|
1526
|
+
var row = rows.get(sid);
|
|
1527
|
+
if (!row) return null;
|
|
1528
|
+
var copy = Object.assign({}, row);
|
|
1529
|
+
copy.sessionKey = transform(row.sessionKey);
|
|
1530
|
+
return copy;
|
|
1531
|
+
},
|
|
1532
|
+
set: async function (sid, row) { rows.set(sid, Object.assign({}, row)); },
|
|
1533
|
+
delete: async function (sid) { rows.delete(sid); },
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
async function testApiEncryptKeypairNonObjectAndMissingFields() {
|
|
1538
|
+
// _validateKeypair's non-object guard (a keypairs entry that isn't an
|
|
1539
|
+
// object at all) and the publicKey/privateKey-missing guard — distinct
|
|
1540
|
+
// from the ecPublicKey guard the existing suite already trips.
|
|
1541
|
+
var threw = null;
|
|
1542
|
+
try { b.middleware.apiEncrypt({ keypairs: [null] }); } catch (e) { threw = e; }
|
|
1543
|
+
check("keypairs entry that is null rejected", threw && threw.code === "INVALID_KEYPAIR");
|
|
1544
|
+
threw = null;
|
|
1545
|
+
try { b.middleware.apiEncrypt({ keypair: "not-an-object" }); } catch (e) { threw = e; }
|
|
1546
|
+
check("keypair that is a string rejected", threw && threw.code === "INVALID_KEYPAIR");
|
|
1547
|
+
threw = null;
|
|
1548
|
+
try { b.middleware.apiEncrypt({ keypair: {} }); } catch (e) { threw = e; }
|
|
1549
|
+
check("keypair missing publicKey/privateKey rejected",
|
|
1550
|
+
threw && threw.code === "INVALID_KEYPAIR" && /publicKey \+ \.privateKey/.test(threw.message));
|
|
1551
|
+
// create() with no args at all — the `opts || {}` default plus the
|
|
1552
|
+
// no-keypair-provided throw.
|
|
1553
|
+
threw = null;
|
|
1554
|
+
try { b.middleware.apiEncrypt(); } catch (e) { threw = e; }
|
|
1555
|
+
check("apiEncrypt() with no args rejected (missing keypair)",
|
|
1556
|
+
threw && threw.code === "INVALID_KEYPAIR");
|
|
1557
|
+
// client()/httpClient() with no args — the `opts || {}` default plus
|
|
1558
|
+
// the missing-pubkey throw.
|
|
1559
|
+
threw = null;
|
|
1560
|
+
try { b.middleware.apiEncrypt.client(); } catch (e) { threw = e; }
|
|
1561
|
+
check("apiEncrypt.client() with no args rejected", threw && threw.code === "CLIENT_INVALID_PUBKEY");
|
|
1562
|
+
threw = null;
|
|
1563
|
+
try { b.httpClient.encrypted(); } catch (e) { threw = e; }
|
|
1564
|
+
check("httpClient.encrypted() with no args rejected", threw && threw.code === "CLIENT_INVALID_PUBKEY");
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
async function testApiEncryptCreateOptionDefaultsTaken() {
|
|
1568
|
+
// The numeric-value branches of the maxDecryptedBytes / pruneIntervalMs
|
|
1569
|
+
// ternaries (operator overrides the framework default) plus a custom
|
|
1570
|
+
// keying value that builds cleanly.
|
|
1571
|
+
var keypair = _serverKeypair();
|
|
1572
|
+
var mw = b.middleware.apiEncrypt({
|
|
1573
|
+
keypair: keypair,
|
|
1574
|
+
audit: false,
|
|
1575
|
+
maxDecryptedBytes: b.constants.BYTES.mib(8),
|
|
1576
|
+
pruneIntervalMs: b.constants.TIME.seconds(45),
|
|
1577
|
+
});
|
|
1578
|
+
check("create() accepts numeric maxDecryptedBytes + pruneIntervalMs override",
|
|
1579
|
+
typeof mw === "function");
|
|
1580
|
+
mw.close();
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
async function testApiEncryptContentTypeHeaderVariants() {
|
|
1584
|
+
var keypair = _serverKeypair();
|
|
1585
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false });
|
|
1586
|
+
|
|
1587
|
+
// A request with NO Content-Type header at all — the content-type
|
|
1588
|
+
// filter cannot match, so the middleware treats it as a public route
|
|
1589
|
+
// and passes it straight through.
|
|
1590
|
+
var reqNoCt = _bodyReq("POST", {}, "");
|
|
1591
|
+
reqNoCt.body = { plain: true };
|
|
1592
|
+
var resNoCt = _mkRes();
|
|
1593
|
+
var passedNoCt = false;
|
|
1594
|
+
await mw(reqNoCt, resNoCt, function () { passedNoCt = true; });
|
|
1595
|
+
check("missing Content-Type header bypasses middleware", passedNoCt === true);
|
|
1596
|
+
check("missing Content-Type: body untouched", reqNoCt.body.plain === true);
|
|
1597
|
+
|
|
1598
|
+
// A request whose header key is capitalised "Content-Type" (not the
|
|
1599
|
+
// lowercased form) still matches — the middleware reads both cases.
|
|
1600
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair });
|
|
1601
|
+
var call = clientCtx.encryptRequest({ cased: "header" });
|
|
1602
|
+
var reqCap = _bodyReq("POST", { "Content-Type": "application/json" }, "");
|
|
1603
|
+
reqCap.body = call.body;
|
|
1604
|
+
var resCap = _mkRes();
|
|
1605
|
+
var finCap = _newFinish(resCap);
|
|
1606
|
+
var casedRan = false;
|
|
1607
|
+
await mw(reqCap, resCap, function () {
|
|
1608
|
+
casedRan = true;
|
|
1609
|
+
check("capitalised Content-Type: body decrypted", reqCap.body.cased === "header");
|
|
1610
|
+
resCap.json({ ok: true });
|
|
1611
|
+
});
|
|
1612
|
+
await finCap;
|
|
1613
|
+
check("capitalised Content-Type header matched the filter", casedRan === true);
|
|
1614
|
+
mw.close();
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
async function testApiEncryptBodyShapeGuards() {
|
|
1618
|
+
var keypair = _serverKeypair();
|
|
1619
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false });
|
|
1620
|
+
|
|
1621
|
+
// req.body is null on a JSON request — the object-shape guard refuses
|
|
1622
|
+
// it before any crypto with the "required" (not "rejected") body.
|
|
1623
|
+
var reqNull = _encReq(null, null);
|
|
1624
|
+
var resNull = _mkRes();
|
|
1625
|
+
var finNull = _newFinish(resNull);
|
|
1626
|
+
await mw(reqNull, resNull, function () { check("null body must not reach next", false); });
|
|
1627
|
+
await finNull;
|
|
1628
|
+
check("null body: 400 encrypted-payload-required",
|
|
1629
|
+
resNull._endedStatus === 400 && /encrypted-payload-required/.test(resNull._captured));
|
|
1630
|
+
|
|
1631
|
+
// req.body is a bare string (not an object) — same guard.
|
|
1632
|
+
var reqStr = _encReq(null, "raw-string-body");
|
|
1633
|
+
var resStr = _mkRes();
|
|
1634
|
+
var finStr = _newFinish(resStr);
|
|
1635
|
+
await mw(reqStr, resStr, function () { check("string body must not reach next", false); });
|
|
1636
|
+
await finStr;
|
|
1637
|
+
check("string body: 400 encrypted-payload-required",
|
|
1638
|
+
resStr._endedStatus === 400 && /encrypted-payload-required/.test(resStr._captured));
|
|
1639
|
+
|
|
1640
|
+
// _writeRejection's defensive guards: a res with headersSent already
|
|
1641
|
+
// true, and a res with no writeHead, both short-circuit without writing
|
|
1642
|
+
// (no throw, next never called). Not awaited on finish — these res
|
|
1643
|
+
// objects never emit it by design.
|
|
1644
|
+
var nextRan = false;
|
|
1645
|
+
var reqA = _encReq(null, null);
|
|
1646
|
+
await mw(reqA, { headersSent: true, writeHead: function () {}, end: function () {} },
|
|
1647
|
+
function () { nextRan = true; });
|
|
1648
|
+
check("_writeRejection: headersSent short-circuits, next not called", nextRan === false);
|
|
1649
|
+
nextRan = false;
|
|
1650
|
+
var reqB = _encReq(null, null);
|
|
1651
|
+
await mw(reqB, { headersSent: false }, function () { nextRan = true; });
|
|
1652
|
+
check("_writeRejection: missing writeHead short-circuits, next not called", nextRan === false);
|
|
1653
|
+
|
|
1654
|
+
// A rejected request that carries neither pathname nor url — the
|
|
1655
|
+
// failure-metadata path falls back to "/" for the audited path.
|
|
1656
|
+
var reqNoUrl = _encReq(null, null);
|
|
1657
|
+
delete reqNoUrl.url;
|
|
1658
|
+
var resNoUrl = _mkRes();
|
|
1659
|
+
var finNoUrl = _newFinish(resNoUrl);
|
|
1660
|
+
await mw(reqNoUrl, resNoUrl, function () { check("no-url null body must not reach next", false); });
|
|
1661
|
+
await finNoUrl;
|
|
1662
|
+
check("no-url null body: 400 encrypted-payload-required",
|
|
1663
|
+
resNoUrl._endedStatus === 400 && /encrypted-payload-required/.test(resNoUrl._captured));
|
|
1664
|
+
mw.close();
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
async function testApiEncryptPerRequestNoEkFallsThroughToShapeReject() {
|
|
1668
|
+
// A body with a valid _ct/_ts but NO _ek/_nonce in per-request mode
|
|
1669
|
+
// falls to the terminal shape-rejection else-branch (neither a
|
|
1670
|
+
// bootstrap nor a per-session subsequent request).
|
|
1671
|
+
var keypair = _serverKeypair();
|
|
1672
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false });
|
|
1673
|
+
var req = _encReq(null, {
|
|
1674
|
+
_ct: Buffer.from("no-ek-here").toString("base64"),
|
|
1675
|
+
_ts: Date.now(),
|
|
1676
|
+
});
|
|
1677
|
+
var res = _mkRes();
|
|
1678
|
+
var fin = _newFinish(res);
|
|
1679
|
+
await mw(req, res, function () { check("no-_ek body must not reach next", false); });
|
|
1680
|
+
await fin;
|
|
1681
|
+
check("no-_ek per-request body: 400 encrypted-payload-required",
|
|
1682
|
+
res._endedStatus === 400 && /encrypted-payload-required/.test(res._captured));
|
|
1683
|
+
mw.close();
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
async function testApiEncryptNonceStoreErrorOnBootstrap() {
|
|
1687
|
+
// The bootstrap nonce claim throwing (store outage) surfaces a 500
|
|
1688
|
+
// nonce-store-unavailable, distinct from the 400 replay refusal.
|
|
1689
|
+
var keypair = _serverKeypair();
|
|
1690
|
+
var errStore = {
|
|
1691
|
+
checkAndInsert: function () { return Promise.reject(new Error("nonce backend down")); },
|
|
1692
|
+
purgeExpired: function () { return Promise.resolve(0); },
|
|
1693
|
+
close: function () {},
|
|
1694
|
+
};
|
|
1695
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false, nonceStore: errStore });
|
|
1696
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair });
|
|
1697
|
+
var call = clientCtx.encryptRequest({ x: 1 });
|
|
1698
|
+
var req = _encReq(null, call.body);
|
|
1699
|
+
var res = _mkRes();
|
|
1700
|
+
var fin = _newFinish(res);
|
|
1701
|
+
await mw(req, res, function () { check("nonce-store outage must not reach next", false); });
|
|
1702
|
+
await fin;
|
|
1703
|
+
check("nonce-store outage: 500", res._endedStatus === 500);
|
|
1704
|
+
check("nonce-store outage: nonce-store-unavailable body",
|
|
1705
|
+
/nonce-store-unavailable/.test(res._captured));
|
|
1706
|
+
mw.close();
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
async function testApiEncryptPruneFailureIsSwallowed() {
|
|
1710
|
+
// A successful request triggers _maybePrune (lastPruneAt starts at 0);
|
|
1711
|
+
// a rejecting purgeExpired must be caught and logged, never crash the
|
|
1712
|
+
// request that already succeeded.
|
|
1713
|
+
var keypair = _serverKeypair();
|
|
1714
|
+
var pruneCalls = 0;
|
|
1715
|
+
var flakyStore = {
|
|
1716
|
+
checkAndInsert: function () { return Promise.resolve(true); },
|
|
1717
|
+
purgeExpired: function () { pruneCalls++; return Promise.reject(new Error("prune backend down")); },
|
|
1718
|
+
close: function () {},
|
|
1719
|
+
};
|
|
1720
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false, nonceStore: flakyStore });
|
|
1721
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair });
|
|
1722
|
+
var call = clientCtx.encryptRequest({ x: 1 });
|
|
1723
|
+
var req = _encReq(null, call.body);
|
|
1724
|
+
var res = _mkRes();
|
|
1725
|
+
var fin = _newFinish(res);
|
|
1726
|
+
var reached = false;
|
|
1727
|
+
await mw(req, res, function () { reached = true; res.json({ ok: true }); });
|
|
1728
|
+
await fin;
|
|
1729
|
+
// Let the rejected purgeExpired().catch microtask settle.
|
|
1730
|
+
await helpers.waitUntil(function () { return pruneCalls >= 1; },
|
|
1731
|
+
{ timeoutMs: 5000, label: "prune-failure: purgeExpired invoked" });
|
|
1732
|
+
check("prune failure: request still succeeded (200)", reached && res._endedStatus === 200);
|
|
1733
|
+
check("prune failure: purgeExpired was attempted", pruneCalls === 1);
|
|
1734
|
+
mw.close();
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
async function testApiEncryptResponseEncodeFallbackAndFailure() {
|
|
1738
|
+
var keypair = _serverKeypair();
|
|
1739
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false });
|
|
1740
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair });
|
|
1741
|
+
|
|
1742
|
+
// Fallback: a res that never had res.json installed. The wrapped
|
|
1743
|
+
// res.json falls back to writeHead + end directly, still emitting the
|
|
1744
|
+
// encrypted envelope.
|
|
1745
|
+
var call1 = clientCtx.encryptRequest({ x: 1 });
|
|
1746
|
+
var rawRes = helpers._bodyRes(); // NO .json method
|
|
1747
|
+
var req1 = _encReq(null, call1.body);
|
|
1748
|
+
var fin1 = _newFinish(rawRes);
|
|
1749
|
+
await mw(req1, rawRes, function () { rawRes.json({ fell: "back" }); });
|
|
1750
|
+
await fin1;
|
|
1751
|
+
check("res.json fallback: 200 written directly", rawRes._endedStatus === 200);
|
|
1752
|
+
var env = JSON.parse(rawRes._captured);
|
|
1753
|
+
check("res.json fallback: emitted an encrypted envelope", typeof env._ct === "string");
|
|
1754
|
+
var plain1 = call1.decryptResponse(env);
|
|
1755
|
+
check("res.json fallback: envelope decrypts", plain1.fell === "back");
|
|
1756
|
+
|
|
1757
|
+
// Failure: encrypting an un-serialisable (circular) response body throws
|
|
1758
|
+
// inside the wrap; the middleware writes a 500 response-encryption-failed
|
|
1759
|
+
// rather than hanging the request.
|
|
1760
|
+
var call2 = clientCtx.encryptRequest({ x: 2 });
|
|
1761
|
+
var req2 = _encReq(null, call2.body);
|
|
1762
|
+
var res2 = _mkRes();
|
|
1763
|
+
var fin2 = _newFinish(res2);
|
|
1764
|
+
await mw(req2, res2, function () {
|
|
1765
|
+
var circular = {};
|
|
1766
|
+
circular.self = circular;
|
|
1767
|
+
res2.json(circular);
|
|
1768
|
+
});
|
|
1769
|
+
await fin2;
|
|
1770
|
+
check("res.json encrypt failure: 500", res2._endedStatus === 500);
|
|
1771
|
+
check("res.json encrypt failure: response-encryption-failed body",
|
|
1772
|
+
/response-encryption-failed/.test(res2._captured));
|
|
1773
|
+
mw.close();
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
async function testApiEncryptPerSessionBootstrapInvalidSidAndCtr() {
|
|
1777
|
+
var keypair = _serverKeypair();
|
|
1778
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false, keying: "per-session" });
|
|
1779
|
+
|
|
1780
|
+
// Bootstrap envelope (valid _ek/_nonce) but a malformed _sid — refused
|
|
1781
|
+
// after the KEM decrypt, before any session row is written.
|
|
1782
|
+
var c1 = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
1783
|
+
var boot1 = c1.encryptRequest({ n: 1 });
|
|
1784
|
+
boot1.body._sid = "not-a-uuid";
|
|
1785
|
+
var req1 = _encReq(null, boot1.body);
|
|
1786
|
+
var res1 = _mkRes();
|
|
1787
|
+
var fin1 = _newFinish(res1);
|
|
1788
|
+
await mw(req1, res1, function () { check("bad-sid bootstrap must not reach next", false); });
|
|
1789
|
+
await fin1;
|
|
1790
|
+
check("per-session bootstrap bad _sid: 400 required",
|
|
1791
|
+
res1._endedStatus === 400 && /encrypted-payload-required/.test(res1._captured));
|
|
1792
|
+
|
|
1793
|
+
// Bootstrap with a valid _sid but a negative _ctr — refused at the
|
|
1794
|
+
// non-negative-int guard.
|
|
1795
|
+
var c2 = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
1796
|
+
var boot2 = c2.encryptRequest({ n: 2 });
|
|
1797
|
+
boot2.body._ctr = -1;
|
|
1798
|
+
var req2 = _encReq(null, boot2.body);
|
|
1799
|
+
var res2 = _mkRes();
|
|
1800
|
+
var fin2 = _newFinish(res2);
|
|
1801
|
+
await mw(req2, res2, function () { check("bad-ctr bootstrap must not reach next", false); });
|
|
1802
|
+
await fin2;
|
|
1803
|
+
check("per-session bootstrap negative _ctr: 400 required",
|
|
1804
|
+
res2._endedStatus === 400 && /encrypted-payload-required/.test(res2._captured));
|
|
1805
|
+
mw.close();
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
async function testApiEncryptPerSessionBootstrapStoreSetError() {
|
|
1809
|
+
// The session-row write failing during bootstrap surfaces a 500
|
|
1810
|
+
// session-store-unavailable (distinct from the nonce path).
|
|
1811
|
+
var keypair = _serverKeypair();
|
|
1812
|
+
var failStore = {
|
|
1813
|
+
get: async function () { return null; },
|
|
1814
|
+
set: async function () { throw new Error("session backend write rejected"); },
|
|
1815
|
+
delete: async function () {},
|
|
1816
|
+
};
|
|
1817
|
+
var mw = b.middleware.apiEncrypt({
|
|
1818
|
+
keypair: keypair, audit: false, keying: "per-session", sessionStore: failStore,
|
|
1819
|
+
});
|
|
1820
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
1821
|
+
var boot = clientCtx.encryptRequest({ n: 1 });
|
|
1822
|
+
var req = _encReq(null, boot.body);
|
|
1823
|
+
var res = _mkRes();
|
|
1824
|
+
var fin = _newFinish(res);
|
|
1825
|
+
await mw(req, res, function () { check("session write failure must not reach next", false); });
|
|
1826
|
+
await fin;
|
|
1827
|
+
check("per-session bootstrap set() failure: 500", res._endedStatus === 500);
|
|
1828
|
+
check("per-session bootstrap set() failure: session-store-unavailable",
|
|
1829
|
+
/session-store-unavailable/.test(res._captured));
|
|
1830
|
+
mw.close();
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
async function testApiEncryptPerSessionSubsequentInvalidSidAndCtr() {
|
|
1834
|
+
var keypair = _serverKeypair();
|
|
1835
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false, keying: "per-session" });
|
|
1836
|
+
|
|
1837
|
+
// Subsequent-shape request (no _ek/_nonce) with a string _sid that is
|
|
1838
|
+
// not a valid UUID.
|
|
1839
|
+
var reqBadSid = _encReq(null, {
|
|
1840
|
+
_ct: Buffer.from("x").toString("base64"),
|
|
1841
|
+
_ts: Date.now(),
|
|
1842
|
+
_sid: "totally-bogus-sid",
|
|
1843
|
+
_ctr: 5,
|
|
1844
|
+
});
|
|
1845
|
+
var resBadSid = _mkRes();
|
|
1846
|
+
var finBadSid = _newFinish(resBadSid);
|
|
1847
|
+
await mw(reqBadSid, resBadSid, function () { check("bad-sid subsequent must not reach next", false); });
|
|
1848
|
+
await finBadSid;
|
|
1849
|
+
check("per-session subsequent bad _sid: 400 required",
|
|
1850
|
+
resBadSid._endedStatus === 400 && /encrypted-payload-required/.test(resBadSid._captured));
|
|
1851
|
+
|
|
1852
|
+
// Subsequent-shape request with a valid UUID _sid but a negative _ctr
|
|
1853
|
+
// (a number, so it enters the subsequent branch, then fails the guard).
|
|
1854
|
+
var reqBadCtr = _encReq(null, {
|
|
1855
|
+
_ct: Buffer.from("x").toString("base64"),
|
|
1856
|
+
_ts: Date.now(),
|
|
1857
|
+
_sid: "12345678-1234-4234-8234-123456789012",
|
|
1858
|
+
_ctr: -3,
|
|
1859
|
+
});
|
|
1860
|
+
var resBadCtr = _mkRes();
|
|
1861
|
+
var finBadCtr = _newFinish(resBadCtr);
|
|
1862
|
+
await mw(reqBadCtr, resBadCtr, function () { check("bad-ctr subsequent must not reach next", false); });
|
|
1863
|
+
await finBadCtr;
|
|
1864
|
+
check("per-session subsequent negative _ctr: 400 required",
|
|
1865
|
+
resBadCtr._endedStatus === 400 && /encrypted-payload-required/.test(resBadCtr._captured));
|
|
1866
|
+
mw.close();
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
async function testApiEncryptPerSessionSubsequentGetError() {
|
|
1870
|
+
// A store whose get() throws on the subsequent request surfaces a 500
|
|
1871
|
+
// session-store-unavailable. Bootstrap uses set() (which succeeds), so
|
|
1872
|
+
// only the follow-up read trips the outage.
|
|
1873
|
+
var keypair = _serverKeypair();
|
|
1874
|
+
var rows = new Map();
|
|
1875
|
+
var getThrows = false;
|
|
1876
|
+
var partialStore = {
|
|
1877
|
+
get: async function (sid) { if (getThrows) throw new Error("session read outage"); return rows.get(sid) || null; },
|
|
1878
|
+
set: async function (sid, row) { rows.set(sid, row); },
|
|
1879
|
+
delete: async function (sid) { rows.delete(sid); },
|
|
1880
|
+
};
|
|
1881
|
+
var mw = b.middleware.apiEncrypt({
|
|
1882
|
+
keypair: keypair, audit: false, keying: "per-session", sessionStore: partialStore,
|
|
1883
|
+
});
|
|
1884
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
1885
|
+
|
|
1886
|
+
var boot = clientCtx.encryptRequest({ n: 1 });
|
|
1887
|
+
var req1 = _encReq(null, boot.body);
|
|
1888
|
+
var res1 = _mkRes();
|
|
1889
|
+
var fin1 = _newFinish(res1);
|
|
1890
|
+
await mw(req1, res1, function () { res1.json({ ok: 1 }); });
|
|
1891
|
+
await fin1;
|
|
1892
|
+
check("get-outage: bootstrap 200", res1._endedStatus === 200);
|
|
1893
|
+
|
|
1894
|
+
getThrows = true;
|
|
1895
|
+
var second = clientCtx.encryptRequest({ n: 2 });
|
|
1896
|
+
var req2 = _encReq(null, second.body);
|
|
1897
|
+
var res2 = _mkRes();
|
|
1898
|
+
var fin2 = _newFinish(res2);
|
|
1899
|
+
await mw(req2, res2, function () { check("get outage must not reach next", false); });
|
|
1900
|
+
await fin2;
|
|
1901
|
+
check("per-session subsequent get() outage: 500", res2._endedStatus === 500);
|
|
1902
|
+
check("per-session subsequent get() outage: session-store-unavailable",
|
|
1903
|
+
/session-store-unavailable/.test(res2._captured));
|
|
1904
|
+
mw.close();
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
async function testApiEncryptPerSessionKeyCoercionVariants() {
|
|
1908
|
+
var keypair = _serverKeypair();
|
|
1909
|
+
|
|
1910
|
+
// Variant A: store hands back the session key as a base64 STRING.
|
|
1911
|
+
await _runCoercionVariant(keypair,
|
|
1912
|
+
function (k) { return Buffer.isBuffer(k) ? k.toString("base64") : k; },
|
|
1913
|
+
200, "base64-string session key coerced back to Buffer");
|
|
1914
|
+
|
|
1915
|
+
// Variant B: store hands back a plain Uint8Array (not a Buffer).
|
|
1916
|
+
await _runCoercionVariant(keypair,
|
|
1917
|
+
function (k) { return Buffer.isBuffer(k) ? Uint8Array.from(k) : k; },
|
|
1918
|
+
200, "Uint8Array session key coerced back to Buffer");
|
|
1919
|
+
|
|
1920
|
+
// Variant C: store hands back a garbage value (a number) — the key
|
|
1921
|
+
// fails the 32-byte Buffer validity gate → 500 session-store-unavailable.
|
|
1922
|
+
await _runCoercionVariant(keypair,
|
|
1923
|
+
function () { return 42; },
|
|
1924
|
+
500, "un-coercible session key rejected");
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
async function _runCoercionVariant(keypair, transform, expectedStatus, label) {
|
|
1928
|
+
var store = _coercingSessionStore(transform);
|
|
1929
|
+
var mw = b.middleware.apiEncrypt({
|
|
1930
|
+
keypair: keypair, audit: false, keying: "per-session", sessionStore: store,
|
|
1931
|
+
});
|
|
1932
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
1933
|
+
|
|
1934
|
+
var boot = clientCtx.encryptRequest({ n: 1 });
|
|
1935
|
+
var req1 = _encReq(null, boot.body);
|
|
1936
|
+
var res1 = _mkRes();
|
|
1937
|
+
var fin1 = _newFinish(res1);
|
|
1938
|
+
await mw(req1, res1, function () { res1.json({ ok: 1 }); });
|
|
1939
|
+
await fin1;
|
|
1940
|
+
check("coercion(" + label + "): bootstrap 200", res1._endedStatus === 200);
|
|
1941
|
+
|
|
1942
|
+
var second = clientCtx.encryptRequest({ n: 2 });
|
|
1943
|
+
var req2 = _encReq(null, second.body);
|
|
1944
|
+
var res2 = _mkRes();
|
|
1945
|
+
var fin2 = _newFinish(res2);
|
|
1946
|
+
var reached = false;
|
|
1947
|
+
await mw(req2, res2, function () { reached = true; res2.json({ ok: 2 }); });
|
|
1948
|
+
await fin2;
|
|
1949
|
+
check("coercion(" + label + "): subsequent status " + expectedStatus,
|
|
1950
|
+
res2._endedStatus === expectedStatus);
|
|
1951
|
+
if (expectedStatus === 200) {
|
|
1952
|
+
check("coercion(" + label + "): reached handler + decrypts",
|
|
1953
|
+
reached && second.decryptResponse(JSON.parse(res2._captured)).ok === 2);
|
|
1954
|
+
} else {
|
|
1955
|
+
check("coercion(" + label + "): did not reach handler", reached === false);
|
|
1956
|
+
check("coercion(" + label + "): session-store-unavailable",
|
|
1957
|
+
/session-store-unavailable/.test(res2._captured));
|
|
1958
|
+
}
|
|
1959
|
+
mw.close();
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
async function testApiEncryptPerSessionExpiredRowEncryptedRejection() {
|
|
1963
|
+
// With a store that does NOT self-evict past-TTL rows (a real cluster
|
|
1964
|
+
// store returns the row and lets the server decide), the middleware
|
|
1965
|
+
// resolves the session, keys the channel, THEN refuses on expiry — so
|
|
1966
|
+
// the rejection rides the session envelope (encrypted), not cleartext.
|
|
1967
|
+
var keypair = _serverKeypair();
|
|
1968
|
+
var store = _clusterStyleSessionStore();
|
|
1969
|
+
var mw = b.middleware.apiEncrypt({
|
|
1970
|
+
keypair: keypair, audit: false, keying: "per-session",
|
|
1971
|
+
sessionStore: store, sessionTtlMs: 1,
|
|
1972
|
+
});
|
|
1973
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
1974
|
+
|
|
1975
|
+
var boot = clientCtx.encryptRequest({ n: 1 });
|
|
1976
|
+
var req1 = _encReq(null, boot.body);
|
|
1977
|
+
var res1 = _mkRes();
|
|
1978
|
+
var fin1 = _newFinish(res1);
|
|
1979
|
+
await mw(req1, res1, function () { res1.json({ ok: 1 }); });
|
|
1980
|
+
await fin1;
|
|
1981
|
+
check("expired-row: bootstrap 200", res1._endedStatus === 200);
|
|
1982
|
+
|
|
1983
|
+
await helpers.passiveObserve(20, "apiEncrypt: cluster-style row past sessionTtlMs=1");
|
|
1984
|
+
|
|
1985
|
+
var second = clientCtx.encryptRequest({ n: 2 });
|
|
1986
|
+
var req2 = _encReq(null, second.body);
|
|
1987
|
+
var res2 = _mkRes();
|
|
1988
|
+
var fin2 = _newFinish(res2);
|
|
1989
|
+
await mw(req2, res2, function () { check("expired session must not reach next", false); });
|
|
1990
|
+
await fin2;
|
|
1991
|
+
check("expired-row: 401", res2._endedStatus === 401);
|
|
1992
|
+
check("expired-row: rejection is an encrypted envelope (reason not leaked cleartext)",
|
|
1993
|
+
_isEncryptedEnvelope(res2._captured));
|
|
1994
|
+
mw.close();
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// A cluster-shaped store (returns rows regardless of TTL) whose delete()
|
|
1998
|
+
// always throws — models a store whose eviction write fails. The
|
|
1999
|
+
// middleware's expiry / rotation evictions are best-effort, so a throwing
|
|
2000
|
+
// delete must not change the refusal outcome.
|
|
2001
|
+
function _deleteThrowingSessionStore() {
|
|
2002
|
+
var rows = Object.create(null);
|
|
2003
|
+
return {
|
|
2004
|
+
get: async function (sid) {
|
|
2005
|
+
await Promise.resolve();
|
|
2006
|
+
var raw = rows[sid];
|
|
2007
|
+
return raw === undefined ? null : JSON.parse(raw);
|
|
2008
|
+
},
|
|
2009
|
+
set: async function (sid, row) { await Promise.resolve(); rows[sid] = JSON.stringify(row); },
|
|
2010
|
+
delete: async function () { await Promise.resolve(); throw new Error("delete backend down"); },
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
async function testApiEncryptPerSessionEvictionDeleteBestEffort() {
|
|
2015
|
+
var keypair = _serverKeypair();
|
|
2016
|
+
|
|
2017
|
+
// Expiry: the past-TTL row's best-effort delete throws; the request is
|
|
2018
|
+
// still refused 401 (encrypted envelope), the throw swallowed.
|
|
2019
|
+
var expStore = _deleteThrowingSessionStore();
|
|
2020
|
+
var mwExp = b.middleware.apiEncrypt({
|
|
2021
|
+
keypair: keypair, audit: false, keying: "per-session",
|
|
2022
|
+
sessionStore: expStore, sessionTtlMs: 1,
|
|
2023
|
+
});
|
|
2024
|
+
var cExp = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
2025
|
+
var bootE = cExp.encryptRequest({ n: 1 });
|
|
2026
|
+
var reqE1 = _encReq(null, bootE.body);
|
|
2027
|
+
var resE1 = _mkRes();
|
|
2028
|
+
var finE1 = _newFinish(resE1);
|
|
2029
|
+
await mwExp(reqE1, resE1, function () { resE1.json({ ok: 1 }); });
|
|
2030
|
+
await finE1;
|
|
2031
|
+
await helpers.passiveObserve(20, "apiEncrypt: delete-throwing store row past sessionTtlMs=1");
|
|
2032
|
+
var secondE = cExp.encryptRequest({ n: 2 });
|
|
2033
|
+
var reqE2 = _encReq(null, secondE.body);
|
|
2034
|
+
var resE2 = _mkRes();
|
|
2035
|
+
var finE2 = _newFinish(resE2);
|
|
2036
|
+
await mwExp(reqE2, resE2, function () { check("expiry w/ failing delete must not reach next", false); });
|
|
2037
|
+
await finE2;
|
|
2038
|
+
check("eviction-delete: expiry still 401 despite failing delete",
|
|
2039
|
+
resE2._endedStatus === 401 && _isEncryptedEnvelope(resE2._captured));
|
|
2040
|
+
mwExp.close();
|
|
2041
|
+
|
|
2042
|
+
// Rotation: the max-responses eviction's best-effort delete throws; the
|
|
2043
|
+
// request is still refused 401 rotation-required (encrypted envelope).
|
|
2044
|
+
var rotStore = _deleteThrowingSessionStore();
|
|
2045
|
+
var mwRot = b.middleware.apiEncrypt({
|
|
2046
|
+
keypair: keypair, audit: false, keying: "per-session",
|
|
2047
|
+
sessionStore: rotStore, sessionMaxResponses: 1,
|
|
2048
|
+
});
|
|
2049
|
+
var cRot = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
2050
|
+
var bootR = cRot.encryptRequest({ n: 1 });
|
|
2051
|
+
var reqR1 = _encReq(null, bootR.body);
|
|
2052
|
+
var resR1 = _mkRes();
|
|
2053
|
+
var finR1 = _newFinish(resR1);
|
|
2054
|
+
await mwRot(reqR1, resR1, function () { resR1.json({ ok: 1 }); });
|
|
2055
|
+
await finR1;
|
|
2056
|
+
var secondR = cRot.encryptRequest({ n: 2 });
|
|
2057
|
+
var reqR2 = _encReq(null, secondR.body);
|
|
2058
|
+
var resR2 = _mkRes();
|
|
2059
|
+
var finR2 = _newFinish(resR2);
|
|
2060
|
+
await mwRot(reqR2, resR2, function () { check("rotation w/ failing delete must not reach next", false); });
|
|
2061
|
+
await finR2;
|
|
2062
|
+
check("eviction-delete: rotation still 401 despite failing delete",
|
|
2063
|
+
resR2._endedStatus === 401 && _isEncryptedEnvelope(resR2._captured));
|
|
2064
|
+
mwRot.close();
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
async function testApiEncryptPerSessionCtrClaimStoreError() {
|
|
2068
|
+
// The atomic (sid, ctr) claim throwing on the subsequent request
|
|
2069
|
+
// surfaces a 500 nonce-store-unavailable — the claim uses the same
|
|
2070
|
+
// nonceStore as the bootstrap, so only the "ctr:" key path fails here.
|
|
2071
|
+
var keypair = _serverKeypair();
|
|
2072
|
+
var inner = b.nonceStore.create({ backend: "memory" });
|
|
2073
|
+
var claimStore = {
|
|
2074
|
+
checkAndInsert: function (key, expireAt) {
|
|
2075
|
+
if (/^ctr:/.test(key)) return Promise.reject(new Error("claim backend down"));
|
|
2076
|
+
return inner.checkAndInsert(key, expireAt);
|
|
2077
|
+
},
|
|
2078
|
+
purgeExpired: function () { return inner.purgeExpired(); },
|
|
2079
|
+
close: function () { return inner.close(); },
|
|
2080
|
+
};
|
|
2081
|
+
var mw = b.middleware.apiEncrypt({
|
|
2082
|
+
keypair: keypair, audit: false, keying: "per-session", nonceStore: claimStore,
|
|
2083
|
+
});
|
|
2084
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
2085
|
+
|
|
2086
|
+
var boot = clientCtx.encryptRequest({ n: 1 });
|
|
2087
|
+
var req1 = _encReq(null, boot.body);
|
|
2088
|
+
var res1 = _mkRes();
|
|
2089
|
+
var fin1 = _newFinish(res1);
|
|
2090
|
+
await mw(req1, res1, function () { res1.json({ ok: 1 }); });
|
|
2091
|
+
await fin1;
|
|
2092
|
+
check("ctr-claim-outage: bootstrap 200", res1._endedStatus === 200);
|
|
2093
|
+
|
|
2094
|
+
var second = clientCtx.encryptRequest({ n: 2 });
|
|
2095
|
+
var req2 = _encReq(null, second.body);
|
|
2096
|
+
var res2 = _mkRes();
|
|
2097
|
+
var fin2 = _newFinish(res2);
|
|
2098
|
+
await mw(req2, res2, function () { check("ctr-claim outage must not reach next", false); });
|
|
2099
|
+
await fin2;
|
|
2100
|
+
check("ctr-claim-outage: 500", res2._endedStatus === 500);
|
|
2101
|
+
// The claim gate fires AFTER the session channel is keyed, so the
|
|
2102
|
+
// rejection rides the session envelope (encrypted) — the store-outage
|
|
2103
|
+
// reason never travels in cleartext. Decrypting it recovers the reason.
|
|
2104
|
+
check("ctr-claim-outage: rejection is an encrypted envelope", _isEncryptedEnvelope(res2._captured));
|
|
2105
|
+
var decoded = second.decryptResponse(JSON.parse(res2._captured));
|
|
2106
|
+
check("ctr-claim-outage: decrypted reason is nonce-store-unavailable",
|
|
2107
|
+
decoded && decoded.error === "nonce-store-unavailable");
|
|
2108
|
+
mw.close();
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
async function testApiEncryptSessionCreatedAuditEmitted() {
|
|
2112
|
+
// Per-session bootstrap with audit ON drives the session.created event
|
|
2113
|
+
// through _emitSessionAudit's safeEmit path (the branch the audit:false
|
|
2114
|
+
// suite skips). The audit is drop-silent best-effort, so the observable
|
|
2115
|
+
// contract is that emitting it never disturbs the request: the bootstrap
|
|
2116
|
+
// still returns 200 and the response still decrypts.
|
|
2117
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-ae-"));
|
|
2118
|
+
try {
|
|
2119
|
+
await setupTestDb(tmpDir);
|
|
2120
|
+
var keypair = _serverKeypair();
|
|
2121
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, keying: "per-session" }); // audit defaults ON
|
|
2122
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
2123
|
+
var boot = clientCtx.encryptRequest({ n: 1 });
|
|
2124
|
+
var req = _encReq(null, boot.body);
|
|
2125
|
+
var res = _mkRes();
|
|
2126
|
+
var fin = _newFinish(res);
|
|
2127
|
+
await mw(req, res, function () { res.json({ ok: 1 }); });
|
|
2128
|
+
await fin;
|
|
2129
|
+
check("session-audit: audited bootstrap still returns 200", res._endedStatus === 200);
|
|
2130
|
+
var plain = boot.decryptResponse(JSON.parse(res._captured));
|
|
2131
|
+
check("session-audit: audited bootstrap response still decrypts", plain.ok === 1);
|
|
2132
|
+
await b.audit.flush();
|
|
2133
|
+
mw.close();
|
|
2134
|
+
} finally {
|
|
2135
|
+
await teardownTestDb(tmpDir);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
async function testApiEncryptPublishPublicKeyNoJsonRes() {
|
|
2140
|
+
// publishPublicKey's raw-write fallback when the res has no res.json.
|
|
2141
|
+
var keypair = _serverKeypair();
|
|
2142
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false });
|
|
2143
|
+
var handler = mw.publishPublicKey();
|
|
2144
|
+
var res = helpers._bodyRes(); // NO .json method
|
|
2145
|
+
handler(_bodyReq("GET", {}, ""), res);
|
|
2146
|
+
var body = JSON.parse(res._captured);
|
|
2147
|
+
check("publishPublicKey no-json res: wrote 200", res._endedStatus === 200);
|
|
2148
|
+
check("publishPublicKey no-json res: publicKey present", body.publicKey === keypair.publicKey);
|
|
2149
|
+
mw.close();
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
async function testApiEncryptClientRejectsBadKeying() {
|
|
2153
|
+
var keypair = _serverKeypair();
|
|
2154
|
+
var threw = null;
|
|
2155
|
+
try { b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "hourly" }); }
|
|
2156
|
+
catch (e) { threw = e; }
|
|
2157
|
+
check("client rejects bogus keying value", threw && threw.code === "CLIENT_BAD_OPT");
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
async function testApiEncryptClientResponseShapeSidCtrGuards() {
|
|
2161
|
+
var keypair = _serverKeypair();
|
|
2162
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
2163
|
+
// Open a session so perSessionSid is populated.
|
|
2164
|
+
var boot = clientCtx.encryptRequest({ n: 1 });
|
|
2165
|
+
var sid = boot.body._sid;
|
|
2166
|
+
|
|
2167
|
+
var threw = null;
|
|
2168
|
+
try { boot.decryptResponse({ _sid: sid, _ctr: 1 }); } catch (e) { threw = e; } // no _ct
|
|
2169
|
+
check("client per-session response missing _ct: CLIENT_RESPONSE_SHAPE",
|
|
2170
|
+
threw && threw.code === "CLIENT_RESPONSE_SHAPE");
|
|
2171
|
+
|
|
2172
|
+
threw = null;
|
|
2173
|
+
try {
|
|
2174
|
+
boot.decryptResponse({ _ct: Buffer.from("x").toString("base64"),
|
|
2175
|
+
_sid: "00000000-0000-4000-8000-000000000000", _ctr: 1 });
|
|
2176
|
+
} catch (e) { threw = e; }
|
|
2177
|
+
check("client per-session response wrong _sid: CLIENT_RESPONSE_SID",
|
|
2178
|
+
threw && threw.code === "CLIENT_RESPONSE_SID");
|
|
2179
|
+
|
|
2180
|
+
threw = null;
|
|
2181
|
+
try {
|
|
2182
|
+
boot.decryptResponse({ _ct: Buffer.from("x").toString("base64"), _sid: sid, _ctr: "nope" });
|
|
2183
|
+
} catch (e) { threw = e; }
|
|
2184
|
+
check("client per-session response non-numeric _ctr: CLIENT_RESPONSE_REPLAY",
|
|
2185
|
+
threw && threw.code === "CLIENT_RESPONSE_REPLAY");
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
async function testApiEncryptPerRequestClientRejectsTamperedResponse() {
|
|
2189
|
+
// Per-request client: a response whose _ct is well-formed base64 but
|
|
2190
|
+
// fails authenticated decryption is refused typed.
|
|
2191
|
+
var keypair = _serverKeypair();
|
|
2192
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair });
|
|
2193
|
+
clientCtx.encryptRequest({ x: 1 }); // establishes the per-request session key
|
|
2194
|
+
var call = clientCtx.encryptRequest({ x: 2 });
|
|
2195
|
+
var threw = null;
|
|
2196
|
+
try {
|
|
2197
|
+
call.decryptResponse({ _ct: b.crypto.generateBytes(64).toString("base64") });
|
|
2198
|
+
} catch (e) { threw = e; }
|
|
2199
|
+
check("per-request client: garbage response ciphertext refused typed",
|
|
2200
|
+
threw && threw.code === "CLIENT_RESPONSE_TAMPERED");
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
async function testApiEncryptClientNullPayloadDefaults() {
|
|
2204
|
+
// encryptRequest() with no argument defaults the payload to null in
|
|
2205
|
+
// both keying modes (the `payload === undefined` branch).
|
|
2206
|
+
var keypair = _serverKeypair();
|
|
2207
|
+
var perReq = b.middleware.apiEncrypt.client({ pubkey: keypair });
|
|
2208
|
+
var r1 = perReq.encryptRequest();
|
|
2209
|
+
check("per-request encryptRequest() with no payload builds a body", typeof r1.body._ct === "string");
|
|
2210
|
+
|
|
2211
|
+
var perSess = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
2212
|
+
var r2 = perSess.encryptRequest();
|
|
2213
|
+
check("per-session encryptRequest() with no payload builds a bootstrap body",
|
|
2214
|
+
typeof r2.body._ek === "string" && r2.body._ctr === 1);
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
async function testApiEncryptHttpClientOptionValidation() {
|
|
2218
|
+
var keypair = _serverKeypair();
|
|
2219
|
+
|
|
2220
|
+
// Bad responseMode at create time.
|
|
2221
|
+
var threw = null;
|
|
2222
|
+
try { b.httpClient.encrypted({ pubkey: keypair, responseMode: "swallow" }); }
|
|
2223
|
+
catch (e) { threw = e; }
|
|
2224
|
+
check("httpClient.encrypted: bad responseMode default rejected", threw && threw.code === "CLIENT_BAD_OPT");
|
|
2225
|
+
|
|
2226
|
+
// maxDecryptedBytes + keying overrides build cleanly (the non-default
|
|
2227
|
+
// ternary branches).
|
|
2228
|
+
var enc = b.httpClient.encrypted({
|
|
2229
|
+
pubkey: keypair, baseUrl: "http://127.0.0.1:1",
|
|
2230
|
+
maxDecryptedBytes: b.constants.BYTES.mib(2), keying: "per-request",
|
|
2231
|
+
});
|
|
2232
|
+
check("httpClient.encrypted: numeric maxDecryptedBytes + keying build", typeof enc.request === "function");
|
|
2233
|
+
|
|
2234
|
+
// Bad per-call responseMode override is rejected before any network I/O.
|
|
2235
|
+
threw = null;
|
|
2236
|
+
try { await enc.request({ url: "http://127.0.0.1:1/x", responseMode: "swallow" }); }
|
|
2237
|
+
catch (e) { threw = e; }
|
|
2238
|
+
check("httpClient.encrypted.request: bad per-call responseMode rejected",
|
|
2239
|
+
threw && threw.code === "CLIENT_BAD_OPT");
|
|
2240
|
+
|
|
2241
|
+
// request() with no args at all resolves through `reqOpts || {}` into the
|
|
2242
|
+
// url-required throw.
|
|
2243
|
+
threw = null;
|
|
2244
|
+
try { await enc.request(); } catch (e) { threw = e; }
|
|
2245
|
+
check("httpClient.encrypted.request(): no args rejected (url required)",
|
|
2246
|
+
threw && threw.code === "CLIENT_INVALID_URL");
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
async function testApiEncryptHttpClientUrlFormsAndBodyless() {
|
|
2250
|
+
// Live round trip exercising _resolveUrl's absolute-url branch and its
|
|
2251
|
+
// leading-slash normalisation, plus a body-less request (the `body
|
|
2252
|
+
// !== undefined ? body : null` default).
|
|
2253
|
+
var http = require("http");
|
|
2254
|
+
var keypair = _serverKeypair();
|
|
2255
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false });
|
|
2256
|
+
var server = http.createServer(function (req, res) {
|
|
2257
|
+
res.json = function (data) {
|
|
2258
|
+
res.writeHead(res.statusCode || 200, { "Content-Type": "application/json" });
|
|
2259
|
+
res.end(JSON.stringify(data));
|
|
2260
|
+
};
|
|
2261
|
+
req.pathname = req.url.split("?")[0];
|
|
2262
|
+
var chunks = [];
|
|
2263
|
+
req.on("data", function (c) { chunks.push(c); });
|
|
2264
|
+
req.on("end", function () {
|
|
2265
|
+
try { req.body = JSON.parse(Buffer.concat(chunks).toString("utf8")); }
|
|
2266
|
+
catch (_e) { req.body = null; }
|
|
2267
|
+
mw(req, res, function () { res.json({ echo: req.body, at: req.pathname }); });
|
|
2268
|
+
});
|
|
2269
|
+
});
|
|
2270
|
+
var port = await helpers.listenOnRandomPort(server);
|
|
2271
|
+
try {
|
|
2272
|
+
var common = { allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true };
|
|
2273
|
+
|
|
2274
|
+
// Absolute url form (no baseUrl needed).
|
|
2275
|
+
var encAbs = b.httpClient.encrypted({ pubkey: keypair, method: "POST" });
|
|
2276
|
+
var respAbs = await encAbs.request(Object.assign(
|
|
2277
|
+
{ url: "http://127.0.0.1:" + port + "/api/abs", body: { via: "url" } }, common));
|
|
2278
|
+
check("httpClient url form: 200 + decrypted echo",
|
|
2279
|
+
respAbs.statusCode === 200 && respAbs.body.echo.via === "url" && respAbs.body.at === "/api/abs");
|
|
2280
|
+
|
|
2281
|
+
// path WITHOUT a leading slash, resolved against baseUrl; also body-less.
|
|
2282
|
+
var encPath = b.httpClient.encrypted({
|
|
2283
|
+
pubkey: keypair, baseUrl: "http://127.0.0.1:" + port, method: "POST",
|
|
2284
|
+
});
|
|
2285
|
+
var respPath = await encPath.request(Object.assign({ path: "api/noslash" }, common));
|
|
2286
|
+
check("httpClient no-leading-slash path: 200 at normalised /api/noslash",
|
|
2287
|
+
respPath.statusCode === 200 && respPath.body.at === "/api/noslash");
|
|
2288
|
+
check("httpClient body-less request: echo is null", respPath.body.echo === null);
|
|
2289
|
+
} finally {
|
|
2290
|
+
server.close();
|
|
2291
|
+
mw.close();
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
async function testApiEncryptHttpClientEmptyAndNonJsonBodies() {
|
|
2296
|
+
// The httpClient response decoder's empty-body short-circuit (→ null)
|
|
2297
|
+
// and its non-JSON-body guard (→ CLIENT_RESPONSE_NOT_JSON). The server
|
|
2298
|
+
// replies directly (bypassing the middleware) to shape each case.
|
|
2299
|
+
var http = require("http");
|
|
2300
|
+
var keypair = _serverKeypair();
|
|
2301
|
+
var server = http.createServer(function (req, res) {
|
|
2302
|
+
req.on("data", function () {});
|
|
2303
|
+
req.on("end", function () {
|
|
2304
|
+
if (/empty/.test(req.url)) { res.writeHead(204); res.end(); return; }
|
|
2305
|
+
res.writeHead(200, { "Content-Type": "text/plain" });
|
|
2306
|
+
res.end("<<< this is not json >>>");
|
|
2307
|
+
});
|
|
2308
|
+
});
|
|
2309
|
+
var port = await helpers.listenOnRandomPort(server);
|
|
2310
|
+
try {
|
|
2311
|
+
var common = { allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true };
|
|
2312
|
+
var enc = b.httpClient.encrypted({
|
|
2313
|
+
pubkey: keypair, baseUrl: "http://127.0.0.1:" + port, method: "POST",
|
|
2314
|
+
});
|
|
2315
|
+
|
|
2316
|
+
var empty = await enc.request(Object.assign({ path: "/empty", body: { x: 1 } }, common));
|
|
2317
|
+
check("httpClient empty (204) body: statusCode 204", empty.statusCode === 204);
|
|
2318
|
+
check("httpClient empty (204) body: body is null", empty.body === null);
|
|
2319
|
+
|
|
2320
|
+
var threw = null;
|
|
2321
|
+
try { await enc.request(Object.assign({ path: "/junk", body: { x: 1 } }, common)); }
|
|
2322
|
+
catch (e) { threw = e; }
|
|
2323
|
+
check("httpClient non-JSON body: CLIENT_RESPONSE_NOT_JSON",
|
|
2324
|
+
threw && threw.code === "CLIENT_RESPONSE_NOT_JSON");
|
|
2325
|
+
} finally {
|
|
2326
|
+
server.close();
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
async function testApiEncryptCloseReleasesSessionStore() {
|
|
2331
|
+
// mw.close() closes an in-mode default session store too (the
|
|
2332
|
+
// sessionStore-present branch of close()).
|
|
2333
|
+
var keypair = _serverKeypair();
|
|
2334
|
+
var mw = b.middleware.apiEncrypt({ keypair: keypair, audit: false, keying: "per-session" });
|
|
2335
|
+
check("per-session mw exposes a default sessionStore", mw.sessionStore && typeof mw.sessionStore.close === "function");
|
|
2336
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
2337
|
+
var boot = clientCtx.encryptRequest({ n: 1 });
|
|
2338
|
+
var req = _encReq(null, boot.body);
|
|
2339
|
+
var res = _mkRes();
|
|
2340
|
+
var fin = _newFinish(res);
|
|
2341
|
+
await mw(req, res, function () { res.json({ ok: 1 }); });
|
|
2342
|
+
await fin;
|
|
2343
|
+
check("per-session default store holds the row before close", mw.sessionStore.size() === 1);
|
|
2344
|
+
mw.close();
|
|
2345
|
+
check("mw.close() clears the default session store", mw.sessionStore.size() === 0);
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
async function testApiEncryptDefaultSessionStorePurgeExpired() {
|
|
2349
|
+
// The default session store's purgeExpired() sweep — reachable via the
|
|
2350
|
+
// exposed handle (the middleware itself relies on TTL-on-get, but the
|
|
2351
|
+
// store advertises purgeExpired for operator dashboards).
|
|
2352
|
+
var keypair = _serverKeypair();
|
|
2353
|
+
var mw = b.middleware.apiEncrypt({
|
|
2354
|
+
keypair: keypair, audit: false, keying: "per-session", sessionTtlMs: 1,
|
|
2355
|
+
});
|
|
2356
|
+
var clientCtx = b.middleware.apiEncrypt.client({ pubkey: keypair, keying: "per-session" });
|
|
2357
|
+
var boot = clientCtx.encryptRequest({ n: 1 });
|
|
2358
|
+
var req = _encReq(null, boot.body);
|
|
2359
|
+
var res = _mkRes();
|
|
2360
|
+
var fin = _newFinish(res);
|
|
2361
|
+
await mw(req, res, function () { res.json({ ok: 1 }); });
|
|
2362
|
+
await fin;
|
|
2363
|
+
await helpers.passiveObserve(20, "apiEncrypt: default session row past sessionTtlMs=1");
|
|
2364
|
+
var purged = mw.sessionStore.purgeExpired();
|
|
2365
|
+
check("default session store purgeExpired reclaims the expired row", purged === 1);
|
|
2366
|
+
check("default session store empty after purge", mw.sessionStore.size() === 0);
|
|
2367
|
+
mw.close();
|
|
2368
|
+
}
|
|
2369
|
+
|
|
1508
2370
|
async function run() {
|
|
1509
2371
|
await testNonceStoreSurface();
|
|
1510
2372
|
await testNonceStoreMemoryBasics();
|
|
@@ -1558,6 +2420,35 @@ async function run() {
|
|
|
1558
2420
|
await testApiEncryptPerSessionSessionInfo();
|
|
1559
2421
|
await testApiEncryptPerSessionResetRotates();
|
|
1560
2422
|
await testApiEncryptObservabilityCounters();
|
|
2423
|
+
|
|
2424
|
+
// error / adversarial / option-default branch coverage
|
|
2425
|
+
await testApiEncryptKeypairNonObjectAndMissingFields();
|
|
2426
|
+
await testApiEncryptCreateOptionDefaultsTaken();
|
|
2427
|
+
await testApiEncryptContentTypeHeaderVariants();
|
|
2428
|
+
await testApiEncryptBodyShapeGuards();
|
|
2429
|
+
await testApiEncryptPerRequestNoEkFallsThroughToShapeReject();
|
|
2430
|
+
await testApiEncryptNonceStoreErrorOnBootstrap();
|
|
2431
|
+
await testApiEncryptPruneFailureIsSwallowed();
|
|
2432
|
+
await testApiEncryptResponseEncodeFallbackAndFailure();
|
|
2433
|
+
await testApiEncryptPerSessionBootstrapInvalidSidAndCtr();
|
|
2434
|
+
await testApiEncryptPerSessionBootstrapStoreSetError();
|
|
2435
|
+
await testApiEncryptPerSessionSubsequentInvalidSidAndCtr();
|
|
2436
|
+
await testApiEncryptPerSessionSubsequentGetError();
|
|
2437
|
+
await testApiEncryptPerSessionKeyCoercionVariants();
|
|
2438
|
+
await testApiEncryptPerSessionExpiredRowEncryptedRejection();
|
|
2439
|
+
await testApiEncryptPerSessionEvictionDeleteBestEffort();
|
|
2440
|
+
await testApiEncryptPerSessionCtrClaimStoreError();
|
|
2441
|
+
await testApiEncryptSessionCreatedAuditEmitted();
|
|
2442
|
+
await testApiEncryptPublishPublicKeyNoJsonRes();
|
|
2443
|
+
await testApiEncryptClientRejectsBadKeying();
|
|
2444
|
+
await testApiEncryptClientResponseShapeSidCtrGuards();
|
|
2445
|
+
await testApiEncryptPerRequestClientRejectsTamperedResponse();
|
|
2446
|
+
await testApiEncryptClientNullPayloadDefaults();
|
|
2447
|
+
await testApiEncryptHttpClientOptionValidation();
|
|
2448
|
+
await testApiEncryptHttpClientUrlFormsAndBodyless();
|
|
2449
|
+
await testApiEncryptHttpClientEmptyAndNonJsonBodies();
|
|
2450
|
+
await testApiEncryptCloseReleasesSessionStore();
|
|
2451
|
+
await testApiEncryptDefaultSessionStorePurgeExpired();
|
|
1561
2452
|
}
|
|
1562
2453
|
|
|
1563
2454
|
// #355 — b.httpClient.encrypted must forward timeoutMs (overall wall-clock cap),
|