@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
|
@@ -0,0 +1,1169 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* body-parser middleware — content-type dispatch, size caps, charset,
|
|
6
|
+
* malformed JSON / urlencoded / multipart, request-smuggling defense,
|
|
7
|
+
* per-sub-parser option defaults, and the 400 / 413 / 415 / 500 error
|
|
8
|
+
* branches.
|
|
9
|
+
*
|
|
10
|
+
* Drives the real `b.middleware.bodyParser(opts)` consumer path with
|
|
11
|
+
* EventEmitter-backed request streams (via the shared bodyReq/bodyRes
|
|
12
|
+
* mocks) so every assertion runs the shipped middleware, not an
|
|
13
|
+
* internal helper in isolation. The standalone `b.parsers.{json,
|
|
14
|
+
* multipart}` surface (the same pipelines, exposed for lazy-parse
|
|
15
|
+
* handlers) is exercised alongside it.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
var EventEmitter = require("events").EventEmitter;
|
|
19
|
+
var helpers = require("../helpers");
|
|
20
|
+
var b = helpers.b;
|
|
21
|
+
var check = helpers.check;
|
|
22
|
+
var fs = helpers.fs;
|
|
23
|
+
var os = helpers.os;
|
|
24
|
+
var path = helpers.path;
|
|
25
|
+
var _bodyRes = helpers._bodyRes;
|
|
26
|
+
var _bodyReq = helpers._bodyReq;
|
|
27
|
+
|
|
28
|
+
var BYTES = b.constants.BYTES;
|
|
29
|
+
var bodyParserLib = require("../../lib/middleware/body-parser.js");
|
|
30
|
+
|
|
31
|
+
// ---- shared fixtures ----
|
|
32
|
+
|
|
33
|
+
// A request that does NOT auto-emit data/end — the test controls the
|
|
34
|
+
// stream so it can synthesize truncation, a mid-stream error, or a
|
|
35
|
+
// deferred single chunk. Mirrors the shape lib/testing.bodyReq builds
|
|
36
|
+
// but hands emission control to the caller.
|
|
37
|
+
function _manualReq(method, headers) {
|
|
38
|
+
var req = new EventEmitter();
|
|
39
|
+
req.method = method || "POST";
|
|
40
|
+
req.url = "/";
|
|
41
|
+
req.headers = Object.assign({}, headers || {});
|
|
42
|
+
req.socket = { remoteAddress: "127.0.0.1" };
|
|
43
|
+
req._destroyed = false;
|
|
44
|
+
req.destroy = function () { req._destroyed = true; };
|
|
45
|
+
return req;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Drive the middleware to a single settled outcome: either it called
|
|
49
|
+
// next() (body parsed / skipped) or it wrote a terminal error response
|
|
50
|
+
// (finish fired). Wrapped in withTestTimeout so a parser that never
|
|
51
|
+
// settles surfaces as a hard failure rather than hanging the suite.
|
|
52
|
+
function _run(opts, req) {
|
|
53
|
+
var bp = b.middleware.bodyParser(opts);
|
|
54
|
+
var res = _bodyRes();
|
|
55
|
+
return helpers.withTestTimeout("body-parser: middleware settles", function () {
|
|
56
|
+
return new Promise(function (resolve) {
|
|
57
|
+
var settled = false;
|
|
58
|
+
function settle(v) {
|
|
59
|
+
if (settled) return;
|
|
60
|
+
settled = true;
|
|
61
|
+
resolve(Object.assign({ req: req, res: res }, v));
|
|
62
|
+
}
|
|
63
|
+
res.on("finish", function () {
|
|
64
|
+
settle({
|
|
65
|
+
nexted: false,
|
|
66
|
+
status: res._endedStatus,
|
|
67
|
+
headers: res._headers,
|
|
68
|
+
body: res._captured,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
bp(req, res, function (err) {
|
|
72
|
+
settle({ nexted: true, err: err });
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}, { timeoutMs: 3000 }); // allow:raw-byte-literal — settle budget ms
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function _bodyJson(rv) {
|
|
79
|
+
try { return JSON.parse(String(rv.body || "")); }
|
|
80
|
+
catch (_e) { return {}; }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Build a multipart/form-data body. Each part is either
|
|
84
|
+
// { headers: { name: value }, body } or { headerBlock: "verbatim", body }
|
|
85
|
+
// (headerBlock lets a test inject an obs-fold / bad-header shape).
|
|
86
|
+
var CRLF = "\r\n";
|
|
87
|
+
function _mpBody(boundary, parts) {
|
|
88
|
+
var segs = [Buffer.from("--" + boundary + CRLF)];
|
|
89
|
+
parts.forEach(function (p, i) {
|
|
90
|
+
var hdr;
|
|
91
|
+
if (typeof p.headerBlock === "string") {
|
|
92
|
+
hdr = p.headerBlock + CRLF + CRLF;
|
|
93
|
+
} else {
|
|
94
|
+
hdr = "";
|
|
95
|
+
var hs = p.headers || {};
|
|
96
|
+
Object.keys(hs).forEach(function (h) { hdr += h + ": " + hs[h] + CRLF; });
|
|
97
|
+
hdr += CRLF;
|
|
98
|
+
}
|
|
99
|
+
segs.push(Buffer.from(hdr, "binary"));
|
|
100
|
+
segs.push(Buffer.isBuffer(p.body) ? p.body : Buffer.from(String(p.body)));
|
|
101
|
+
segs.push(Buffer.from(
|
|
102
|
+
i === parts.length - 1
|
|
103
|
+
? CRLF + "--" + boundary + "--" + CRLF
|
|
104
|
+
: CRLF + "--" + boundary + CRLF));
|
|
105
|
+
});
|
|
106
|
+
return Buffer.concat(segs);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function _mpHeaders(boundary, extra) {
|
|
110
|
+
return Object.assign({
|
|
111
|
+
"content-type": "multipart/form-data; boundary=" + boundary,
|
|
112
|
+
}, extra || {});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// A shared tmp dir for disk-mode multipart tests; removed in run()'s finally.
|
|
116
|
+
var TMP_ROOT = path.join(os.tmpdir(), "blamejs-bp-test-" + process.pid + "-" + Date.now());
|
|
117
|
+
|
|
118
|
+
// ---- content-type dispatch + option defaults ----
|
|
119
|
+
|
|
120
|
+
async function testNonBodyMethodSkips() {
|
|
121
|
+
var req = _bodyReq("GET", { "content-type": "application/json" }, '{"a":1}');
|
|
122
|
+
var rv = await _run({}, req);
|
|
123
|
+
check("GET (non-body-bearing) skips the parser via next()", rv.nexted === true);
|
|
124
|
+
check("GET leaves req.body untouched", req.body === undefined);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function testAlreadyParsedShortCircuits() {
|
|
128
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": "7" }, '{"a":1}');
|
|
129
|
+
req.body = { preParsed: true };
|
|
130
|
+
var rv = await _run({}, req);
|
|
131
|
+
check("a body already on req short-circuits to next()", rv.nexted === true);
|
|
132
|
+
check("pre-parsed req.body is not overwritten", req.body.preParsed === true);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function testContentLengthZeroIsNoBody() {
|
|
136
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": "0" }, "");
|
|
137
|
+
var rv = await _run({}, req);
|
|
138
|
+
check("Content-Length: 0 is treated as no body → next()", rv.nexted === true);
|
|
139
|
+
check("Content-Length: 0 leaves req.body undefined", req.body === undefined);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function testJsonHappyPath() {
|
|
143
|
+
var req = _bodyReq("POST", { "content-type": "application/json; charset=utf-8", "content-length": "13" }, '{"name":"al"}');
|
|
144
|
+
var rv = await _run({ json: {} }, req);
|
|
145
|
+
check("application/json parses to req.body object", rv.nexted === true && req.body && req.body.name === "al");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function testJsonParseHookTransforms() {
|
|
149
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": "7" }, '{"a":1}');
|
|
150
|
+
var rv = await _run({ json: { parseHook: function (parsed) { return { wrapped: parsed }; } } }, req);
|
|
151
|
+
check("json parseHook transform is applied to req.body",
|
|
152
|
+
rv.nexted === true && req.body && req.body.wrapped && req.body.wrapped.a === 1);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function testUrlencodedHappyAndArray() {
|
|
156
|
+
// Three repeated values so the accumulator crosses scalar → 2-array →
|
|
157
|
+
// array.push (exercises each repeat-key branch).
|
|
158
|
+
var body = "name=al&tag=a&tag=b&tag=c";
|
|
159
|
+
var req = _bodyReq("POST", { "content-type": "application/x-www-form-urlencoded", "content-length": String(body.length) }, body);
|
|
160
|
+
var rv = await _run({ urlencoded: {} }, req);
|
|
161
|
+
check("urlencoded single value parses to string", req.body && req.body.name === "al");
|
|
162
|
+
check("urlencoded repeated key collapses to array (3 values → push)",
|
|
163
|
+
Array.isArray(req.body.tag) && req.body.tag.join("") === "abc");
|
|
164
|
+
check("urlencoded consumer path reaches next()", rv.nexted === true);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function testTextParser() {
|
|
168
|
+
var req = _bodyReq("POST", { "content-type": "text/plain", "content-length": "5" }, "hello");
|
|
169
|
+
var rv = await _run({ text: {} }, req);
|
|
170
|
+
check("text/plain parses to a string body", rv.nexted === true && req.body === "hello");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function testRawParser() {
|
|
174
|
+
var payload = Buffer.from("binary\x00bytes");
|
|
175
|
+
var req = _bodyReq("POST", { "content-type": "application/octet-stream", "content-length": String(payload.length) }, payload);
|
|
176
|
+
var rv = await _run({ raw: {} }, req);
|
|
177
|
+
check("application/octet-stream parses to a Buffer body",
|
|
178
|
+
rv.nexted === true && Buffer.isBuffer(req.body) && req.body.equals(payload));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function testEmptyBodiesReturnDefaults() {
|
|
182
|
+
// Body-bearing (transfer-encoding present) but zero bytes delivered:
|
|
183
|
+
// JSON → undefined, urlencoded → {}.
|
|
184
|
+
var jr = _bodyReq("POST", { "content-type": "application/json", "transfer-encoding": "chunked" }, undefined);
|
|
185
|
+
var jrv = await _run({ json: {} }, jr);
|
|
186
|
+
check("empty JSON body yields req.body === undefined", jrv.nexted === true && jr.body === undefined);
|
|
187
|
+
|
|
188
|
+
var ur = _bodyReq("POST", { "content-type": "application/x-www-form-urlencoded", "transfer-encoding": "chunked" }, undefined);
|
|
189
|
+
var urv = await _run({ urlencoded: {} }, ur);
|
|
190
|
+
check("empty urlencoded body yields req.body === {}",
|
|
191
|
+
urv.nexted === true && ur.body && Object.keys(ur.body).length === 0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ---- 415 unsupported / disabled sub-parser ----
|
|
195
|
+
|
|
196
|
+
async function testUnsupportedContentType415() {
|
|
197
|
+
var req = _bodyReq("POST", { "content-type": "application/vnd.custom", "content-length": "3" }, "abc");
|
|
198
|
+
var rv = await _run({ json: {}, urlencoded: {}, multipart: false, text: false, raw: false }, req);
|
|
199
|
+
check("unmatched Content-Type with a body → 415", rv.nexted === false && rv.status === 415);
|
|
200
|
+
check("415 carries the unsupported-content-type code", _bodyJson(rv).code === "body-parser/unsupported-content-type");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function testDisabledSubParserFallsThroughTo415() {
|
|
204
|
+
// json disabled; a JSON body now has no enabled matcher → 415.
|
|
205
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": "7" }, '{"a":1}');
|
|
206
|
+
var rv = await _run({ json: false, urlencoded: {}, multipart: false, text: false, raw: false }, req);
|
|
207
|
+
check("disabling the json sub-parser routes its body to 415", rv.nexted === false && rv.status === 415);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ---- size caps: 413 / bad content-length: 400 ----
|
|
211
|
+
|
|
212
|
+
async function testContentLengthOverLimitPreflight413() {
|
|
213
|
+
// Content-Length declares more than the limit → reject before reading bytes.
|
|
214
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": "5000" }, "{}");
|
|
215
|
+
var rv = await _run({ json: { limit: BYTES.bytes(100) } }, req);
|
|
216
|
+
check("Content-Length over limit → 413 preflight", rv.nexted === false && rv.status === 413);
|
|
217
|
+
check("413 preflight carries the too-large code", _bodyJson(rv).code === "body-parser/too-large");
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function testStreamOverflow413() {
|
|
221
|
+
// No honest Content-Length (transfer-encoding) so the preflight can't
|
|
222
|
+
// catch it; the collectStream drain guard trips the 413 instead.
|
|
223
|
+
var big = Buffer.alloc(500, 0x61);
|
|
224
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "transfer-encoding": "chunked" }, big);
|
|
225
|
+
var rv = await _run({ json: { limit: BYTES.bytes(50) } }, req);
|
|
226
|
+
check("body exceeding limit with no Content-Length → 413 (drain guard)",
|
|
227
|
+
rv.nexted === false && rv.status === 413);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function testMalformedContentLength400() {
|
|
231
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": "12abc" }, '{"a":1}');
|
|
232
|
+
var rv = await _run({ json: {} }, req);
|
|
233
|
+
check("non-decimal Content-Length → 400", rv.nexted === false && rv.status === 400);
|
|
234
|
+
check("400 carries the bad-content-length code", _bodyJson(rv).code === "body-parser/bad-content-length");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ---- JSON adversarial ----
|
|
238
|
+
|
|
239
|
+
async function testJsonStrictRejectsScalar() {
|
|
240
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": "5" }, '"hi"');
|
|
241
|
+
var rv = await _run({ json: { strict: true } }, req);
|
|
242
|
+
check("strict JSON rejects a non-{/[ body with 400", rv.nexted === false && rv.status === 400);
|
|
243
|
+
check("strict rejection carries json-strict code", _bodyJson(rv).code === "body-parser/json-strict");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function testJsonNonStrictScalar() {
|
|
247
|
+
// strict:false lets a bare scalar through the JSON pipeline.
|
|
248
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": "3" }, "123");
|
|
249
|
+
var rv = await _run({ json: { strict: false } }, req);
|
|
250
|
+
check("non-strict JSON accepts a bare scalar body", rv.nexted === true && req.body === 123);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function testJsonMalformed400() {
|
|
254
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": "8" }, '{"a":1,,}');
|
|
255
|
+
var rv = await _run({ json: {} }, req);
|
|
256
|
+
check("malformed JSON → 400", rv.nexted === false && rv.status === 400);
|
|
257
|
+
check("malformed JSON carries json-malformed code", _bodyJson(rv).code === "body-parser/json-malformed");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function testJsonParseHookThrow400() {
|
|
261
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": "2" }, "{}");
|
|
262
|
+
var rv = await _run({ json: { parseHook: function () { throw new Error("nope"); } } }, req);
|
|
263
|
+
check("parseHook throw → 400 json-hook", rv.nexted === false && rv.status === 400 &&
|
|
264
|
+
_bodyJson(rv).code === "body-parser/json-hook");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ---- urlencoded adversarial ----
|
|
268
|
+
|
|
269
|
+
async function testUrlencodedPoisonedKey400() {
|
|
270
|
+
var body = "__proto__=x";
|
|
271
|
+
var req = _bodyReq("POST", { "content-type": "application/x-www-form-urlencoded", "content-length": String(body.length) }, body);
|
|
272
|
+
var rv = await _run({ urlencoded: {} }, req);
|
|
273
|
+
check("urlencoded __proto__ key → 400 (pollution defense)",
|
|
274
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/urlencoded-poisoned-key");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async function testUrlencodedArrayLimit413() {
|
|
278
|
+
var body = "tag=a&tag=b&tag=c";
|
|
279
|
+
var req = _bodyReq("POST", { "content-type": "application/x-www-form-urlencoded", "content-length": String(body.length) }, body);
|
|
280
|
+
var rv = await _run({ urlencoded: { arrayLimit: 2 } }, req);
|
|
281
|
+
check("urlencoded array over arrayLimit → 413",
|
|
282
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/urlencoded-array-too-large");
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function testUrlencodedTooManyFields413() {
|
|
286
|
+
var pieces = [];
|
|
287
|
+
for (var i = 0; i <= 1100; i++) pieces.push("f" + i + "=1"); // > arrayLimit(100) + headroom(1000)
|
|
288
|
+
var body = pieces.join("&");
|
|
289
|
+
var req = _bodyReq("POST", { "content-type": "application/x-www-form-urlencoded", "content-length": String(body.length) }, body);
|
|
290
|
+
var rv = await _run({ urlencoded: {} }, req);
|
|
291
|
+
check("urlencoded key-bomb over the field cap → 413",
|
|
292
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/urlencoded-too-many-fields");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ---- request smuggling (runs before _hasBody) ----
|
|
296
|
+
|
|
297
|
+
async function testSmugglingClTeConflict() {
|
|
298
|
+
var req = _manualReq("POST", { "content-type": "application/json", "content-length": "5", "transfer-encoding": "chunked" });
|
|
299
|
+
var rv = await _run({ json: {} }, req);
|
|
300
|
+
check("CL + TE present → 400 smuggling reject", rv.nexted === false && rv.status === 400);
|
|
301
|
+
check("CL+TE reject carries the te-cl-conflict code", _bodyJson(rv).code === "smuggling/te-cl-conflict");
|
|
302
|
+
check("smuggling reject sets Connection: close",
|
|
303
|
+
rv.headers && (rv.headers.Connection === "close" || rv.headers.connection === "close"));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function testSmugglingMultipleContentLength() {
|
|
307
|
+
var req = _manualReq("POST", { "content-type": "application/json", "content-length": "5, 6" });
|
|
308
|
+
var rv = await _run({ json: {} }, req);
|
|
309
|
+
check("multiple Content-Length values → 400 smuggling",
|
|
310
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "smuggling/multiple-content-length");
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async function testSmugglingTeNotChunked() {
|
|
314
|
+
var req = _manualReq("POST", { "content-type": "application/json", "transfer-encoding": "gzip" });
|
|
315
|
+
var rv = await _run({ json: {} }, req);
|
|
316
|
+
check("Transfer-Encoding not ending in chunked → 400 smuggling",
|
|
317
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "smuggling/te-not-chunked");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async function testSmugglingDuplicateChunked() {
|
|
321
|
+
var req = _manualReq("POST", { "content-type": "application/json", "transfer-encoding": "chunked, chunked" });
|
|
322
|
+
var rv = await _run({ json: {} }, req);
|
|
323
|
+
check("Transfer-Encoding: chunked, chunked → 400 smuggling",
|
|
324
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "smuggling/duplicate-chunked");
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ---- chunked malformed close hook (RFC 9112 §7.1) ----
|
|
328
|
+
|
|
329
|
+
function _runWithStreamError(headers, parserError) {
|
|
330
|
+
var bp = b.middleware.bodyParser();
|
|
331
|
+
var req = _manualReq("POST", headers);
|
|
332
|
+
var res = _bodyRes();
|
|
333
|
+
return helpers.withTestTimeout("body-parser: chunked-error settles", function () {
|
|
334
|
+
return new Promise(function (resolve) {
|
|
335
|
+
var settled = false;
|
|
336
|
+
function settle(v) { if (settled) return; settled = true; resolve(Object.assign({ req: req, res: res }, v)); }
|
|
337
|
+
res.on("finish", function () { settle({ nexted: false, status: res._endedStatus, headers: res._headers, body: res._captured }); });
|
|
338
|
+
bp(req, res, function () { settle({ nexted: true }); });
|
|
339
|
+
setImmediate(function () { req.emit("error", parserError); });
|
|
340
|
+
});
|
|
341
|
+
}, { timeoutMs: 2000 }); // allow:raw-byte-literal — settle budget ms
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async function testChunkedMalformedRefused() {
|
|
345
|
+
var err = new Error("Parse Error: Invalid character in chunk size header");
|
|
346
|
+
err.code = "HPE_INVALID_CHUNK_SIZE";
|
|
347
|
+
var rv = await _runWithStreamError(
|
|
348
|
+
{ "content-type": "application/json", "transfer-encoding": "chunked" }, err);
|
|
349
|
+
check("HPE_INVALID_CHUNK_SIZE → 400 + Connection: close + req.destroy()",
|
|
350
|
+
rv.nexted === false && rv.status === 400 && rv.req._destroyed === true &&
|
|
351
|
+
rv.headers && (rv.headers.Connection === "close" || rv.headers.connection === "close"));
|
|
352
|
+
check("chunked-malformed body carries the http/chunked-malformed code", _bodyJson(rv).code === "http/chunked-malformed");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function testChunkedExtensionOverflowRefused() {
|
|
356
|
+
var err = new Error("Parse Error: chunk extensions overflow");
|
|
357
|
+
err.code = "HPE_CHUNK_EXTENSIONS_OVERFLOW";
|
|
358
|
+
var rv = await _runWithStreamError(
|
|
359
|
+
{ "content-type": "application/json", "transfer-encoding": "chunked" }, err);
|
|
360
|
+
check("HPE_CHUNK_EXTENSIONS_OVERFLOW → 400 + connection torn down",
|
|
361
|
+
rv.nexted === false && rv.status === 400 && rv.req._destroyed === true);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async function testGenericStreamErrorClosesConnection() {
|
|
365
|
+
var err = new Error("read aborted");
|
|
366
|
+
err.code = "ECONNRESET";
|
|
367
|
+
var rv = await _runWithStreamError(
|
|
368
|
+
{ "content-type": "application/json", "content-length": "5" }, err);
|
|
369
|
+
check("non-chunked stream error still returns 400 + Connection: close",
|
|
370
|
+
rv.nexted === false && rv.status === 400 &&
|
|
371
|
+
rv.headers && (rv.headers.Connection === "close" || rv.headers.connection === "close"));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ---- multipart happy paths (disk + memory) ----
|
|
375
|
+
|
|
376
|
+
async function testMultipartDiskHappyPath() {
|
|
377
|
+
var bd = "BoUnDaRy01";
|
|
378
|
+
var body = _mpBody(bd, [
|
|
379
|
+
{ headers: { "Content-Disposition": 'form-data; name="title"' }, body: "hello world" },
|
|
380
|
+
{ headers: { "Content-Disposition": 'form-data; name="avatar"; filename="pic.png"', "Content-Type": "image/png" },
|
|
381
|
+
body: Buffer.from("PNGDATA") },
|
|
382
|
+
]);
|
|
383
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
384
|
+
var tmpDir = path.join(TMP_ROOT, "disk1");
|
|
385
|
+
var rv = await _run({ multipart: { tmpDir: tmpDir } }, req);
|
|
386
|
+
check("multipart disk: text field lands on req.body", rv.nexted === true && req.body && req.body.title === "hello world");
|
|
387
|
+
check("multipart disk: file lands on req.files with path + hash + size",
|
|
388
|
+
Array.isArray(req.files) && req.files.length === 1 &&
|
|
389
|
+
req.files[0].field === "avatar" && req.files[0].filename === "pic.png" &&
|
|
390
|
+
typeof req.files[0].path === "string" && req.files[0].buffer === null &&
|
|
391
|
+
req.files[0].size === 7 && typeof req.files[0].hash === "string" && req.files[0].hash.length === 128);
|
|
392
|
+
check("multipart disk: tmp file exists on disk before cleanup", fs.existsSync(req.files[0].path));
|
|
393
|
+
// Fire the response-close cleanup hook the middleware wired.
|
|
394
|
+
var savedPath = req.files[0].path;
|
|
395
|
+
rv.res.emit("close");
|
|
396
|
+
check("multipart disk: response-close cleanup unlinks the tmp file", !fs.existsSync(savedPath));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async function testMultipartMemoryStorage() {
|
|
400
|
+
var bd = "BoUnDaRy02";
|
|
401
|
+
var body = _mpBody(bd, [
|
|
402
|
+
{ headers: { "Content-Disposition": 'form-data; name="doc"; filename="a.bin"', "Content-Type": "application/octet-stream" },
|
|
403
|
+
body: Buffer.from("MEMBYTES") },
|
|
404
|
+
]);
|
|
405
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
406
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
407
|
+
check("multipart memory: file exposes buffer, no path",
|
|
408
|
+
rv.nexted === true && req.files.length === 1 &&
|
|
409
|
+
req.files[0].path === null && Buffer.isBuffer(req.files[0].buffer) &&
|
|
410
|
+
req.files[0].buffer.toString() === "MEMBYTES");
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async function testMultipartRepeatedFieldArray() {
|
|
414
|
+
var bd = "BoUnDaRy03";
|
|
415
|
+
var body = _mpBody(bd, [
|
|
416
|
+
{ headers: { "Content-Disposition": 'form-data; name="tag"' }, body: "x" },
|
|
417
|
+
{ headers: { "Content-Disposition": 'form-data; name="tag"' }, body: "y" },
|
|
418
|
+
{ headers: { "Content-Disposition": 'form-data; name="tag"' }, body: "z" },
|
|
419
|
+
]);
|
|
420
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
421
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
422
|
+
check("multipart repeated text field collapses to an array",
|
|
423
|
+
rv.nexted === true && Array.isArray(req.body.tag) &&
|
|
424
|
+
req.body.tag.join("") === "xyz");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function testMultipartRfc5987Filename() {
|
|
428
|
+
var bd = "BoUnDaRy04";
|
|
429
|
+
// filename* extended parameter (RFC 5987) takes precedence over legacy filename.
|
|
430
|
+
var body = _mpBody(bd, [
|
|
431
|
+
{ headers: {
|
|
432
|
+
"Content-Disposition": "form-data; name=\"f\"; filename=\"legacy.txt\"; filename*=UTF-8''na%C3%AFve.txt",
|
|
433
|
+
"Content-Type": "text/plain" },
|
|
434
|
+
body: "data" },
|
|
435
|
+
]);
|
|
436
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
437
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
438
|
+
check("multipart filename* (RFC 5987 utf-8) decodes + wins over legacy filename=",
|
|
439
|
+
rv.nexted === true && req.files.length === 1 && req.files[0].filename === "naïve.txt");
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function testMultipartFileNoContentType() {
|
|
443
|
+
var bd = "BoUnDaRy42";
|
|
444
|
+
// A file part with no Content-Type header defaults to application/octet-stream.
|
|
445
|
+
var body = _mpBody(bd, [
|
|
446
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="x.dat"' }, body: "bytes" },
|
|
447
|
+
]);
|
|
448
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
449
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
450
|
+
check("multipart file part with no Content-Type defaults mimeType to octet-stream",
|
|
451
|
+
rv.nexted === true && req.files.length === 1 && req.files[0].mimeType === "application/octet-stream");
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ---- multipart adversarial ----
|
|
455
|
+
|
|
456
|
+
async function testMultipartNoBoundary400() {
|
|
457
|
+
var req = _bodyReq("POST", { "content-type": "multipart/form-data", "content-length": "10" }, "----junk--");
|
|
458
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
459
|
+
check("multipart with no boundary parameter → 400",
|
|
460
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/multipart-no-boundary");
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async function testMultipartBadBoundary400() {
|
|
464
|
+
var req = _bodyReq("POST", { "content-type": 'multipart/form-data; boundary="has space"', "content-length": "10" }, "----junk--");
|
|
465
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
466
|
+
check("multipart boundary violating RFC 2046 grammar → 400",
|
|
467
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/multipart-bad-boundary");
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async function testMultipartPoisonedField400() {
|
|
471
|
+
var bd = "BoUnDaRy05";
|
|
472
|
+
var body = _mpBody(bd, [
|
|
473
|
+
{ headers: { "Content-Disposition": 'form-data; name="__proto__"' }, body: "x" },
|
|
474
|
+
]);
|
|
475
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
476
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
477
|
+
check("multipart __proto__ field name → 400 (pollution defense)",
|
|
478
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/multipart-poisoned-field");
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async function testMultipartBadDisposition400() {
|
|
482
|
+
var bd = "BoUnDaRy06";
|
|
483
|
+
var body = _mpBody(bd, [
|
|
484
|
+
{ headers: { "Content-Type": "text/plain" }, body: "x" }, // no Content-Disposition
|
|
485
|
+
]);
|
|
486
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
487
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
488
|
+
check("multipart part missing form-data disposition → 400",
|
|
489
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/multipart-bad-disposition");
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
async function testMultipartTraversalFilename400() {
|
|
493
|
+
var bd = "BoUnDaRy07";
|
|
494
|
+
// A filename that sanitizes to empty (pure traversal) is refused.
|
|
495
|
+
var body = _mpBody(bd, [
|
|
496
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename=".."', "Content-Type": "text/plain" }, body: "x" },
|
|
497
|
+
]);
|
|
498
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
499
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
500
|
+
check("multipart filename that doesn't survive sanitization → 400",
|
|
501
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/multipart-bad-filename");
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
async function testMultipartObsFoldHeader400() {
|
|
505
|
+
var bd = "BoUnDaRy08";
|
|
506
|
+
var body = _mpBody(bd, [
|
|
507
|
+
{ headerBlock: 'Content-Disposition: form-data; name="f"' + CRLF + " obs-fold-continuation", body: "x" },
|
|
508
|
+
]);
|
|
509
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
510
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
511
|
+
check("multipart obsolete line folding → 400",
|
|
512
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/multipart-obs-fold");
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
async function testMultipartBadHeaderValue400() {
|
|
516
|
+
var bd = "BoUnDaRy09";
|
|
517
|
+
var body = _mpBody(bd, [
|
|
518
|
+
{ headerBlock: 'Content-Disposition: form-data; name="f"' + CRLF + "X-Bad: a\x00b", body: "x" },
|
|
519
|
+
]);
|
|
520
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
521
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
522
|
+
check("multipart header value with a NUL byte → 400",
|
|
523
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/multipart-bad-header-value");
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function testMultipartMimeNotAllowed415() {
|
|
527
|
+
var bd = "BoUnDaRy10";
|
|
528
|
+
var body = _mpBody(bd, [
|
|
529
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="x.exe"', "Content-Type": "application/x-msdownload" }, body: "x" },
|
|
530
|
+
]);
|
|
531
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
532
|
+
var rv = await _run({ multipart: { storage: "memory", mimeAllowlist: ["image/png"] } }, req);
|
|
533
|
+
check("multipart file MIME not on the global allowlist → 415",
|
|
534
|
+
rv.nexted === false && rv.status === 415 && _bodyJson(rv).code === "body-parser/multipart-mime-not-allowed");
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
async function testMultipartPerFieldMime415() {
|
|
538
|
+
var bd = "BoUnDaRy11";
|
|
539
|
+
var body = _mpBody(bd, [
|
|
540
|
+
{ headers: { "Content-Disposition": 'form-data; name="avatar"; filename="x.gif"', "Content-Type": "image/gif" }, body: "x" },
|
|
541
|
+
]);
|
|
542
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
543
|
+
var rv = await _run({ multipart: { storage: "memory", fields: { avatar: { mimeTypes: ["image/png"] } } } }, req);
|
|
544
|
+
check("multipart per-field MIME allowlist rejects a non-listed type → 415",
|
|
545
|
+
rv.nexted === false && rv.status === 415 && _bodyJson(rv).code === "body-parser/multipart-mime-not-allowed");
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async function testMultipartTooManyFiles413() {
|
|
549
|
+
var bd = "BoUnDaRy12";
|
|
550
|
+
var body = _mpBody(bd, [
|
|
551
|
+
{ headers: { "Content-Disposition": 'form-data; name="a"; filename="1.bin"', "Content-Type": "application/octet-stream" }, body: "x" },
|
|
552
|
+
{ headers: { "Content-Disposition": 'form-data; name="b"; filename="2.bin"', "Content-Type": "application/octet-stream" }, body: "y" },
|
|
553
|
+
]);
|
|
554
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
555
|
+
var rv = await _run({ multipart: { storage: "memory", fileCount: 1 } }, req);
|
|
556
|
+
check("multipart over fileCount → 413",
|
|
557
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/multipart-too-many-files");
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
async function testMultipartTooManyFields413() {
|
|
561
|
+
var bd = "BoUnDaRy13";
|
|
562
|
+
var body = _mpBody(bd, [
|
|
563
|
+
{ headers: { "Content-Disposition": 'form-data; name="a"' }, body: "x" },
|
|
564
|
+
{ headers: { "Content-Disposition": 'form-data; name="b"' }, body: "y" },
|
|
565
|
+
]);
|
|
566
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
567
|
+
var rv = await _run({ multipart: { storage: "memory", fieldCount: 1 } }, req);
|
|
568
|
+
check("multipart over fieldCount → 413",
|
|
569
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/multipart-too-many-fields");
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async function testMultipartFileTooLarge413() {
|
|
573
|
+
var bd = "BoUnDaRy14";
|
|
574
|
+
var body = _mpBody(bd, [
|
|
575
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="big.bin"', "Content-Type": "application/octet-stream" },
|
|
576
|
+
body: Buffer.alloc(200, 0x41) },
|
|
577
|
+
]);
|
|
578
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
579
|
+
var rv = await _run({ multipart: { storage: "memory", fileSize: BYTES.bytes(50) } }, req);
|
|
580
|
+
check("multipart file over fileSize → 413",
|
|
581
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/multipart-file-too-large");
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async function testMultipartPerFieldMaxBytes413() {
|
|
585
|
+
var bd = "BoUnDaRy15";
|
|
586
|
+
var body = _mpBody(bd, [
|
|
587
|
+
{ headers: { "Content-Disposition": 'form-data; name="avatar"; filename="big.bin"', "Content-Type": "application/octet-stream" },
|
|
588
|
+
body: Buffer.alloc(200, 0x41) },
|
|
589
|
+
]);
|
|
590
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
591
|
+
var rv = await _run({ multipart: { storage: "memory", fields: { avatar: { maxBytes: BYTES.bytes(20) } } } }, req);
|
|
592
|
+
check("multipart per-field maxBytes over cap → 413 (per-field message)",
|
|
593
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/multipart-file-too-large");
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
async function testMultipartFieldTooLarge413() {
|
|
597
|
+
var bd = "BoUnDaRy16";
|
|
598
|
+
var body = _mpBody(bd, [
|
|
599
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"' }, body: Buffer.alloc(200, 0x41) },
|
|
600
|
+
]);
|
|
601
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
602
|
+
var rv = await _run({ multipart: { storage: "memory", fieldSize: BYTES.bytes(50) } }, req);
|
|
603
|
+
check("multipart text field over fieldSize → 413",
|
|
604
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/multipart-field-too-large");
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
async function testMultipartHeadersTooLarge413() {
|
|
608
|
+
var bd = "BoUnDaRy17";
|
|
609
|
+
// > 16 KiB of header bytes with no CRLFCRLF terminator.
|
|
610
|
+
var huge = "--" + bd + CRLF + "X-Pad: " + "A".repeat(BYTES.kib(17));
|
|
611
|
+
var req = _manualReq("POST", _mpHeaders(bd, { "content-length": String(huge.length) }));
|
|
612
|
+
var promise = _run({ multipart: { storage: "memory" } }, req);
|
|
613
|
+
setImmediate(function () { req.emit("data", Buffer.from(huge)); });
|
|
614
|
+
var rv = await promise;
|
|
615
|
+
check("multipart part headers over 16 KiB → 413",
|
|
616
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/multipart-headers-too-large");
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
async function testMultipartTotalTooLarge413() {
|
|
620
|
+
var bd = "BoUnDaRy18";
|
|
621
|
+
var body = _mpBody(bd, [
|
|
622
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"' }, body: Buffer.alloc(300, 0x41) },
|
|
623
|
+
]);
|
|
624
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
625
|
+
// fieldSize generous, totalSize small so the body-loop total guard trips.
|
|
626
|
+
var rv = await _run({ multipart: { storage: "memory", fieldSize: BYTES.mib(1), totalSize: BYTES.bytes(120) } }, req);
|
|
627
|
+
check("multipart total request over totalSize → 413",
|
|
628
|
+
rv.nexted === false && rv.status === 413 &&
|
|
629
|
+
(_bodyJson(rv).code === "body-parser/multipart-total-too-large" ||
|
|
630
|
+
_bodyJson(rv).code === "body-parser/multipart-too-large"));
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
async function testMultipartTruncated400() {
|
|
634
|
+
var bd = "BoUnDaRy19";
|
|
635
|
+
// A part with no closing --boundary-- then end.
|
|
636
|
+
var partial = Buffer.from("--" + bd + CRLF +
|
|
637
|
+
'Content-Disposition: form-data; name="f"' + CRLF + CRLF + "value");
|
|
638
|
+
var req = _manualReq("POST", _mpHeaders(bd, { "content-length": String(partial.length) }));
|
|
639
|
+
var promise = _run({ multipart: { storage: "memory" } }, req);
|
|
640
|
+
setImmediate(function () { req.emit("data", partial); req.emit("end"); });
|
|
641
|
+
var rv = await promise;
|
|
642
|
+
check("multipart stream ending before the final boundary → 400 truncated",
|
|
643
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/multipart-truncated");
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
async function testMultipartStreamError400() {
|
|
647
|
+
var bd = "BoUnDaRy20";
|
|
648
|
+
var req = _manualReq("POST", _mpHeaders(bd, { "content-length": "50" }));
|
|
649
|
+
var promise = _run({ multipart: { storage: "memory" } }, req);
|
|
650
|
+
setImmediate(function () {
|
|
651
|
+
req.emit("data", Buffer.from("--" + bd + CRLF));
|
|
652
|
+
req.emit("error", new Error("socket reset"));
|
|
653
|
+
});
|
|
654
|
+
var rv = await promise;
|
|
655
|
+
check("multipart underlying stream error → 400",
|
|
656
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/multipart-stream");
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// ---- multipart fileFilter ----
|
|
660
|
+
|
|
661
|
+
async function testMultipartFileFilterRejectFalse() {
|
|
662
|
+
var bd = "BoUnDaRy21";
|
|
663
|
+
var body = _mpBody(bd, [
|
|
664
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="reject.bin"', "Content-Type": "application/octet-stream" }, body: "skipme" },
|
|
665
|
+
{ headers: { "Content-Disposition": 'form-data; name="g"; filename="keep.bin"', "Content-Type": "application/octet-stream" }, body: "keepme" },
|
|
666
|
+
]);
|
|
667
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
668
|
+
var audit = b.testing.captureAudit();
|
|
669
|
+
var rv = await _run({ multipart: { storage: "memory", audit: audit,
|
|
670
|
+
fileFilter: function (part) { return part.field !== "f"; } } }, req);
|
|
671
|
+
check("fileFilter false rejects the part into req.filesRejected, keeps the other",
|
|
672
|
+
rv.nexted === true && req.files.length === 1 && req.files[0].field === "g" &&
|
|
673
|
+
req.filesRejected.length === 1 && req.filesRejected[0].field === "f" &&
|
|
674
|
+
req.filesRejected[0].code === "fileFilter");
|
|
675
|
+
check("fileFilter rejection emits the audit event",
|
|
676
|
+
audit.byAction("body-parser.multipart.file_rejected").length === 1);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
async function testMultipartFileFilterRejectObject() {
|
|
680
|
+
var bd = "BoUnDaRy22";
|
|
681
|
+
var body = _mpBody(bd, [
|
|
682
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="x.bin"', "Content-Type": "application/octet-stream" }, body: "x" },
|
|
683
|
+
]);
|
|
684
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
685
|
+
var rv = await _run({ multipart: { storage: "memory",
|
|
686
|
+
fileFilter: function () { return { reject: true, code: "too-risky", message: "nope" }; } } }, req);
|
|
687
|
+
check("fileFilter reject-object carries custom code + message into filesRejected",
|
|
688
|
+
rv.nexted === true && req.files.length === 0 && req.filesRejected.length === 1 &&
|
|
689
|
+
req.filesRejected[0].code === "too-risky" && req.filesRejected[0].message === "nope");
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
async function testMultipartFileFilterThrow500() {
|
|
693
|
+
var bd = "BoUnDaRy23";
|
|
694
|
+
var body = _mpBody(bd, [
|
|
695
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="x.bin"', "Content-Type": "application/octet-stream" }, body: "x" },
|
|
696
|
+
]);
|
|
697
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
698
|
+
var rv = await _run({ multipart: { storage: "memory",
|
|
699
|
+
fileFilter: function () { throw new Error("filter boom"); } } }, req);
|
|
700
|
+
check("fileFilter throw → 500 with a generic reason (no internal detail echoed)",
|
|
701
|
+
rv.nexted === false && rv.status === 500);
|
|
702
|
+
check("fileFilter-throw 500 body carries the generic status phrase, not the throw detail",
|
|
703
|
+
String(rv.body).indexOf("filter boom") === -1 && _bodyJson(rv).error === "Internal Server Error");
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
async function testMultipartTmpDirFailure500() {
|
|
707
|
+
var bd = "BoUnDaRy24";
|
|
708
|
+
// tmpDir whose parent is a regular file → ensureDir throws ENOTDIR → 500.
|
|
709
|
+
var blocker = path.join(TMP_ROOT, "blocker-file");
|
|
710
|
+
fs.mkdirSync(TMP_ROOT, { recursive: true });
|
|
711
|
+
fs.writeFileSync(blocker, "x");
|
|
712
|
+
var body = _mpBody(bd, [
|
|
713
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="x.bin"', "Content-Type": "application/octet-stream" }, body: "x" },
|
|
714
|
+
]);
|
|
715
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
716
|
+
var rv = await _run({ multipart: { storage: "disk", tmpDir: path.join(blocker, "sub") } }, req);
|
|
717
|
+
check("multipart tmp-dir creation failure → 500", rv.nexted === false && rv.status === 500);
|
|
718
|
+
check("tmp-dir 500 does not echo the internal fs path to the client",
|
|
719
|
+
String(rv.body).indexOf("blocker-file") === -1 && _bodyJson(rv).error === "Internal Server Error");
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// ---- keepRawBody path ----
|
|
723
|
+
|
|
724
|
+
async function testKeepRawBodyStashesBytes() {
|
|
725
|
+
var raw = '{"hook":true}';
|
|
726
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": String(raw.length) }, raw);
|
|
727
|
+
var rv = await _run({ keepRawBody: true, json: {} }, req);
|
|
728
|
+
check("keepRawBody stashes the raw bytes AND parses req.body",
|
|
729
|
+
rv.nexted === true && Buffer.isBuffer(req.bodyRaw) && req.bodyRaw.toString() === raw &&
|
|
730
|
+
req.body && req.body.hook === true);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
async function testKeepRawBodyEmpty() {
|
|
734
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "transfer-encoding": "chunked" }, undefined);
|
|
735
|
+
var rv = await _run({ keepRawBody: true, json: {} }, req);
|
|
736
|
+
check("keepRawBody with an empty body → req.body undefined, empty bodyRaw",
|
|
737
|
+
rv.nexted === true && req.body === undefined && Buffer.isBuffer(req.bodyRaw) && req.bodyRaw.length === 0);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
async function testKeepRawBodyStrictReject() {
|
|
741
|
+
var raw = '"scalar"';
|
|
742
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": String(raw.length) }, raw);
|
|
743
|
+
var rv = await _run({ keepRawBody: true, json: { strict: true } }, req);
|
|
744
|
+
check("keepRawBody strict-mode scalar → 400 json-strict",
|
|
745
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/json-strict");
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
async function testKeepRawBodyHookThrow() {
|
|
749
|
+
var raw = "{}";
|
|
750
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": String(raw.length) }, raw);
|
|
751
|
+
var rv = await _run({ keepRawBody: true, json: { parseHook: function () { throw new Error("secret 5432"); } } }, req);
|
|
752
|
+
check("keepRawBody parseHook throw → 400 json-hook, no detail echoed",
|
|
753
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/json-hook" &&
|
|
754
|
+
String(rv.body).indexOf("5432") === -1);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// ---- config-time validation ----
|
|
758
|
+
|
|
759
|
+
function testCreateRejectsUnknownOpt() {
|
|
760
|
+
var threw = false;
|
|
761
|
+
try { b.middleware.bodyParser({ nope: true }); }
|
|
762
|
+
catch (_e) { threw = true; }
|
|
763
|
+
check("bodyParser({ unknownOpt }) throws at construction", threw === true);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
function testCreateRejectsBadStorage() {
|
|
767
|
+
var threw = false;
|
|
768
|
+
try { b.middleware.bodyParser({ multipart: { storage: "s3" } }); }
|
|
769
|
+
catch (e) { threw = e instanceof TypeError; }
|
|
770
|
+
check("multipart.storage other than disk/memory throws TypeError", threw === true);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// ---- raw() convenience factory ----
|
|
774
|
+
|
|
775
|
+
async function testRawFactoryCapturesAnyType() {
|
|
776
|
+
var payload = Buffer.from("webhook-bytes");
|
|
777
|
+
var req = _bodyReq("POST", { "content-type": "application/x-weird", "content-length": String(payload.length) }, payload);
|
|
778
|
+
var bp = b.middleware.bodyParser.raw({ limit: BYTES.mib(1) });
|
|
779
|
+
var res = _bodyRes();
|
|
780
|
+
var rv = await helpers.withTestTimeout("body-parser: raw factory settles", function () {
|
|
781
|
+
return new Promise(function (resolve) {
|
|
782
|
+
var settled = false;
|
|
783
|
+
function settle(v) { if (settled) return; settled = true; resolve(v); }
|
|
784
|
+
res.on("finish", function () { settle({ nexted: false, status: res._endedStatus }); });
|
|
785
|
+
bp(req, res, function () { settle({ nexted: true }); });
|
|
786
|
+
});
|
|
787
|
+
}, { timeoutMs: 3000 }); // allow:raw-byte-literal — settle budget ms
|
|
788
|
+
check("bodyParser.raw() captures any Content-Type as a Buffer body",
|
|
789
|
+
rv.nexted === true && Buffer.isBuffer(req.body) && req.body.equals(payload));
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// ---- standalone b.parsers.{json,multipart} ----
|
|
793
|
+
|
|
794
|
+
async function testStandaloneJsonHappy() {
|
|
795
|
+
var raw = '{"ok":1}';
|
|
796
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": String(raw.length) }, raw);
|
|
797
|
+
var parsed = await b.parsers.json(req, {});
|
|
798
|
+
check("b.parsers.json parses a request body directly", parsed && parsed.ok === 1);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
async function testStandaloneJsonBadMaxBytes() {
|
|
802
|
+
var threw = false;
|
|
803
|
+
var req = _bodyReq("POST", { "content-type": "application/json" }, "{}");
|
|
804
|
+
try { await b.parsers.json(req, { maxBytes: -5 }); }
|
|
805
|
+
catch (e) { threw = e && e.code === "body-parser/bad-max-bytes"; }
|
|
806
|
+
check("b.parsers.json rejects a negative maxBytes at resolve", threw === true);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
async function testStandaloneMultipartNotMultipart() {
|
|
810
|
+
var threw = false;
|
|
811
|
+
var req = _bodyReq("POST", { "content-type": "application/json" }, "{}");
|
|
812
|
+
try { await b.parsers.multipart(req, {}); }
|
|
813
|
+
catch (e) { threw = e && e.code === "body-parser/standalone-not-multipart"; }
|
|
814
|
+
check("b.parsers.multipart on a non-multipart request throws", threw === true);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
async function testStandaloneMultipartBadMaxFiles() {
|
|
818
|
+
var bd = "BoUnDaRy30";
|
|
819
|
+
var threw = false;
|
|
820
|
+
var req = _bodyReq("POST", _mpHeaders(bd), "");
|
|
821
|
+
try { await b.parsers.multipart(req, { maxFiles: -1 }); }
|
|
822
|
+
catch (e) { threw = e && e.code === "body-parser/bad-max-files"; }
|
|
823
|
+
check("b.parsers.multipart rejects a negative maxFiles", threw === true);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
async function testStandaloneMultipartHappy() {
|
|
827
|
+
var bd = "BoUnDaRy31";
|
|
828
|
+
var body = _mpBody(bd, [
|
|
829
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="s.bin"', "Content-Type": "application/octet-stream" }, body: "standalone" },
|
|
830
|
+
]);
|
|
831
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
832
|
+
var result = await b.parsers.multipart(req, { tmpDir: path.join(TMP_ROOT, "standalone") });
|
|
833
|
+
check("b.parsers.multipart returns { fields, files, filesRejected }",
|
|
834
|
+
result && Array.isArray(result.files) && result.files.length === 1 && result.files[0].field === "f");
|
|
835
|
+
if (result.files[0].path) { try { fs.unlinkSync(result.files[0].path); } catch (_e) { /* already gone */ } }
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// ---- multipart RFC 5987 filename* charset edge cases ----
|
|
839
|
+
|
|
840
|
+
async function testMultipartRfc5987CharsetEdges() {
|
|
841
|
+
var bd = "BoUnDaRy32";
|
|
842
|
+
var body = _mpBody(bd, [
|
|
843
|
+
// iso-8859-1 opt-in: %e9 → é (Latin-1 byte maps directly to code point).
|
|
844
|
+
{ headers: { "Content-Disposition": "form-data; name=\"a\"; filename*=iso-8859-1''caf%e9.txt", "Content-Type": "text/plain" }, body: "1" },
|
|
845
|
+
// utf-8 ext-value that fails decodeURIComponent → dropped → legacy filename wins.
|
|
846
|
+
{ headers: { "Content-Disposition": "form-data; name=\"b\"; filename=\"fallback.txt\"; filename*=UTF-8''%C3", "Content-Type": "text/plain" }, body: "2" },
|
|
847
|
+
// charset neither utf-8 nor an opted-in one → dropped → legacy filename wins.
|
|
848
|
+
{ headers: { "Content-Disposition": "form-data; name=\"c\"; filename=\"legacy2.txt\"; filename*=Shift_JIS''x", "Content-Type": "text/plain" }, body: "3" },
|
|
849
|
+
]);
|
|
850
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
851
|
+
var rv = await _run({ multipart: { storage: "memory", filenameCharsets: ["iso-8859-1"] } }, req);
|
|
852
|
+
check("multipart filename* iso-8859-1 (opted in) percent-decodes to Latin-1",
|
|
853
|
+
rv.nexted === true && req.files.length === 3 && req.files[0].filename === "café.txt");
|
|
854
|
+
check("multipart filename* utf-8 malformed → falls back to legacy filename=",
|
|
855
|
+
req.files[1].filename === "fallback.txt");
|
|
856
|
+
check("multipart filename* unsupported charset → falls back to legacy filename=",
|
|
857
|
+
req.files[2].filename === "legacy2.txt");
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// ---- multipart preamble drop + chunk-split body (multi-chunk delivery) ----
|
|
861
|
+
|
|
862
|
+
async function testMultipartPreambleDrop() {
|
|
863
|
+
var bd = "BoUnDaRy33";
|
|
864
|
+
// A large preamble (> boundary.length + 100) with no boundary in the first
|
|
865
|
+
// chunk forces the INITIAL-state memory-bound drop before the real body.
|
|
866
|
+
var preamble = Buffer.from("P".repeat(200));
|
|
867
|
+
var rest = _mpBody(bd, [
|
|
868
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"' }, body: "hi" },
|
|
869
|
+
]);
|
|
870
|
+
var req = _manualReq("POST", _mpHeaders(bd, { "content-length": String(preamble.length + rest.length) }));
|
|
871
|
+
var promise = _run({ multipart: { storage: "memory" } }, req);
|
|
872
|
+
setImmediate(function () {
|
|
873
|
+
req.emit("data", preamble);
|
|
874
|
+
setImmediate(function () { req.emit("data", rest); req.emit("end"); });
|
|
875
|
+
});
|
|
876
|
+
var rv = await promise;
|
|
877
|
+
check("multipart preamble drop then real body parses the field",
|
|
878
|
+
rv.nexted === true && req.body && req.body.f === "hi");
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
async function testMultipartChunkSplitBody() {
|
|
882
|
+
var bd = "BoUnDaRy34";
|
|
883
|
+
// Deliver a field body split across two chunks so the MP_BODY emit-without-
|
|
884
|
+
// marker branch (emit all but the trailing look-ahead window) runs.
|
|
885
|
+
var chunk1 = Buffer.from("--" + bd + CRLF + 'Content-Disposition: form-data; name="f"' + CRLF + CRLF + "A".repeat(40));
|
|
886
|
+
var chunk2 = Buffer.from("B".repeat(10) + CRLF + "--" + bd + "--" + CRLF);
|
|
887
|
+
var req = _manualReq("POST", _mpHeaders(bd, { "content-length": String(chunk1.length + chunk2.length) }));
|
|
888
|
+
var promise = _run({ multipart: { storage: "memory" } }, req);
|
|
889
|
+
setImmediate(function () {
|
|
890
|
+
req.emit("data", chunk1);
|
|
891
|
+
setImmediate(function () { req.emit("data", chunk2); req.emit("end"); });
|
|
892
|
+
});
|
|
893
|
+
var rv = await promise;
|
|
894
|
+
check("multipart chunk-split field body reassembles across chunks",
|
|
895
|
+
rv.nexted === true && req.body.f === "A".repeat(40) + "B".repeat(10));
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// ---- multipart boundary-terminator tolerances ----
|
|
899
|
+
|
|
900
|
+
async function testMultipartBareLfTolerated() {
|
|
901
|
+
var bd = "BoUnDaRy35";
|
|
902
|
+
// Transport replaced the CRLF after the opening boundary with a bare LF.
|
|
903
|
+
var body = Buffer.from("--" + bd + "\n" +
|
|
904
|
+
'Content-Disposition: form-data; name="f"' + CRLF + CRLF + "hello" +
|
|
905
|
+
CRLF + "--" + bd + "--" + CRLF);
|
|
906
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
907
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
908
|
+
check("multipart tolerates a bare LF after the boundary", rv.nexted === true && req.body.f === "hello");
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
async function testMultipartGarbageAfterBoundary400() {
|
|
912
|
+
var bd = "BoUnDaRy36";
|
|
913
|
+
var body = Buffer.from("--" + bd + "XY"); // neither --, CRLF, nor LF after boundary
|
|
914
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
915
|
+
var rv = await _run({ multipart: { storage: "memory" } }, req);
|
|
916
|
+
check("multipart garbage after the boundary → 400 malformed",
|
|
917
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/multipart-malformed");
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// ---- multipart total-size guards (headers-phase + file-body-phase + discard) ----
|
|
921
|
+
|
|
922
|
+
async function testMultipartTotalTooLargeInHeaders413() {
|
|
923
|
+
var bd = "BoUnDaRy37";
|
|
924
|
+
var body = _mpBody(bd, [
|
|
925
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"' }, body: "x" },
|
|
926
|
+
]);
|
|
927
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
928
|
+
// totalSize smaller than the first part's header bytes → trip in MP_HEADERS.
|
|
929
|
+
var rv = await _run({ multipart: { storage: "memory", totalSize: BYTES.bytes(10) } }, req);
|
|
930
|
+
check("multipart header bytes alone over totalSize → 413 (headers-phase guard)",
|
|
931
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/multipart-too-large");
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
async function testMultipartFileBodyTotalTooLarge413() {
|
|
935
|
+
var bd = "BoUnDaRy38";
|
|
936
|
+
var body = _mpBody(bd, [
|
|
937
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="x.bin"', "Content-Type": "application/octet-stream" },
|
|
938
|
+
body: Buffer.alloc(200, 0x41) },
|
|
939
|
+
]);
|
|
940
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
941
|
+
// Per-file cap generous, request total small → trip on the file-body total guard.
|
|
942
|
+
var rv = await _run({ multipart: { storage: "memory", fileSize: BYTES.mib(1), totalSize: BYTES.bytes(150) } }, req);
|
|
943
|
+
check("multipart file body over totalSize (per-file cap generous) → 413 total",
|
|
944
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/multipart-total-too-large");
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
async function testMultipartDiscardTotalTooLarge413() {
|
|
948
|
+
var bd = "BoUnDaRy39";
|
|
949
|
+
var body = _mpBody(bd, [
|
|
950
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="x.bin"', "Content-Type": "application/octet-stream" },
|
|
951
|
+
body: Buffer.alloc(200, 0x41) },
|
|
952
|
+
]);
|
|
953
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
954
|
+
// fileFilter discards the part; the discard path still counts bytes toward
|
|
955
|
+
// totalSize as a per-request DoS guard.
|
|
956
|
+
var rv = await _run({ multipart: { storage: "memory", totalSize: BYTES.bytes(150),
|
|
957
|
+
fileFilter: function () { return false; } } }, req);
|
|
958
|
+
check("multipart discarded-part bytes over totalSize → 413 total",
|
|
959
|
+
rv.nexted === false && rv.status === 413 && _bodyJson(rv).code === "body-parser/multipart-total-too-large");
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// ---- keepRawBody malformed JSON ----
|
|
963
|
+
|
|
964
|
+
async function testKeepRawBodyMalformedJson() {
|
|
965
|
+
var raw = "{bad json";
|
|
966
|
+
var req = _bodyReq("POST", { "content-type": "application/json", "content-length": String(raw.length) }, raw);
|
|
967
|
+
var rv = await _run({ keepRawBody: true, json: {} }, req);
|
|
968
|
+
check("keepRawBody malformed JSON → 400 json-malformed (raw-buf pipeline)",
|
|
969
|
+
rv.nexted === false && rv.status === 400 && _bodyJson(rv).code === "body-parser/json-malformed");
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// ---- standalone multipart maxBytes clamp ----
|
|
973
|
+
|
|
974
|
+
async function testStandaloneMultipartMaxBytesClamp() {
|
|
975
|
+
var bd = "BoUnDaRy41";
|
|
976
|
+
var body = _mpBody(bd, [
|
|
977
|
+
{ headers: { "Content-Disposition": 'form-data; name="f"; filename="s.bin"', "Content-Type": "application/octet-stream" }, body: "tiny" },
|
|
978
|
+
]);
|
|
979
|
+
var req = _bodyReq("POST", _mpHeaders(bd, { "content-length": String(body.length) }), body);
|
|
980
|
+
// maxBytes below the default fileSize clamps the per-file cap down to it.
|
|
981
|
+
var result = await b.parsers.multipart(req, { maxBytes: BYTES.bytes(500), tmpDir: path.join(TMP_ROOT, "clamp") });
|
|
982
|
+
check("b.parsers.multipart maxBytes clamps totalSize + fileSize and still parses",
|
|
983
|
+
result && result.files.length === 1 && result.files[0].size === 4);
|
|
984
|
+
if (result.files[0].path) { try { fs.unlinkSync(result.files[0].path); } catch (_e) { /* already gone */ } }
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// ---- exported pure helpers ----
|
|
988
|
+
|
|
989
|
+
function testExportedContentType() {
|
|
990
|
+
var ct = bodyParserLib._contentType({ headers: { "content-type": 'multipart/form-data; boundary="a;b"; charset=utf-8' } });
|
|
991
|
+
check("_contentType lowercases the type", ct.type === "multipart/form-data");
|
|
992
|
+
check("_contentType quote-aware splitter keeps a quoted boundary intact", ct.params.boundary === "a;b");
|
|
993
|
+
check("_contentType parses a second parameter", ct.params.charset === "utf-8");
|
|
994
|
+
var none = bodyParserLib._contentType({ headers: {} });
|
|
995
|
+
check("_contentType with no header → empty type + params", none.type === "" && Object.keys(none.params).length === 0);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function testExportedContentTypeDropsPoisonedParam() {
|
|
999
|
+
var ct = bodyParserLib._contentType({ headers: { "content-type": "text/plain; __proto__=x; charset=utf-8" } });
|
|
1000
|
+
check("_contentType drops a poisoned parameter name", !("__proto__" in ct.params) && ct.params.charset === "utf-8");
|
|
1001
|
+
check("_contentType params map carries no prototype chain", Object.getPrototypeOf(ct.params) === null);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function testExportedHasBody() {
|
|
1005
|
+
check("_hasBody false for GET", bodyParserLib._hasBody({ method: "GET", headers: {} }) === false);
|
|
1006
|
+
check("_hasBody false for POST Content-Length 0",
|
|
1007
|
+
bodyParserLib._hasBody({ method: "POST", headers: { "content-length": "0" } }) === false);
|
|
1008
|
+
check("_hasBody true for POST with a positive Content-Length",
|
|
1009
|
+
bodyParserLib._hasBody({ method: "POST", headers: { "content-length": "5" } }) === true);
|
|
1010
|
+
check("_hasBody true for POST with Transfer-Encoding",
|
|
1011
|
+
bodyParserLib._hasBody({ method: "POST", headers: { "transfer-encoding": "chunked" } }) === true);
|
|
1012
|
+
check("_hasBody true for a malformed Content-Length (routes to the 400 path)",
|
|
1013
|
+
bodyParserLib._hasBody({ method: "POST", headers: { "content-length": "abc" } }) === true);
|
|
1014
|
+
check("_hasBody false for a body-bearing method with neither CL nor TE",
|
|
1015
|
+
bodyParserLib._hasBody({ method: "POST", headers: {} }) === false);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
function testExportedSanitizeFilename() {
|
|
1019
|
+
check("_sanitizeFilename strips POSIX path components",
|
|
1020
|
+
bodyParserLib._sanitizeFilename("/etc/passwd") === "passwd");
|
|
1021
|
+
check("_sanitizeFilename strips Windows path components",
|
|
1022
|
+
bodyParserLib._sanitizeFilename("C:\\Users\\x\\report.pdf") === "report.pdf");
|
|
1023
|
+
check("_sanitizeFilename strips BiDi override codepoints",
|
|
1024
|
+
bodyParserLib._sanitizeFilename("Photogpj.png") === "Photogpj.png");
|
|
1025
|
+
check("_sanitizeFilename strips leading/trailing dots",
|
|
1026
|
+
bodyParserLib._sanitizeFilename("...name...") === "name");
|
|
1027
|
+
check("_sanitizeFilename returns null for a pure-traversal name",
|
|
1028
|
+
bodyParserLib._sanitizeFilename("..") === null);
|
|
1029
|
+
check("_sanitizeFilename returns null for non-string input",
|
|
1030
|
+
bodyParserLib._sanitizeFilename(42) === null);
|
|
1031
|
+
check("_sanitizeFilename caps length at 255",
|
|
1032
|
+
bodyParserLib._sanitizeFilename("a".repeat(400)).length === 255);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
function testPoisonedKeysExport() {
|
|
1036
|
+
check("POISONED_KEYS export is a Set including __proto__",
|
|
1037
|
+
bodyParserLib.POISONED_KEYS instanceof Set && bodyParserLib.POISONED_KEYS.has("__proto__"));
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// ---- runner ----
|
|
1041
|
+
|
|
1042
|
+
async function run() {
|
|
1043
|
+
try {
|
|
1044
|
+
// dispatch + defaults
|
|
1045
|
+
await testNonBodyMethodSkips();
|
|
1046
|
+
await testAlreadyParsedShortCircuits();
|
|
1047
|
+
await testContentLengthZeroIsNoBody();
|
|
1048
|
+
await testJsonHappyPath();
|
|
1049
|
+
await testJsonParseHookTransforms();
|
|
1050
|
+
await testUrlencodedHappyAndArray();
|
|
1051
|
+
await testTextParser();
|
|
1052
|
+
await testRawParser();
|
|
1053
|
+
await testEmptyBodiesReturnDefaults();
|
|
1054
|
+
|
|
1055
|
+
// 415 / disabled
|
|
1056
|
+
await testUnsupportedContentType415();
|
|
1057
|
+
await testDisabledSubParserFallsThroughTo415();
|
|
1058
|
+
|
|
1059
|
+
// size caps + bad content-length
|
|
1060
|
+
await testContentLengthOverLimitPreflight413();
|
|
1061
|
+
await testStreamOverflow413();
|
|
1062
|
+
await testMalformedContentLength400();
|
|
1063
|
+
|
|
1064
|
+
// JSON adversarial
|
|
1065
|
+
await testJsonStrictRejectsScalar();
|
|
1066
|
+
await testJsonNonStrictScalar();
|
|
1067
|
+
await testJsonMalformed400();
|
|
1068
|
+
await testJsonParseHookThrow400();
|
|
1069
|
+
|
|
1070
|
+
// urlencoded adversarial
|
|
1071
|
+
await testUrlencodedPoisonedKey400();
|
|
1072
|
+
await testUrlencodedArrayLimit413();
|
|
1073
|
+
await testUrlencodedTooManyFields413();
|
|
1074
|
+
|
|
1075
|
+
// smuggling
|
|
1076
|
+
await testSmugglingClTeConflict();
|
|
1077
|
+
await testSmugglingMultipleContentLength();
|
|
1078
|
+
await testSmugglingTeNotChunked();
|
|
1079
|
+
await testSmugglingDuplicateChunked();
|
|
1080
|
+
|
|
1081
|
+
// chunked malformed close hook
|
|
1082
|
+
await testChunkedMalformedRefused();
|
|
1083
|
+
await testChunkedExtensionOverflowRefused();
|
|
1084
|
+
await testGenericStreamErrorClosesConnection();
|
|
1085
|
+
|
|
1086
|
+
// multipart happy
|
|
1087
|
+
await testMultipartDiskHappyPath();
|
|
1088
|
+
await testMultipartMemoryStorage();
|
|
1089
|
+
await testMultipartRepeatedFieldArray();
|
|
1090
|
+
await testMultipartRfc5987Filename();
|
|
1091
|
+
await testMultipartRfc5987CharsetEdges();
|
|
1092
|
+
await testMultipartPreambleDrop();
|
|
1093
|
+
await testMultipartChunkSplitBody();
|
|
1094
|
+
await testMultipartBareLfTolerated();
|
|
1095
|
+
await testMultipartFileNoContentType();
|
|
1096
|
+
|
|
1097
|
+
// multipart adversarial
|
|
1098
|
+
await testMultipartNoBoundary400();
|
|
1099
|
+
await testMultipartBadBoundary400();
|
|
1100
|
+
await testMultipartPoisonedField400();
|
|
1101
|
+
await testMultipartBadDisposition400();
|
|
1102
|
+
await testMultipartTraversalFilename400();
|
|
1103
|
+
await testMultipartObsFoldHeader400();
|
|
1104
|
+
await testMultipartBadHeaderValue400();
|
|
1105
|
+
await testMultipartMimeNotAllowed415();
|
|
1106
|
+
await testMultipartPerFieldMime415();
|
|
1107
|
+
await testMultipartTooManyFiles413();
|
|
1108
|
+
await testMultipartTooManyFields413();
|
|
1109
|
+
await testMultipartFileTooLarge413();
|
|
1110
|
+
await testMultipartPerFieldMaxBytes413();
|
|
1111
|
+
await testMultipartFieldTooLarge413();
|
|
1112
|
+
await testMultipartHeadersTooLarge413();
|
|
1113
|
+
await testMultipartTotalTooLarge413();
|
|
1114
|
+
await testMultipartTotalTooLargeInHeaders413();
|
|
1115
|
+
await testMultipartFileBodyTotalTooLarge413();
|
|
1116
|
+
await testMultipartGarbageAfterBoundary400();
|
|
1117
|
+
await testMultipartTruncated400();
|
|
1118
|
+
await testMultipartStreamError400();
|
|
1119
|
+
|
|
1120
|
+
// fileFilter
|
|
1121
|
+
await testMultipartFileFilterRejectFalse();
|
|
1122
|
+
await testMultipartFileFilterRejectObject();
|
|
1123
|
+
await testMultipartFileFilterThrow500();
|
|
1124
|
+
await testMultipartTmpDirFailure500();
|
|
1125
|
+
await testMultipartDiscardTotalTooLarge413();
|
|
1126
|
+
|
|
1127
|
+
// keepRawBody
|
|
1128
|
+
await testKeepRawBodyStashesBytes();
|
|
1129
|
+
await testKeepRawBodyEmpty();
|
|
1130
|
+
await testKeepRawBodyStrictReject();
|
|
1131
|
+
await testKeepRawBodyHookThrow();
|
|
1132
|
+
await testKeepRawBodyMalformedJson();
|
|
1133
|
+
|
|
1134
|
+
// config-time validation
|
|
1135
|
+
testCreateRejectsUnknownOpt();
|
|
1136
|
+
testCreateRejectsBadStorage();
|
|
1137
|
+
|
|
1138
|
+
// raw factory
|
|
1139
|
+
await testRawFactoryCapturesAnyType();
|
|
1140
|
+
|
|
1141
|
+
// standalone parsers
|
|
1142
|
+
await testStandaloneJsonHappy();
|
|
1143
|
+
await testStandaloneJsonBadMaxBytes();
|
|
1144
|
+
await testStandaloneMultipartNotMultipart();
|
|
1145
|
+
await testStandaloneMultipartBadMaxFiles();
|
|
1146
|
+
await testStandaloneMultipartHappy();
|
|
1147
|
+
await testStandaloneMultipartMaxBytesClamp();
|
|
1148
|
+
|
|
1149
|
+
// exported pure helpers
|
|
1150
|
+
testExportedContentType();
|
|
1151
|
+
testExportedContentTypeDropsPoisonedParam();
|
|
1152
|
+
testExportedHasBody();
|
|
1153
|
+
testExportedSanitizeFilename();
|
|
1154
|
+
testPoisonedKeysExport();
|
|
1155
|
+
} finally {
|
|
1156
|
+
// The 4xx/5xx/chunked rejects emit on the real audit chain, scheduling
|
|
1157
|
+
// its age-flush timer; drain it so no timer lingers past run().
|
|
1158
|
+
await b.audit.flush();
|
|
1159
|
+
// Remove the disk-mode multipart tmp root.
|
|
1160
|
+
try { fs.rmSync(TMP_ROOT, { recursive: true, force: true }); } catch (_e) { /* best-effort */ }
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
module.exports = { run: run };
|
|
1165
|
+
|
|
1166
|
+
if (require.main === module) {
|
|
1167
|
+
run().then(function () { console.log("OK"); })
|
|
1168
|
+
.catch(function (e) { console.error(e.stack || e); process.exit(1); });
|
|
1169
|
+
}
|