@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
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
* mail-bounce — vendor-shaped intake for bounce / complaint /
|
|
6
6
|
* delivery webhooks (postmark / ses / resend / custom).
|
|
7
7
|
*
|
|
8
|
+
* Exercises the happy paths plus every throw, fallback default, and
|
|
9
|
+
* fault-injected failure mode across the vendor parsers, the webhook
|
|
10
|
+
* handler middleware, and the RFC 3464 DSN parse/build pair.
|
|
11
|
+
*
|
|
8
12
|
* Run standalone: `node test/layer-0-primitives/mail-bounce.test.js`
|
|
9
13
|
* Or via smoke: `node test/smoke.js`
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
|
-
var
|
|
16
|
+
var EventEmitter = require("events").EventEmitter;
|
|
17
|
+
var helpers = require("../helpers");
|
|
13
18
|
var b = helpers.b;
|
|
14
19
|
var fs = helpers.fs;
|
|
15
20
|
var os = helpers.os;
|
|
@@ -24,6 +29,19 @@ function _waitFinish(res) {
|
|
|
24
29
|
return new Promise(function (resolve) { res.on("finish", resolve); });
|
|
25
30
|
}
|
|
26
31
|
|
|
32
|
+
// Raw EventEmitter request for tests that need manual data/end/error
|
|
33
|
+
// control (multi-chunk overflow, socket error, custom parser wiring).
|
|
34
|
+
function _rawReq(headers) {
|
|
35
|
+
var req = new EventEmitter();
|
|
36
|
+
req.headers = headers || {};
|
|
37
|
+
req.socket = { remoteAddress: "127.0.0.1" };
|
|
38
|
+
return req;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function _threw(fn) {
|
|
42
|
+
try { fn(); return null; } catch (e) { return e; }
|
|
43
|
+
}
|
|
44
|
+
|
|
27
45
|
async function testSurface() {
|
|
28
46
|
check("b.mailBounce is exposed", typeof b.mailBounce === "object");
|
|
29
47
|
check("b.mailBounce.parse is a function", typeof b.mailBounce.parse === "function");
|
|
@@ -692,6 +710,750 @@ async function testDsnBuildHeadersOnly() {
|
|
|
692
710
|
!/Content-Type: message\/rfc822\r\n/.test(raw));
|
|
693
711
|
}
|
|
694
712
|
|
|
713
|
+
// ---- Postmark parser: error + fallback branches ----
|
|
714
|
+
|
|
715
|
+
async function testPostmarkEmptyPayload() {
|
|
716
|
+
var e1 = _threw(function () { b.mailBounce.parse(null, { vendor: "postmark" }); });
|
|
717
|
+
check("postmark: null payload rejected", e1 && e1.code === "postmark/empty");
|
|
718
|
+
var e2 = _threw(function () { b.mailBounce.parse("nope", { vendor: "postmark" }); });
|
|
719
|
+
check("postmark: non-object payload rejected", e2 && e2.code === "postmark/empty");
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
async function testPostmarkMissingEmail() {
|
|
723
|
+
var e1 = _threw(function () {
|
|
724
|
+
b.mailBounce.parse({ RecordType: "Bounce", Type: "HardBounce" }, { vendor: "postmark" });
|
|
725
|
+
});
|
|
726
|
+
check("postmark: missing Email rejected", e1 && e1.code === "postmark/missing-email");
|
|
727
|
+
var e2 = _threw(function () {
|
|
728
|
+
b.mailBounce.parse({ RecordType: "Bounce", Email: "" }, { vendor: "postmark" });
|
|
729
|
+
});
|
|
730
|
+
check("postmark: empty-string Email rejected", e2 && e2.code === "postmark/missing-email");
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
async function testPostmarkDeliveryDefaults() {
|
|
734
|
+
// No MessageID (→ null) and no DeliveredAt (→ generated ISO timestamp).
|
|
735
|
+
var ev = b.mailBounce.parse({ RecordType: "Delivery", Email: "a@b.com" }, { vendor: "postmark" });
|
|
736
|
+
check("postmark delivery: messageId null when absent", ev.messageId === null);
|
|
737
|
+
check("postmark delivery: subType null", ev.subType === null);
|
|
738
|
+
check("postmark delivery: timestamp defaulted", typeof ev.timestamp === "string" && ev.timestamp.length > 0);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
async function testPostmarkSpamDescriptionFallback() {
|
|
742
|
+
// No Details, no BouncedAt → reason falls back to Description, timestamp generated.
|
|
743
|
+
var ev = b.mailBounce.parse({ RecordType: "SpamComplaint", Email: "a@b.com", Description: "why" },
|
|
744
|
+
{ vendor: "postmark" });
|
|
745
|
+
check("postmark spam: reason falls back to Description", ev.reason === "why");
|
|
746
|
+
check("postmark spam: timestamp defaulted", typeof ev.timestamp === "string" && ev.timestamp.length > 0);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
async function testPostmarkBounceTypeFallbacks() {
|
|
750
|
+
// Unrecognized Type string → subType is the lowercased Type.
|
|
751
|
+
var ev1 = b.mailBounce.parse({ RecordType: "Bounce", Type: "WeirdBounce", Email: "a@b.com" },
|
|
752
|
+
{ vendor: "postmark" });
|
|
753
|
+
check("postmark bounce: unknown Type lowercased into subType", ev1.subType === "weirdbounce");
|
|
754
|
+
|
|
755
|
+
// Non-string Type → subType "unknown".
|
|
756
|
+
var ev2 = b.mailBounce.parse({ RecordType: "Bounce", Type: 123, Email: "a@b.com" },
|
|
757
|
+
{ vendor: "postmark" });
|
|
758
|
+
check("postmark bounce: non-string Type → unknown", ev2.subType === "unknown");
|
|
759
|
+
|
|
760
|
+
// No RecordType at all (!record) still routes through the bounce branch.
|
|
761
|
+
var ev3 = b.mailBounce.parse({ Type: "HardBounce", Email: "a@b.com" }, { vendor: "postmark" });
|
|
762
|
+
check("postmark: absent RecordType routes to bounce", ev3.type === "bounce" && ev3.subType === "hard");
|
|
763
|
+
|
|
764
|
+
// SubscriptionChange routes through the bounce branch too.
|
|
765
|
+
var ev4 = b.mailBounce.parse({ RecordType: "SubscriptionChange", Type: "Unsubscribe", Email: "a@b.com" },
|
|
766
|
+
{ vendor: "postmark" });
|
|
767
|
+
check("postmark: SubscriptionChange routes to bounce mapping",
|
|
768
|
+
ev4.type === "complaint" && ev4.subType === "unsubscribe");
|
|
769
|
+
|
|
770
|
+
// Bounce timestamp falls back to DeliveredAt when BouncedAt absent.
|
|
771
|
+
var ev5 = b.mailBounce.parse({ RecordType: "Bounce", Type: "HardBounce", Email: "a@b.com",
|
|
772
|
+
DeliveredAt: "2026-01-01T00:00:00Z" }, { vendor: "postmark" });
|
|
773
|
+
check("postmark bounce: timestamp falls back to DeliveredAt", ev5.timestamp === "2026-01-01T00:00:00Z");
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
async function testPostmarkUnknownRecord() {
|
|
777
|
+
var e1 = _threw(function () {
|
|
778
|
+
b.mailBounce.parse({ RecordType: "Nope", Email: "a@b.com" }, { vendor: "postmark" });
|
|
779
|
+
});
|
|
780
|
+
check("postmark: unrecognized RecordType rejected", e1 && e1.code === "postmark/unknown-record");
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// ---- SES parser: error + fallback branches ----
|
|
784
|
+
|
|
785
|
+
async function testSesEmptyPayload() {
|
|
786
|
+
var e1 = _threw(function () { b.mailBounce.parse(null, { vendor: "ses" }); });
|
|
787
|
+
check("ses: null payload rejected", e1 && e1.code === "ses/empty");
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
async function testSesBadSnsMessageJson() {
|
|
791
|
+
var e1 = _threw(function () {
|
|
792
|
+
b.mailBounce.parse({ Type: "Notification", Message: "{not valid json" }, { vendor: "ses" });
|
|
793
|
+
});
|
|
794
|
+
check("ses: SNS Message not-JSON rejected", e1 && e1.code === "ses/bad-message-json");
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
async function testSesSnsNullMessageTypedError() {
|
|
798
|
+
// A syntactically valid SNS Message of `null` decodes to msg=null. The
|
|
799
|
+
// parser must reject it with a typed MailBounceError (like every other
|
|
800
|
+
// malformed-input path) rather than dereferencing null and throwing a
|
|
801
|
+
// raw TypeError whose V8 message ("Cannot read properties of null...")
|
|
802
|
+
// would leak into the 400 response body.
|
|
803
|
+
var e1 = _threw(function () {
|
|
804
|
+
b.mailBounce.parse({ Type: "Notification", Message: "null" }, { vendor: "ses" });
|
|
805
|
+
});
|
|
806
|
+
check("ses: SNS Message JSON-null → typed MailBounceError",
|
|
807
|
+
e1 instanceof b.mailBounce.MailBounceError);
|
|
808
|
+
check("ses: SNS Message JSON-null → ses/bad-message-json code",
|
|
809
|
+
e1 && e1.code === "ses/bad-message-json");
|
|
810
|
+
check("ses: SNS Message JSON-null does not leak the raw TypeError text",
|
|
811
|
+
e1 && !/Cannot read properties/.test(e1.message));
|
|
812
|
+
|
|
813
|
+
// Every other non-object JSON literal fails closed the same way — a
|
|
814
|
+
// number, string, boolean, or array can never be a valid SES Message.
|
|
815
|
+
var nonObjects = ["5", "\"hi\"", "true", "false", "[1,2]"];
|
|
816
|
+
var allTyped = true;
|
|
817
|
+
for (var i = 0; i < nonObjects.length; i += 1) {
|
|
818
|
+
var e = _threw(function () {
|
|
819
|
+
return b.mailBounce.parse({ Type: "Notification", Message: nonObjects[i] }, { vendor: "ses" });
|
|
820
|
+
});
|
|
821
|
+
if (!(e instanceof b.mailBounce.MailBounceError) || e.code !== "ses/bad-message-json") {
|
|
822
|
+
allTyped = false;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
check("ses: non-object SNS Message literals all rejected typed", allTyped);
|
|
826
|
+
|
|
827
|
+
// A correctly wrapped SNS envelope still unwraps + parses — the guard
|
|
828
|
+
// only rejects non-object decodes, never a real Message object.
|
|
829
|
+
var ev = b.mailBounce.parse({
|
|
830
|
+
Type: "Notification",
|
|
831
|
+
Message: JSON.stringify({
|
|
832
|
+
notificationType: "Bounce",
|
|
833
|
+
mail: { messageId: "m1", destination: ["a@b.com"] },
|
|
834
|
+
bounce: { bounceType: "Permanent", bouncedRecipients: [{ emailAddress: "a@b.com" }] },
|
|
835
|
+
}),
|
|
836
|
+
}, { vendor: "ses" });
|
|
837
|
+
check("ses: wrapped SNS envelope still parses after the guard",
|
|
838
|
+
ev.type === "bounce" && ev.subType === "hard" && ev.recipient === "a@b.com");
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
async function testHandlerSesNullMessageNoLeak() {
|
|
842
|
+
// Drive the real consumer path: the webhook handler surfaces the parse
|
|
843
|
+
// error's code + message in the 400 body. The typed error keeps the raw
|
|
844
|
+
// V8 TypeError text ("Cannot read properties of null") out of the wire.
|
|
845
|
+
var handler = b.mailBounce.handler({ vendor: "ses", audit: false });
|
|
846
|
+
var req = _bodyReq("POST", { "content-type": "application/json" },
|
|
847
|
+
JSON.stringify({ Type: "Notification", Message: "null" }));
|
|
848
|
+
var res = _bodyRes();
|
|
849
|
+
handler(req, res);
|
|
850
|
+
await _waitFinish(res);
|
|
851
|
+
check("handler ses null-message: 400", res._endedStatus === 400);
|
|
852
|
+
check("handler ses null-message: typed code surfaced",
|
|
853
|
+
/ses\/bad-message-json/.test(res._captured));
|
|
854
|
+
check("handler ses null-message: no raw TypeError leak",
|
|
855
|
+
!/Cannot read properties/.test(res._captured));
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
async function testSesMissingNotificationType() {
|
|
859
|
+
var e1 = _threw(function () {
|
|
860
|
+
b.mailBounce.parse({ mail: { messageId: "x" } }, { vendor: "ses" });
|
|
861
|
+
});
|
|
862
|
+
check("ses: missing notificationType rejected", e1 && e1.code === "ses/missing-notification-type");
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
async function testSesEventTypeAliasAndSoftBounce() {
|
|
866
|
+
// `eventType` alias (SES event-publishing shape) + Transient → soft.
|
|
867
|
+
var ev = b.mailBounce.parse({
|
|
868
|
+
eventType: "Bounce",
|
|
869
|
+
mail: { messageId: "m", destination: ["a@b.com"] },
|
|
870
|
+
bounce: { bounceType: "Transient", bouncedRecipients: [{ emailAddress: "a@b.com" }] },
|
|
871
|
+
}, { vendor: "ses" });
|
|
872
|
+
check("ses: eventType alias accepted", ev.type === "bounce");
|
|
873
|
+
check("ses: Transient bounceType → soft", ev.subType === "soft");
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
async function testSesBounceUndeterminedAndFallbacks() {
|
|
877
|
+
// Undetermined bounceType → unknown; empty bouncedRecipients → recipient
|
|
878
|
+
// falls back to mail.destination[0]; no diagnosticCode + no bounceSubType
|
|
879
|
+
// → reason null; no timestamp → generated.
|
|
880
|
+
var ev = b.mailBounce.parse({
|
|
881
|
+
notificationType: "Bounce",
|
|
882
|
+
mail: { destination: ["d@e.com"] },
|
|
883
|
+
bounce: { bounceType: "Undetermined", bouncedRecipients: [] },
|
|
884
|
+
}, { vendor: "ses" });
|
|
885
|
+
check("ses: Undetermined → unknown subType", ev.subType === "unknown");
|
|
886
|
+
check("ses: recipient falls back to destination[0]", ev.recipient === "d@e.com");
|
|
887
|
+
check("ses: reason null when no diag/subtype", ev.reason === null);
|
|
888
|
+
check("ses: messageId null when mail.messageId absent", ev.messageId === null);
|
|
889
|
+
check("ses: timestamp generated when absent", typeof ev.timestamp === "string" && ev.timestamp.length > 0);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
async function testSesBounceSubTypeReason() {
|
|
893
|
+
// No diagnosticCode but bounceSubType present → reason "bounceSubType: X".
|
|
894
|
+
var ev = b.mailBounce.parse({
|
|
895
|
+
notificationType: "Bounce",
|
|
896
|
+
mail: { destination: ["x@e.com"] },
|
|
897
|
+
bounce: { bounceType: "Permanent", bounceSubType: "General",
|
|
898
|
+
bouncedRecipients: [{ emailAddress: "x@e.com" }] },
|
|
899
|
+
}, { vendor: "ses" });
|
|
900
|
+
check("ses: reason derived from bounceSubType", ev.reason === "bounceSubType: General");
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
async function testSesComplaintDefaultsAndDelivery() {
|
|
904
|
+
// Complaint: default subType "abuse" (no complaintFeedbackType), reason
|
|
905
|
+
// from userAgent, recipient from destination fallback.
|
|
906
|
+
var ev1 = b.mailBounce.parse({
|
|
907
|
+
notificationType: "Complaint",
|
|
908
|
+
mail: { destination: ["c@e.com"] },
|
|
909
|
+
complaint: { userAgent: "UA/1", timestamp: "t" },
|
|
910
|
+
}, { vendor: "ses" });
|
|
911
|
+
check("ses complaint: default subType abuse", ev1.subType === "abuse");
|
|
912
|
+
check("ses complaint: reason from userAgent", ev1.reason === "UA/1");
|
|
913
|
+
check("ses complaint: recipient from destination fallback", ev1.recipient === "c@e.com");
|
|
914
|
+
|
|
915
|
+
// Delivery: recipient falls back to destination when delivery.recipients absent.
|
|
916
|
+
var ev2 = b.mailBounce.parse({
|
|
917
|
+
notificationType: "Delivery",
|
|
918
|
+
mail: { messageId: "m", destination: ["d@e.com"] },
|
|
919
|
+
delivery: { timestamp: "t" },
|
|
920
|
+
}, { vendor: "ses" });
|
|
921
|
+
check("ses delivery: recipient from destination fallback", ev2.recipient === "d@e.com");
|
|
922
|
+
check("ses delivery: type delivery", ev2.type === "delivery");
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
async function testSesUnknownNotificationType() {
|
|
926
|
+
var e1 = _threw(function () {
|
|
927
|
+
b.mailBounce.parse({ notificationType: "Reject" }, { vendor: "ses" });
|
|
928
|
+
});
|
|
929
|
+
check("ses: unknown notificationType rejected", e1 && e1.code === "ses/unknown-notification-type");
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// ---- Resend parser: error + fallback branches ----
|
|
933
|
+
|
|
934
|
+
async function testResendEmptyAndMissingType() {
|
|
935
|
+
var e1 = _threw(function () { b.mailBounce.parse(null, { vendor: "resend" }); });
|
|
936
|
+
check("resend: null payload rejected", e1 && e1.code === "resend/empty");
|
|
937
|
+
var e2 = _threw(function () { b.mailBounce.parse({ data: {} }, { vendor: "resend" }); });
|
|
938
|
+
check("resend: missing type rejected", e2 && e2.code === "resend/missing-type");
|
|
939
|
+
var e3 = _threw(function () { b.mailBounce.parse({ type: 42 }, { vendor: "resend" }); });
|
|
940
|
+
check("resend: non-string type rejected", e3 && e3.code === "resend/missing-type");
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
async function testResendBounceFallbacks() {
|
|
944
|
+
// No data at all → data defaults to {}; recipient null; messageId null;
|
|
945
|
+
// bounce default {} → unknown subType; reason null.
|
|
946
|
+
var ev1 = b.mailBounce.parse({ type: "email.bounced" }, { vendor: "resend" });
|
|
947
|
+
check("resend bounced: recipient null when no data", ev1.recipient === null);
|
|
948
|
+
check("resend bounced: messageId null when no id", ev1.messageId === null);
|
|
949
|
+
check("resend bounced: unknown subType when no bounce", ev1.subType === "unknown");
|
|
950
|
+
check("resend bounced: reason null when no bounce message", ev1.reason === null);
|
|
951
|
+
|
|
952
|
+
// Transient bounce → soft.
|
|
953
|
+
var ev2 = b.mailBounce.parse({
|
|
954
|
+
type: "email.bounced",
|
|
955
|
+
data: { id: "1", to: ["a@b.com"], bounce: { type: "Transient", message: "m" } },
|
|
956
|
+
}, { vendor: "resend" });
|
|
957
|
+
check("resend bounced: Transient → soft", ev2.subType === "soft");
|
|
958
|
+
|
|
959
|
+
// Unrecognized bounce.type → unknown.
|
|
960
|
+
var ev3 = b.mailBounce.parse({
|
|
961
|
+
type: "email.bounced",
|
|
962
|
+
data: { id: "1", to: ["a@b.com"], bounce: { type: "Weird" } },
|
|
963
|
+
}, { vendor: "resend" });
|
|
964
|
+
check("resend bounced: unrecognized bounce.type → unknown", ev3.subType === "unknown");
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
async function testResendUnknownType() {
|
|
968
|
+
var e1 = _threw(function () {
|
|
969
|
+
b.mailBounce.parse({ type: "email.opened", data: { id: "1", to: "a@b.com" } }, { vendor: "resend" });
|
|
970
|
+
});
|
|
971
|
+
check("resend: unknown event type rejected", e1 && e1.code === "resend/unknown-type");
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// ---- Custom parser validation branches ----
|
|
975
|
+
|
|
976
|
+
async function testCustomParserValidation() {
|
|
977
|
+
// Parser returns a non-object.
|
|
978
|
+
var e1 = _threw(function () {
|
|
979
|
+
b.mailBounce.parse({}, { parser: function () { return 7; } });
|
|
980
|
+
});
|
|
981
|
+
check("custom: non-object result rejected", e1 && e1.code === "custom/bad-shape");
|
|
982
|
+
|
|
983
|
+
// Parser returns null.
|
|
984
|
+
var e2 = _threw(function () {
|
|
985
|
+
b.mailBounce.parse({}, { parser: function () { return null; } });
|
|
986
|
+
});
|
|
987
|
+
check("custom: null result rejected", e2 && e2.code === "custom/bad-shape");
|
|
988
|
+
|
|
989
|
+
// Missing vendor.
|
|
990
|
+
var e3 = _threw(function () {
|
|
991
|
+
b.mailBounce.parse({}, { parser: function () { return { type: "bounce", recipient: "a@b.com" }; } });
|
|
992
|
+
});
|
|
993
|
+
check("custom: missing vendor rejected", e3 && e3.code === "custom/missing-vendor");
|
|
994
|
+
|
|
995
|
+
// Missing / empty recipient.
|
|
996
|
+
var e4 = _threw(function () {
|
|
997
|
+
b.mailBounce.parse({}, { parser: function () { return { vendor: "x", type: "bounce", recipient: "" }; } });
|
|
998
|
+
});
|
|
999
|
+
check("custom: empty recipient rejected", e4 && e4.code === "custom/missing-recipient");
|
|
1000
|
+
var e5 = _threw(function () {
|
|
1001
|
+
b.mailBounce.parse({}, { parser: function () { return { vendor: "x", type: "delivery" }; } });
|
|
1002
|
+
});
|
|
1003
|
+
check("custom: absent recipient rejected", e5 && e5.code === "custom/missing-recipient");
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// ---- parse() routing defaults ----
|
|
1007
|
+
|
|
1008
|
+
async function testParseRoutingDefaults() {
|
|
1009
|
+
// No opts object at all → default {} → missing-vendor.
|
|
1010
|
+
var e1 = _threw(function () { b.mailBounce.parse({ Email: "a@b.com" }); });
|
|
1011
|
+
check("parse: no opts → missing-vendor", e1 && e1.code === "missing-vendor");
|
|
1012
|
+
// Empty-string vendor → missing-vendor (length 0 branch).
|
|
1013
|
+
var e2 = _threw(function () { b.mailBounce.parse({}, { vendor: "" }); });
|
|
1014
|
+
check("parse: empty vendor → missing-vendor", e2 && e2.code === "missing-vendor");
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// ---- handler: config-time validation ----
|
|
1018
|
+
|
|
1019
|
+
async function testHandlerConfigValidation() {
|
|
1020
|
+
var e0 = _threw(function () { b.mailBounce.handler(); });
|
|
1021
|
+
check("handler: no opts → bad-config", e0 && e0.code === "handler/bad-config");
|
|
1022
|
+
|
|
1023
|
+
var e1 = _threw(function () { b.mailBounce.handler({ vendor: "mailgun" }); });
|
|
1024
|
+
check("handler: unknown vendor → bad-config", e1 && e1.code === "handler/bad-config");
|
|
1025
|
+
|
|
1026
|
+
// maxBytes must be a positive finite integer — Infinity / NaN / negative /
|
|
1027
|
+
// non-integer all throw at create() time.
|
|
1028
|
+
var bad = [-1, 0, 1.5, Infinity, NaN];
|
|
1029
|
+
var allThrew = true;
|
|
1030
|
+
for (var i = 0; i < bad.length; i += 1) {
|
|
1031
|
+
var e = _threw(function () { return b.mailBounce.handler({ vendor: "postmark", maxBytes: bad[i] }); });
|
|
1032
|
+
if (!e || e.code !== "mail-bounce/bad-opt") allThrew = false;
|
|
1033
|
+
}
|
|
1034
|
+
check("handler: invalid maxBytes rejected at create()", allThrew);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// ---- handler: middleware runtime branches ----
|
|
1038
|
+
|
|
1039
|
+
async function testHandlerCustomParserPath() {
|
|
1040
|
+
var seen = null;
|
|
1041
|
+
var handler = b.mailBounce.handler({
|
|
1042
|
+
audit: false,
|
|
1043
|
+
parser: function (p) {
|
|
1044
|
+
return { vendor: "internal", type: "bounce", subType: "hard",
|
|
1045
|
+
recipient: p.who, messageId: null, reason: null, timestamp: "t", raw: p };
|
|
1046
|
+
},
|
|
1047
|
+
onBounce: function (ev) { seen = ev; },
|
|
1048
|
+
});
|
|
1049
|
+
var req = _bodyReq("POST", { "content-type": "application/json" }, JSON.stringify({ who: "a@b.com" }));
|
|
1050
|
+
var res = _bodyRes();
|
|
1051
|
+
handler(req, res);
|
|
1052
|
+
await _waitFinish(res);
|
|
1053
|
+
check("handler custom-parser: 200", res._endedStatus === 200);
|
|
1054
|
+
check("handler custom-parser: onBounce saw normalized event", seen && seen.recipient === "a@b.com");
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
async function testHandlerCustomParserBadShape() {
|
|
1058
|
+
// A misbehaving custom parser (bad shape) is caught → 400 with the code.
|
|
1059
|
+
var handler = b.mailBounce.handler({
|
|
1060
|
+
audit: false,
|
|
1061
|
+
parser: function () { return { vendor: "x", type: "garbage", recipient: "a@b.com" }; },
|
|
1062
|
+
});
|
|
1063
|
+
var req = _bodyReq("POST", { "content-type": "application/json" }, JSON.stringify({ any: 1 }));
|
|
1064
|
+
var res = _bodyRes();
|
|
1065
|
+
handler(req, res);
|
|
1066
|
+
await _waitFinish(res);
|
|
1067
|
+
check("handler custom-parser bad shape: 400", res._endedStatus === 400);
|
|
1068
|
+
check("handler custom-parser bad shape: code surfaced", /custom\/bad-type/.test(res._captured));
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
async function testHandlerParseFailSurfacesCode() {
|
|
1072
|
+
var handler = b.mailBounce.handler({ vendor: "postmark", audit: false });
|
|
1073
|
+
var req = _bodyReq("POST", { "content-type": "application/json" },
|
|
1074
|
+
JSON.stringify({ RecordType: "Bounce" })); // missing Email
|
|
1075
|
+
var res = _bodyRes();
|
|
1076
|
+
handler(req, res);
|
|
1077
|
+
await _waitFinish(res);
|
|
1078
|
+
check("handler parse-fail: 400", res._endedStatus === 400);
|
|
1079
|
+
check("handler parse-fail: error code surfaced", /postmark\/missing-email/.test(res._captured));
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
async function testHandlerVerifyThrows() {
|
|
1083
|
+
// verify() throwing is distinct from verify() returning false: it maps to
|
|
1084
|
+
// 401 "verification error" (fail-closed), and onBounce never runs.
|
|
1085
|
+
var onBounceRan = false;
|
|
1086
|
+
var handler = b.mailBounce.handler({
|
|
1087
|
+
vendor: "postmark", audit: false,
|
|
1088
|
+
verify: function () { throw new Error("boom"); },
|
|
1089
|
+
onBounce: function () { onBounceRan = true; },
|
|
1090
|
+
});
|
|
1091
|
+
var req = _bodyReq("POST", { "content-type": "application/json" },
|
|
1092
|
+
JSON.stringify({ RecordType: "Delivery", Email: "a@b.com" }));
|
|
1093
|
+
var res = _bodyRes();
|
|
1094
|
+
handler(req, res);
|
|
1095
|
+
await _waitFinish(res);
|
|
1096
|
+
check("handler verify-throws: 401", res._endedStatus === 401);
|
|
1097
|
+
check("handler verify-throws: verification error body", /verification error/.test(res._captured));
|
|
1098
|
+
check("handler verify-throws: onBounce not run", onBounceRan === false);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
async function testHandlerReqError() {
|
|
1102
|
+
// A socket 'error' before the body completes → 400 request error.
|
|
1103
|
+
var handler = b.mailBounce.handler({ vendor: "postmark", audit: false });
|
|
1104
|
+
var req = _rawReq({ "content-type": "application/json" });
|
|
1105
|
+
var res = _bodyRes();
|
|
1106
|
+
var finished = _waitFinish(res);
|
|
1107
|
+
handler(req, res);
|
|
1108
|
+
req.emit("error", new Error("ECONNRESET"));
|
|
1109
|
+
await finished;
|
|
1110
|
+
check("handler req-error: 400", res._endedStatus === 400);
|
|
1111
|
+
check("handler req-error: request error body", /request error/.test(res._captured));
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
async function testHandlerMultiChunkOverflowAbort() {
|
|
1115
|
+
// First chunk overflows maxBytes → 413 + aborted; subsequent chunk and the
|
|
1116
|
+
// end event are ignored (single response).
|
|
1117
|
+
var handler = b.mailBounce.handler({ vendor: "postmark", audit: false, maxBytes: 10 });
|
|
1118
|
+
var req = _rawReq({ "content-type": "application/json" });
|
|
1119
|
+
var res = _bodyRes();
|
|
1120
|
+
var finishCount = 0;
|
|
1121
|
+
res.on("finish", function () { finishCount += 1; });
|
|
1122
|
+
var finished = _waitFinish(res);
|
|
1123
|
+
handler(req, res);
|
|
1124
|
+
req.emit("data", Buffer.from("12345678901234567890")); // 20 bytes > 10 → abort + 413
|
|
1125
|
+
req.emit("data", Buffer.from("more")); // ignored (aborted)
|
|
1126
|
+
req.emit("end"); // ignored (aborted)
|
|
1127
|
+
await finished;
|
|
1128
|
+
check("handler overflow: 413", res._endedStatus === 413);
|
|
1129
|
+
check("handler overflow: only one response sent", finishCount === 1);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
async function testHandlerNoWriteHeadResNoCrash() {
|
|
1133
|
+
// _send guards on typeof res.writeHead === "function"; a response object
|
|
1134
|
+
// lacking writeHead must be a silent no-op, never a crash.
|
|
1135
|
+
var handler = b.mailBounce.handler({ vendor: "postmark", audit: false });
|
|
1136
|
+
var req = _rawReq({ "content-type": "application/json" });
|
|
1137
|
+
var res = new EventEmitter(); // no writeHead / no end
|
|
1138
|
+
var err = _threw(function () {
|
|
1139
|
+
handler(req, res);
|
|
1140
|
+
req.emit("data", Buffer.from("not-json")); // → invalid JSON → _send → no-op
|
|
1141
|
+
req.emit("end");
|
|
1142
|
+
});
|
|
1143
|
+
check("handler no-writeHead res: no crash", err === null);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
async function testHandlerAuditOnDeliverySuccess() {
|
|
1147
|
+
// audit defaults ON: a delivery event emits an audit row with outcome
|
|
1148
|
+
// "success" (vs "denied" for bounce/complaint), and the no-verify /
|
|
1149
|
+
// onBounce-resolve path returns 200.
|
|
1150
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-mbc-"));
|
|
1151
|
+
try {
|
|
1152
|
+
await setupTestDb(tmpDir);
|
|
1153
|
+
var seen = null;
|
|
1154
|
+
var handler = b.mailBounce.handler({
|
|
1155
|
+
vendor: "postmark",
|
|
1156
|
+
onBounce: function (ev) { seen = ev; },
|
|
1157
|
+
});
|
|
1158
|
+
var req = _bodyReq("POST", { "content-type": "application/json" },
|
|
1159
|
+
JSON.stringify({ RecordType: "Delivery", Email: "ok@e.com", MessageID: "d1",
|
|
1160
|
+
DeliveredAt: "2026-04-28T00:00:00Z" }));
|
|
1161
|
+
var res = _bodyRes();
|
|
1162
|
+
handler(req, res);
|
|
1163
|
+
await _waitFinish(res);
|
|
1164
|
+
check("handler audit-on delivery: 200", res._endedStatus === 200);
|
|
1165
|
+
check("handler audit-on delivery: onBounce saw delivery", seen && seen.type === "delivery");
|
|
1166
|
+
|
|
1167
|
+
await b.audit.flush();
|
|
1168
|
+
var rows = await b.audit.query({ action: "system.mail.bounce" });
|
|
1169
|
+
check("handler audit-on delivery: one audit row", rows.length === 1);
|
|
1170
|
+
check("handler audit-on delivery: outcome success", rows[0].outcome === "success");
|
|
1171
|
+
} finally {
|
|
1172
|
+
await teardownTestDb(tmpDir);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// ---- DSN parse: error / defensive branches ----
|
|
1177
|
+
|
|
1178
|
+
async function testDsnParseInputGuards() {
|
|
1179
|
+
var e1 = _threw(function () { b.mailBounce.dsn.parse(12345); });
|
|
1180
|
+
check("dsn parse: non-string rejected", e1 && e1.code === "bounce/dsn-parse-failed");
|
|
1181
|
+
var e2 = _threw(function () { b.mailBounce.dsn.parse(null); });
|
|
1182
|
+
check("dsn parse: null rejected", e2 && e2.code === "bounce/dsn-parse-failed");
|
|
1183
|
+
|
|
1184
|
+
// Body cap — a payload over 1 MiB is a typed error, not a regex-backtrack hang.
|
|
1185
|
+
var oversize = 'Content-Type: multipart/report; report-type=delivery-status; boundary="b"\r\n\r\n' +
|
|
1186
|
+
"x".repeat(b.constants.BYTES.mib(1) + 10);
|
|
1187
|
+
var e3 = _threw(function () { b.mailBounce.dsn.parse(oversize); });
|
|
1188
|
+
check("dsn parse: oversize rejected", e3 && e3.code === "bounce/dsn-parse-failed" &&
|
|
1189
|
+
/exceeds/.test(e3.message));
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
async function testDsnParseStructureGuards() {
|
|
1193
|
+
var e1 = _threw(function () { b.mailBounce.dsn.parse("Subject: hi\r\n\r\nbody"); });
|
|
1194
|
+
check("dsn parse: missing top Content-Type rejected", e1 && e1.code === "bounce/dsn-malformed");
|
|
1195
|
+
|
|
1196
|
+
var e2 = _threw(function () {
|
|
1197
|
+
b.mailBounce.dsn.parse('Content-Type: multipart/report; report-type=disposition-notification; ' +
|
|
1198
|
+
'boundary="b"\r\n\r\n--b\r\n--b--\r\n');
|
|
1199
|
+
});
|
|
1200
|
+
check("dsn parse: wrong report-type rejected", e2 && e2.code === "bounce/dsn-malformed");
|
|
1201
|
+
|
|
1202
|
+
var e3 = _threw(function () {
|
|
1203
|
+
b.mailBounce.dsn.parse("Content-Type: multipart/report; report-type=delivery-status\r\n\r\nbody");
|
|
1204
|
+
});
|
|
1205
|
+
check("dsn parse: missing boundary rejected", e3 && e3.code === "bounce/dsn-malformed");
|
|
1206
|
+
|
|
1207
|
+
// multipart/report with only one part → needs >= 2.
|
|
1208
|
+
var onePart = [
|
|
1209
|
+
'Content-Type: multipart/report; report-type=delivery-status; boundary="b"', '',
|
|
1210
|
+
'--b', 'Content-Type: text/plain', '', 'only one part', '',
|
|
1211
|
+
'--b--', '',
|
|
1212
|
+
].join('\r\n');
|
|
1213
|
+
var e4 = _threw(function () { b.mailBounce.dsn.parse(onePart); });
|
|
1214
|
+
check("dsn parse: fewer than 2 parts rejected", e4 && e4.code === "bounce/dsn-malformed");
|
|
1215
|
+
|
|
1216
|
+
// Two parts but none is message/delivery-status.
|
|
1217
|
+
var noStatus = [
|
|
1218
|
+
'Content-Type: multipart/report; report-type=delivery-status; boundary="b"', '',
|
|
1219
|
+
'--b', 'Content-Type: text/plain', '', 't', '',
|
|
1220
|
+
'--b', 'Content-Type: message/rfc822', '', 'From: x', '',
|
|
1221
|
+
'--b--', '',
|
|
1222
|
+
].join('\r\n');
|
|
1223
|
+
var e5 = _threw(function () { b.mailBounce.dsn.parse(noStatus); });
|
|
1224
|
+
check("dsn parse: no delivery-status part rejected", e5 && e5.code === "bounce/dsn-malformed");
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
async function testDsnParseRecipientGuards() {
|
|
1228
|
+
// delivery-status part present but only a per-message group (no recipient group).
|
|
1229
|
+
var onlyPerMsg = [
|
|
1230
|
+
'Content-Type: multipart/report; report-type=delivery-status; boundary="b"', '',
|
|
1231
|
+
'--b', 'Content-Type: text/plain', '', 't', '',
|
|
1232
|
+
'--b', 'Content-Type: message/delivery-status', '', 'Reporting-MTA: dns; m', '',
|
|
1233
|
+
'--b--', '',
|
|
1234
|
+
].join('\r\n');
|
|
1235
|
+
var e1 = _threw(function () { b.mailBounce.dsn.parse(onlyPerMsg); });
|
|
1236
|
+
check("dsn parse: no per-recipient group rejected", e1 && e1.code === "bounce/dsn-malformed");
|
|
1237
|
+
|
|
1238
|
+
// Empty delivery-status body (groups.length === 0 path) → no recipients.
|
|
1239
|
+
var emptyStatus = [
|
|
1240
|
+
'Content-Type: multipart/report; report-type=delivery-status; boundary="b"', '',
|
|
1241
|
+
'--b', 'Content-Type: text/plain', '', 't', '',
|
|
1242
|
+
'--b', 'Content-Type: message/delivery-status', '', '',
|
|
1243
|
+
'--b--', '',
|
|
1244
|
+
].join('\r\n');
|
|
1245
|
+
var e2 = _threw(function () { b.mailBounce.dsn.parse(emptyStatus); });
|
|
1246
|
+
check("dsn parse: empty delivery-status body rejected", e2 && e2.code === "bounce/dsn-malformed");
|
|
1247
|
+
|
|
1248
|
+
// Recipient group missing Final-Recipient AND Original-Recipient.
|
|
1249
|
+
var noFinal = [
|
|
1250
|
+
'Content-Type: multipart/report; report-type=delivery-status; boundary="b"', '',
|
|
1251
|
+
'--b', 'Content-Type: text/plain', '', 't', '',
|
|
1252
|
+
'--b', 'Content-Type: message/delivery-status', '', 'Reporting-MTA: dns; m', '',
|
|
1253
|
+
'Action: failed', 'Status: 5.1.1', '',
|
|
1254
|
+
'--b--', '',
|
|
1255
|
+
].join('\r\n');
|
|
1256
|
+
var e3 = _threw(function () { b.mailBounce.dsn.parse(noFinal); });
|
|
1257
|
+
check("dsn parse: missing Final-Recipient rejected", e3 && e3.code === "bounce/dsn-malformed");
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
async function testDsnParseActionBranches() {
|
|
1261
|
+
function dsn(action, status) {
|
|
1262
|
+
return [
|
|
1263
|
+
'Content-Type: multipart/report; report-type=delivery-status; boundary="b"', '',
|
|
1264
|
+
'--b', 'Content-Type: text/plain', '', 'human', '',
|
|
1265
|
+
'--b', 'Content-Type: message/delivery-status', '', 'Reporting-MTA: dns; m', '',
|
|
1266
|
+
'Final-Recipient: rfc822;u@e.com',
|
|
1267
|
+
].concat(action ? ['Action: ' + action] : [])
|
|
1268
|
+
.concat(['Status: ' + status, '', '--b--', '']).join('\r\n');
|
|
1269
|
+
}
|
|
1270
|
+
// relayed / expanded → delivery.
|
|
1271
|
+
var evR = b.mailBounce.dsn.parse(dsn("relayed", "2.0.0"));
|
|
1272
|
+
check("dsn parse: relayed → delivery", evR.type === "delivery" && evR.subType === null);
|
|
1273
|
+
var evX = b.mailBounce.dsn.parse(dsn("expanded", "2.0.0"));
|
|
1274
|
+
check("dsn parse: expanded → delivery", evX.type === "delivery" && evX.subType === null);
|
|
1275
|
+
// No Action field → bounce with subType unknown.
|
|
1276
|
+
var evN = b.mailBounce.dsn.parse(dsn(null, "5.0.0"));
|
|
1277
|
+
check("dsn parse: absent Action → bounce/unknown", evN.type === "bounce" && evN.subType === "unknown");
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
async function testDsnParseReasonAndSkipBranches() {
|
|
1281
|
+
// No Diagnostic-Code AND no text/plain part → reason null.
|
|
1282
|
+
var noReason = [
|
|
1283
|
+
'Content-Type: multipart/report; report-type=delivery-status; boundary="b"', '',
|
|
1284
|
+
'--b', 'Content-Type: message/delivery-status', '', 'Reporting-MTA: dns; m', '',
|
|
1285
|
+
'Final-Recipient: rfc822;u@e.com', 'Action: failed', 'Status: 5.1.1', '',
|
|
1286
|
+
'--b', 'Content-Type: message/rfc822', '', 'From: x', '',
|
|
1287
|
+
'--b--', '',
|
|
1288
|
+
].join('\r\n');
|
|
1289
|
+
var ev1 = b.mailBounce.dsn.parse(noReason);
|
|
1290
|
+
check("dsn parse: reason null with no diag and no humanText", ev1.reason === null);
|
|
1291
|
+
|
|
1292
|
+
// A malformed (colon-less) recipient group is skipped; a valid group after
|
|
1293
|
+
// it still parses (covers the headers.length === 0 `continue`).
|
|
1294
|
+
var contThenOk = [
|
|
1295
|
+
'Content-Type: multipart/report; report-type=delivery-status; boundary="b"', '',
|
|
1296
|
+
'--b', 'Content-Type: text/plain', '', 'human', '',
|
|
1297
|
+
'--b', 'Content-Type: message/delivery-status', '', 'Reporting-MTA: dns; m', '',
|
|
1298
|
+
'nocolongarbage', '',
|
|
1299
|
+
'Final-Recipient: rfc822;real@e.com', 'Action: failed', 'Status: 5.1.1', '',
|
|
1300
|
+
'--b--', '',
|
|
1301
|
+
].join('\r\n');
|
|
1302
|
+
var ev2 = b.mailBounce.dsn.parse(contThenOk);
|
|
1303
|
+
check("dsn parse: colon-less recipient group skipped, later group parses",
|
|
1304
|
+
ev2.recipient === "real@e.com" && ev2.subType === "hard");
|
|
1305
|
+
|
|
1306
|
+
// Human-text fallback truncates to first 5 lines when Diagnostic-Code absent.
|
|
1307
|
+
var multi = [
|
|
1308
|
+
'Content-Type: multipart/report; report-type=delivery-status; boundary="b"', '',
|
|
1309
|
+
'--b', 'Content-Type: text/plain', '', 'L1', 'L2', 'L3', 'L4', 'L5', 'L6', 'L7', '',
|
|
1310
|
+
'--b', 'Content-Type: message/delivery-status', '', 'Reporting-MTA: dns; m', '',
|
|
1311
|
+
'Final-Recipient: rfc822;u@e.com', 'Action: failed', 'Status: 5.1.1', '',
|
|
1312
|
+
'--b--', '',
|
|
1313
|
+
].join('\r\n');
|
|
1314
|
+
var ev3 = b.mailBounce.dsn.parse(multi);
|
|
1315
|
+
check("dsn parse: humanText fallback keeps first 5 lines", ev3.reason === "L1 L2 L3 L4 L5");
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
// ---- DSN build: error / defensive / optional-field branches ----
|
|
1319
|
+
|
|
1320
|
+
async function testDsnBuildInputGuards() {
|
|
1321
|
+
var e0 = _threw(function () { b.mailBounce.dsn.build(); });
|
|
1322
|
+
check("dsn build: missing opts rejected", e0 && e0.code === "bounce/dsn-malformed");
|
|
1323
|
+
var e1 = _threw(function () { b.mailBounce.dsn.build(null); });
|
|
1324
|
+
check("dsn build: null opts rejected", e1 && e1.code === "bounce/dsn-malformed");
|
|
1325
|
+
var e2 = _threw(function () { b.mailBounce.dsn.build({ action: "failed", status: "5.1.1" }); });
|
|
1326
|
+
check("dsn build: missing finalRecipient rejected", e2 && e2.code === "bounce/dsn-malformed");
|
|
1327
|
+
|
|
1328
|
+
// status non-string (number) and absent both hit the status guard.
|
|
1329
|
+
var e3 = _threw(function () {
|
|
1330
|
+
b.mailBounce.dsn.build({ finalRecipient: "u@e.com", action: "failed", status: 500 });
|
|
1331
|
+
});
|
|
1332
|
+
check("dsn build: non-string status rejected", e3 && e3.code === "bounce/dsn-malformed");
|
|
1333
|
+
var e4 = _threw(function () {
|
|
1334
|
+
b.mailBounce.dsn.build({ finalRecipient: "u@e.com", action: "failed" });
|
|
1335
|
+
});
|
|
1336
|
+
check("dsn build: absent status rejected", e4 && e4.code === "bounce/dsn-malformed");
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
async function testDsnBuildDefaultAction() {
|
|
1340
|
+
// No action → defaults to "failed".
|
|
1341
|
+
var raw = b.mailBounce.dsn.build({ finalRecipient: "u@e.com", status: "5.1.1" });
|
|
1342
|
+
check("dsn build: default action failed", /Action: failed/.test(raw));
|
|
1343
|
+
// No diagnosticCode → humanText omits the "remote server reported" line.
|
|
1344
|
+
check("dsn build: humanText omits report line without diagnosticCode",
|
|
1345
|
+
!/remote server reported/.test(raw));
|
|
1346
|
+
check("dsn build: default Reporting-MTA", /Reporting-MTA: dns; localhost/.test(raw));
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
async function testDsnBuildAllOptionalFields() {
|
|
1350
|
+
var raw = b.mailBounce.dsn.build({
|
|
1351
|
+
finalRecipient: "u@e.com",
|
|
1352
|
+
originalRecipient: "alias@e.com",
|
|
1353
|
+
action: "delayed",
|
|
1354
|
+
status: "4.4.1",
|
|
1355
|
+
from: "mailer@x",
|
|
1356
|
+
to: "sender@y",
|
|
1357
|
+
subject: "Delivery failure",
|
|
1358
|
+
messageId: "<id@x>",
|
|
1359
|
+
reportingMta: "dns; mta.x",
|
|
1360
|
+
remoteMta: "dns; mx.y",
|
|
1361
|
+
arrivalDate: "Mon, 28 Apr 2026 12:00:00 +0000",
|
|
1362
|
+
originalEnvelopeId: "env-1",
|
|
1363
|
+
lastAttemptDate: "Tue, 29 Apr 2026 12:00:00 +0000",
|
|
1364
|
+
willRetryUntil: "Wed, 30 Apr 2026 12:00:00 +0000",
|
|
1365
|
+
humanText: "Custom operator human text",
|
|
1366
|
+
diagnosticCode: "smtp; 421 try later",
|
|
1367
|
+
originalMessage: "From: x\r\n\r\nbody",
|
|
1368
|
+
});
|
|
1369
|
+
check("dsn build: From header emitted", /^From: mailer@x$/m.test(raw));
|
|
1370
|
+
check("dsn build: To header emitted", /^To: sender@y$/m.test(raw));
|
|
1371
|
+
check("dsn build: Subject header emitted", /^Subject: Delivery failure$/m.test(raw));
|
|
1372
|
+
check("dsn build: Message-ID header emitted", /^Message-ID: <id@x>$/m.test(raw));
|
|
1373
|
+
check("dsn build: Original-Envelope-Id emitted", /Original-Envelope-Id: env-1/.test(raw));
|
|
1374
|
+
check("dsn build: Original-Recipient emitted", /Original-Recipient: rfc822;alias@e.com/.test(raw));
|
|
1375
|
+
check("dsn build: Last-Attempt-Date emitted", /Last-Attempt-Date: Tue, 29 Apr 2026/.test(raw));
|
|
1376
|
+
check("dsn build: Will-Retry-Until emitted", /Will-Retry-Until: Wed, 30 Apr 2026/.test(raw));
|
|
1377
|
+
check("dsn build: custom humanText used", /Custom operator human text/.test(raw));
|
|
1378
|
+
check("dsn build: Remote-MTA emitted", /Remote-MTA: dns; mx.y/.test(raw));
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
async function testDsnBuildOriginalMessageShapes() {
|
|
1382
|
+
// Plain-string originalMessage → message/rfc822 with body.
|
|
1383
|
+
var raw1 = b.mailBounce.dsn.build({
|
|
1384
|
+
finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1385
|
+
originalMessage: "From: x\r\n\r\nbody",
|
|
1386
|
+
});
|
|
1387
|
+
check("dsn build: string originalMessage → message/rfc822",
|
|
1388
|
+
/Content-Type: message\/rfc822/.test(raw1) && /body/.test(raw1));
|
|
1389
|
+
|
|
1390
|
+
// Non-headersOnly object → message/rfc822 with an empty body (else branch).
|
|
1391
|
+
var raw2 = b.mailBounce.dsn.build({
|
|
1392
|
+
finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1393
|
+
originalMessage: { foo: 1 },
|
|
1394
|
+
});
|
|
1395
|
+
check("dsn build: plain-object originalMessage → message/rfc822 empty body",
|
|
1396
|
+
/Content-Type: message\/rfc822/.test(raw2));
|
|
1397
|
+
|
|
1398
|
+
// headersOnly with no headers field → text/rfc822-headers, empty.
|
|
1399
|
+
var raw3 = b.mailBounce.dsn.build({
|
|
1400
|
+
finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1401
|
+
originalMessage: { headersOnly: true },
|
|
1402
|
+
});
|
|
1403
|
+
check("dsn build: headersOnly with no headers → text/rfc822-headers",
|
|
1404
|
+
/Content-Type: text\/rfc822-headers/.test(raw3));
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
async function testDsnBuildHeaderInjectionGuards() {
|
|
1408
|
+
// Structured fields reject CR/LF/NUL (header-injection fail-closed). These
|
|
1409
|
+
// complement testDsnBuildRejectsCrlfInjection above, which drives
|
|
1410
|
+
// finalRecipient / reportingMta / from / subject / remoteMta.
|
|
1411
|
+
var cases = {
|
|
1412
|
+
originalRecipient: { finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1413
|
+
originalRecipient: "a@e.com\r\nX-Evil: 1" },
|
|
1414
|
+
to: { finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1415
|
+
to: "x@y\r\nBcc: victim@evil.test" },
|
|
1416
|
+
messageId: { finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1417
|
+
messageId: "<id>\r\nX-Evil: 1" },
|
|
1418
|
+
arrivalDate: { finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1419
|
+
arrivalDate: "Mon\r\nX-Evil: 1" },
|
|
1420
|
+
originalEnvelopeId:{ finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1421
|
+
originalEnvelopeId: "e\r\nX-Evil: 1" },
|
|
1422
|
+
lastAttemptDate: { finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1423
|
+
lastAttemptDate: "d\r\nX-Evil: 1" },
|
|
1424
|
+
willRetryUntil: { finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1425
|
+
willRetryUntil: "d\r\nX-Evil: 1" },
|
|
1426
|
+
nulFinalRecipient: { finalRecipient: "u@e.com" + String.fromCharCode(0) + "x",
|
|
1427
|
+
action: "failed", status: "5.1.1" },
|
|
1428
|
+
};
|
|
1429
|
+
var names = Object.keys(cases);
|
|
1430
|
+
var allBadDsnField = true;
|
|
1431
|
+
for (var i = 0; i < names.length; i += 1) {
|
|
1432
|
+
var name = names[i];
|
|
1433
|
+
var e = _threw(function () { return b.mailBounce.dsn.build(cases[name]); });
|
|
1434
|
+
if (!e || e.code !== "bounce/bad-dsn-field") allBadDsnField = false;
|
|
1435
|
+
}
|
|
1436
|
+
check("dsn build: CR/LF/NUL in structured fields all fail closed", allBadDsnField);
|
|
1437
|
+
|
|
1438
|
+
// Bad action allowlist rejection at build.
|
|
1439
|
+
var eAct = _threw(function () {
|
|
1440
|
+
b.mailBounce.dsn.build({ finalRecipient: "u@e.com", action: "explode", status: "5.1.1" });
|
|
1441
|
+
});
|
|
1442
|
+
check("dsn build: non-RFC3464 action rejected", eAct && eAct.code === "bounce/dsn-malformed");
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
async function testDsnBuildDiagnosticFoldNotReject() {
|
|
1446
|
+
// Diagnostic-Code is free text (may legitimately wrap): folded to one line,
|
|
1447
|
+
// NUL stripped — not rejected.
|
|
1448
|
+
var raw = b.mailBounce.dsn.build({
|
|
1449
|
+
finalRecipient: "u@e.com", action: "failed", status: "5.1.1",
|
|
1450
|
+
diagnosticCode: "smtp; 550 line1\r\nline2" + String.fromCharCode(0) + "z",
|
|
1451
|
+
});
|
|
1452
|
+
check("dsn build: folded diagnosticCode cannot start a new header",
|
|
1453
|
+
!/^line2/m.test(raw) && !/^X-/m.test(raw));
|
|
1454
|
+
check("dsn build: NUL stripped from diagnosticCode", raw.indexOf(String.fromCharCode(0)) === -1);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
695
1457
|
async function run() {
|
|
696
1458
|
await testSurface();
|
|
697
1459
|
await testParsePostmarkBounce();
|
|
@@ -729,6 +1491,54 @@ async function run() {
|
|
|
729
1491
|
await testDsnBuildRejectsBadStatus();
|
|
730
1492
|
await testDsnBuildRejectsMissingRecipient();
|
|
731
1493
|
await testDsnBuildHeadersOnly();
|
|
1494
|
+
|
|
1495
|
+
await testPostmarkEmptyPayload();
|
|
1496
|
+
await testPostmarkMissingEmail();
|
|
1497
|
+
await testPostmarkDeliveryDefaults();
|
|
1498
|
+
await testPostmarkSpamDescriptionFallback();
|
|
1499
|
+
await testPostmarkBounceTypeFallbacks();
|
|
1500
|
+
await testPostmarkUnknownRecord();
|
|
1501
|
+
|
|
1502
|
+
await testSesEmptyPayload();
|
|
1503
|
+
await testSesBadSnsMessageJson();
|
|
1504
|
+
await testSesSnsNullMessageTypedError();
|
|
1505
|
+
await testHandlerSesNullMessageNoLeak();
|
|
1506
|
+
await testSesMissingNotificationType();
|
|
1507
|
+
await testSesEventTypeAliasAndSoftBounce();
|
|
1508
|
+
await testSesBounceUndeterminedAndFallbacks();
|
|
1509
|
+
await testSesBounceSubTypeReason();
|
|
1510
|
+
await testSesComplaintDefaultsAndDelivery();
|
|
1511
|
+
await testSesUnknownNotificationType();
|
|
1512
|
+
|
|
1513
|
+
await testResendEmptyAndMissingType();
|
|
1514
|
+
await testResendBounceFallbacks();
|
|
1515
|
+
await testResendUnknownType();
|
|
1516
|
+
|
|
1517
|
+
await testCustomParserValidation();
|
|
1518
|
+
await testParseRoutingDefaults();
|
|
1519
|
+
|
|
1520
|
+
await testHandlerConfigValidation();
|
|
1521
|
+
await testHandlerCustomParserPath();
|
|
1522
|
+
await testHandlerCustomParserBadShape();
|
|
1523
|
+
await testHandlerParseFailSurfacesCode();
|
|
1524
|
+
await testHandlerVerifyThrows();
|
|
1525
|
+
await testHandlerReqError();
|
|
1526
|
+
await testHandlerMultiChunkOverflowAbort();
|
|
1527
|
+
await testHandlerNoWriteHeadResNoCrash();
|
|
1528
|
+
await testHandlerAuditOnDeliverySuccess();
|
|
1529
|
+
|
|
1530
|
+
await testDsnParseInputGuards();
|
|
1531
|
+
await testDsnParseStructureGuards();
|
|
1532
|
+
await testDsnParseRecipientGuards();
|
|
1533
|
+
await testDsnParseActionBranches();
|
|
1534
|
+
await testDsnParseReasonAndSkipBranches();
|
|
1535
|
+
|
|
1536
|
+
await testDsnBuildInputGuards();
|
|
1537
|
+
await testDsnBuildDefaultAction();
|
|
1538
|
+
await testDsnBuildAllOptionalFields();
|
|
1539
|
+
await testDsnBuildOriginalMessageShapes();
|
|
1540
|
+
await testDsnBuildHeaderInjectionGuards();
|
|
1541
|
+
await testDsnBuildDiagnosticFoldNotReject();
|
|
732
1542
|
}
|
|
733
1543
|
|
|
734
1544
|
module.exports = { run: run };
|