@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
|
@@ -675,6 +675,894 @@ async function test304MergesHeaders() {
|
|
|
675
675
|
});
|
|
676
676
|
}
|
|
677
677
|
|
|
678
|
+
// ---- Cache-Control parsing edge cases -------------------------------
|
|
679
|
+
|
|
680
|
+
function testParseCacheControlBranches() {
|
|
681
|
+
var pcc = b.httpClient.cache._parseCacheControl;
|
|
682
|
+
|
|
683
|
+
// Non-string / empty input returns an empty directive set.
|
|
684
|
+
check("_parseCacheControl: empty string → {}",
|
|
685
|
+
Object.keys(pcc("")).length === 0);
|
|
686
|
+
check("_parseCacheControl: non-string → {}",
|
|
687
|
+
Object.keys(pcc(123)).length === 0);
|
|
688
|
+
|
|
689
|
+
// A quoted directive argument must NOT be sliced at the comma inside
|
|
690
|
+
// the quotes — RFC 9111 §5.2 / RFC 8941 §3.3.3 quote-aware split. A
|
|
691
|
+
// naive value.split(",") would emit a fake "Cookie" directive.
|
|
692
|
+
var quoted = pcc('no-cache="Authorization, Cookie", max-age=5');
|
|
693
|
+
check("_parseCacheControl: quoted comma not split into fake directive",
|
|
694
|
+
quoted["no-cache"] === "Authorization, Cookie" &&
|
|
695
|
+
quoted["max-age"] === "5" &&
|
|
696
|
+
quoted.cookie === undefined);
|
|
697
|
+
|
|
698
|
+
// Empty segments from a double comma are skipped.
|
|
699
|
+
var doubled = pcc("public, , max-age=60");
|
|
700
|
+
check("_parseCacheControl: empty segment skipped",
|
|
701
|
+
doubled.public === "" && doubled["max-age"] === "60");
|
|
702
|
+
|
|
703
|
+
// Bare valueless directive.
|
|
704
|
+
var bare = pcc("no-store");
|
|
705
|
+
check("_parseCacheControl: bare directive → empty-string value",
|
|
706
|
+
Object.prototype.hasOwnProperty.call(bare, "no-store") && bare["no-store"] === "");
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// ---- Storage decision branch coverage (RFC 9111 §3) -----------------
|
|
710
|
+
|
|
711
|
+
function testEvaluateStorageBranches() {
|
|
712
|
+
var es = b.httpClient.cache._evaluateStorage;
|
|
713
|
+
var now = Date.now();
|
|
714
|
+
var d = new Date(now).toUTCString();
|
|
715
|
+
|
|
716
|
+
check("_evaluateStorage: POST → method-not-cacheable",
|
|
717
|
+
es("POST", 200, { "cache-control": "max-age=60" }, true, {}).reason === "method-not-cacheable");
|
|
718
|
+
|
|
719
|
+
// Undefined method defaults to GET and remains cacheable.
|
|
720
|
+
check("_evaluateStorage: undefined method defaults to GET",
|
|
721
|
+
es(undefined, 200, { "cache-control": "max-age=60" }, true, {}).cacheable === true);
|
|
722
|
+
|
|
723
|
+
check("_evaluateStorage: status 500 → status-not-cacheable",
|
|
724
|
+
es("GET", 500, { "cache-control": "max-age=60" }, true, {}).reason === "status-not-cacheable");
|
|
725
|
+
|
|
726
|
+
check("_evaluateStorage: Vary:* → vary-star",
|
|
727
|
+
es("GET", 200, { "cache-control": "max-age=60", "vary": "*" }, true, {}).reason === "vary-star");
|
|
728
|
+
|
|
729
|
+
check("_evaluateStorage: no lifetime + no validator → no-freshness-no-validator",
|
|
730
|
+
es("GET", 200, {}, true, {}).reason === "no-freshness-no-validator");
|
|
731
|
+
|
|
732
|
+
var etagOnly = es("GET", 200, { "etag": '"x"' }, true, {});
|
|
733
|
+
check("_evaluateStorage: ETag but no lifetime → validator-only, freshness 0",
|
|
734
|
+
etagOnly.reason === "validator-only" && etagOnly.freshnessMs === 0);
|
|
735
|
+
|
|
736
|
+
// Malformed max-age parses to null (_ccNumber isFinite/negative guard),
|
|
737
|
+
// so a validator-only decision is reached instead of a bogus lifetime.
|
|
738
|
+
check("_evaluateStorage: max-age=abc ignored → validator-only",
|
|
739
|
+
es("GET", 200, { "cache-control": "max-age=abc", "etag": '"x"' }, true, {}).reason === "validator-only");
|
|
740
|
+
|
|
741
|
+
var expFuture = es("GET", 200, { "date": d, "expires": new Date(now + 60000).toUTCString() }, true, {});
|
|
742
|
+
check("_evaluateStorage: Expires in future (with Date) → cacheable, positive freshness",
|
|
743
|
+
expFuture.cacheable === true && expFuture.freshnessMs > 0);
|
|
744
|
+
|
|
745
|
+
check("_evaluateStorage: Expires in past → expires-in-past",
|
|
746
|
+
es("GET", 200, { "date": d, "expires": new Date(now - 60000).toUTCString() }, true, {}).reason === "expires-in-past");
|
|
747
|
+
|
|
748
|
+
var expNoDate = es("GET", 200, { "expires": new Date(now + 60000).toUTCString() }, true, {});
|
|
749
|
+
check("_evaluateStorage: Expires with no Date header still cacheable",
|
|
750
|
+
expNoDate.cacheable === true && expNoDate.freshnessMs > 0);
|
|
751
|
+
|
|
752
|
+
// h2-style array-valued header — _headerOne picks the first element.
|
|
753
|
+
check("_evaluateStorage: array-valued Cache-Control uses first element",
|
|
754
|
+
es("GET", 200, { "cache-control": ["max-age=60"], "date": [d] }, true, {}).freshnessMs === 60000);
|
|
755
|
+
|
|
756
|
+
// An empty-array header value resolves to null (no first element).
|
|
757
|
+
check("_evaluateStorage: empty-array header treated as absent",
|
|
758
|
+
es("GET", 200, { "cache-control": [], "etag": '"x"' }, true, {}).reason === "validator-only");
|
|
759
|
+
|
|
760
|
+
// A garbage Date header parses to null and is simply ignored.
|
|
761
|
+
check("_evaluateStorage: unparseable Date ignored, max-age still applies",
|
|
762
|
+
es("GET", 200, { "cache-control": "max-age=60", "date": "not-a-date" }, true, {}).cacheable === true);
|
|
763
|
+
|
|
764
|
+
// Null response headers must not throw (defensive _lcHeaders guard).
|
|
765
|
+
check("_evaluateStorage: null response headers → no-freshness-no-validator",
|
|
766
|
+
es("GET", 200, null, true, null).reason === "no-freshness-no-validator");
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function testEvaluateStorageAuthAndSMaxage() {
|
|
770
|
+
var es = b.httpClient.cache._evaluateStorage;
|
|
771
|
+
|
|
772
|
+
// RFC 9111 §3.5 — a shared cache must not store an Authorization-bearing
|
|
773
|
+
// response that lacks an explicit share opt-in.
|
|
774
|
+
check("_evaluateStorage: shared + Authorization + no opt-in → authorization-shared",
|
|
775
|
+
es("GET", 200, { "cache-control": "max-age=60" }, true, { authorization: "Bearer t" }).reason === "authorization-shared");
|
|
776
|
+
|
|
777
|
+
check("_evaluateStorage: shared + Authorization + public → cacheable",
|
|
778
|
+
es("GET", 200, { "cache-control": "public, max-age=60" }, true, { authorization: "Bearer t" }).cacheable === true);
|
|
779
|
+
|
|
780
|
+
var authSMax = es("GET", 200, { "cache-control": "s-maxage=60" }, true, { authorization: "Bearer t" });
|
|
781
|
+
check("_evaluateStorage: shared + Authorization + s-maxage → cacheable, s-maxage lifetime",
|
|
782
|
+
authSMax.cacheable === true && authSMax.freshnessMs === 60000);
|
|
783
|
+
|
|
784
|
+
// An empty Authorization value is not treated as authenticated — the
|
|
785
|
+
// §3.5 gate must not fire, so the plain max-age response stays cacheable.
|
|
786
|
+
check("_evaluateStorage: empty Authorization value does not trip §3.5 gate",
|
|
787
|
+
es("GET", 200, { "cache-control": "max-age=60" }, true, { authorization: "" }).cacheable === true);
|
|
788
|
+
|
|
789
|
+
// Private cache ignores §3.5 entirely.
|
|
790
|
+
check("_evaluateStorage: non-shared cache ignores Authorization",
|
|
791
|
+
es("GET", 200, { "cache-control": "max-age=60" }, false, { authorization: "Bearer t" }).cacheable === true);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// ---- Pure helper branch coverage ------------------------------------
|
|
795
|
+
|
|
796
|
+
function testUrlKeyAndVaryHelpers() {
|
|
797
|
+
var cacheNs = b.httpClient.cache;
|
|
798
|
+
|
|
799
|
+
// Query parameters are sorted for a stable key.
|
|
800
|
+
check("_normalizeUrl: query params sorted",
|
|
801
|
+
cacheNs._normalizeUrl("http://h/p?b=2&a=1&b=1") === "http://h/p?a=1&b=1&b=2");
|
|
802
|
+
|
|
803
|
+
// An unparseable URL falls back to String(url).
|
|
804
|
+
check("_normalizeUrl: invalid URL falls back to String(url)",
|
|
805
|
+
cacheNs._normalizeUrl("http://[bad") === "http://[bad");
|
|
806
|
+
|
|
807
|
+
// Missing path normalizes to "/".
|
|
808
|
+
check("_normalizeUrl: missing path → /",
|
|
809
|
+
cacheNs._normalizeUrl("http://h") === "http://h/");
|
|
810
|
+
|
|
811
|
+
// Default method + null vary in the key shape.
|
|
812
|
+
var key = cacheNs._buildCacheKey(undefined, "http://h/");
|
|
813
|
+
check("_buildCacheKey: undefined method defaults to GET, vary null",
|
|
814
|
+
key.indexOf('"m":"GET"') !== -1 && key.indexOf('"v":null') !== -1);
|
|
815
|
+
|
|
816
|
+
// Vary:* is the "uncacheable" sentinel.
|
|
817
|
+
check("_extractVaryValues: '*' → null sentinel",
|
|
818
|
+
cacheNs._extractVaryValues("*", {}) === null);
|
|
819
|
+
|
|
820
|
+
// A Vary name absent from the request headers projects to null.
|
|
821
|
+
var pairs = cacheNs._extractVaryValues("accept, x-missing", { accept: "a" });
|
|
822
|
+
check("_extractVaryValues: absent request header → null value",
|
|
823
|
+
pairs.length === 2 && pairs[1][0] === "x-missing" && pairs[1][1] === null);
|
|
824
|
+
|
|
825
|
+
// Non-string Vary header → empty pair list.
|
|
826
|
+
check("_extractVaryValues: non-string Vary → []",
|
|
827
|
+
Array.isArray(cacheNs._extractVaryValues(123, {})) && cacheNs._extractVaryValues(123, {}).length === 0);
|
|
828
|
+
|
|
829
|
+
// Age header dominates apparent age when the Date header is absent.
|
|
830
|
+
var now = Date.now();
|
|
831
|
+
check("_currentAgeMs: Age header dominates when Date missing",
|
|
832
|
+
cacheNs._currentAgeMs({ storedAtMs: now - 5000, dateMs: null, ageHeaderSec: 10 }, now) === 15000);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// ---- memoryStore direct branch coverage -----------------------------
|
|
836
|
+
|
|
837
|
+
function testMemoryStoreDirectBranches() {
|
|
838
|
+
var ms = b.httpClient.cache.memoryStore;
|
|
839
|
+
|
|
840
|
+
var threw = false;
|
|
841
|
+
try { ms("not-an-object"); }
|
|
842
|
+
catch (_e) { threw = true; }
|
|
843
|
+
check("memoryStore: throws when opts is a non-object primitive", threw);
|
|
844
|
+
|
|
845
|
+
// Oversized single entry is refused (not stored, not a full wipe) and
|
|
846
|
+
// reported via the eviction hook.
|
|
847
|
+
var store = ms({ maxBytes: 100, maxEntries: 8 });
|
|
848
|
+
var reasons = [];
|
|
849
|
+
store._setOnEvict(function (info) { reasons.push(info.reason); });
|
|
850
|
+
store.set("big", { body: Buffer.alloc(500), headers: {} });
|
|
851
|
+
check("memoryStore: oversized entry refused via entry-too-large",
|
|
852
|
+
store.get("big") === null && reasons.indexOf("entry-too-large") !== -1);
|
|
853
|
+
|
|
854
|
+
// Non-string key / falsy entry are no-ops.
|
|
855
|
+
store.set(123, { body: Buffer.alloc(1), headers: {} });
|
|
856
|
+
store.set("k", null);
|
|
857
|
+
check("memoryStore: bad key / null entry are no-ops",
|
|
858
|
+
store._stats().entries === 0);
|
|
859
|
+
|
|
860
|
+
// Deleting an absent key is a no-op.
|
|
861
|
+
store.delete("never-stored");
|
|
862
|
+
check("memoryStore: delete of absent key is a no-op",
|
|
863
|
+
store._stats().entries === 0);
|
|
864
|
+
|
|
865
|
+
// Byte-cap eviction loop runs its body; a non-function onEvict is
|
|
866
|
+
// accepted and silently ignored.
|
|
867
|
+
var store2 = ms({ maxBytes: 1000, maxEntries: 100 });
|
|
868
|
+
store2._setOnEvict("not-a-function");
|
|
869
|
+
store2.set("a", { body: Buffer.alloc(400), headers: {} });
|
|
870
|
+
store2.set("b", { body: Buffer.alloc(400), headers: {} });
|
|
871
|
+
store2.set("c", { body: Buffer.alloc(400), headers: {} });
|
|
872
|
+
check("memoryStore: byte-cap eviction drops the LRU head",
|
|
873
|
+
store2.get("a") === null && store2._stats().entries === 2);
|
|
874
|
+
|
|
875
|
+
// A throwing eviction callback during a real (max-entries) eviction is
|
|
876
|
+
// best-effort — it must not surface out of set().
|
|
877
|
+
var store3 = ms({ maxBytes: 1048576, maxEntries: 1 });
|
|
878
|
+
store3._setOnEvict(function () { throw new Error("evict callback boom"); });
|
|
879
|
+
store3.set("first", { body: Buffer.alloc(10), headers: {} });
|
|
880
|
+
var evictThrowOk = true;
|
|
881
|
+
try { store3.set("second", { body: Buffer.alloc(10), headers: {} }); }
|
|
882
|
+
catch (_e) { evictThrowOk = false; }
|
|
883
|
+
check("memoryStore: throwing eviction callback swallowed during eviction",
|
|
884
|
+
evictThrowOk && store3._stats().entries === 1 && store3.get("first") === null);
|
|
885
|
+
|
|
886
|
+
// An entry with no body buffer still contributes header bytes and stores.
|
|
887
|
+
var store4 = ms({ maxBytes: 1048576, maxEntries: 8 });
|
|
888
|
+
store4.set("headers-only", { headers: { "x-a": "b" } });
|
|
889
|
+
check("memoryStore: body-less entry stored, counts header bytes",
|
|
890
|
+
store4._stats().entries === 1 && store4._stats().bytes > 0);
|
|
891
|
+
|
|
892
|
+
// A throwing eviction callback on the oversized-entry (entry-too-large)
|
|
893
|
+
// path is also swallowed.
|
|
894
|
+
var store5 = ms({ maxBytes: 100, maxEntries: 8 });
|
|
895
|
+
store5._setOnEvict(function () { throw new Error("too-large callback boom"); });
|
|
896
|
+
var tooLargeThrowOk = true;
|
|
897
|
+
try { store5.set("big", { body: Buffer.alloc(500), headers: {} }); }
|
|
898
|
+
catch (_e) { tooLargeThrowOk = false; }
|
|
899
|
+
check("memoryStore: throwing callback on entry-too-large swallowed",
|
|
900
|
+
tooLargeThrowOk && store5.get("big") === null);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// ---- Audit / observability sink branch coverage ---------------------
|
|
904
|
+
|
|
905
|
+
function testEmitAndObsSinkBranches() {
|
|
906
|
+
var cacheNs = b.httpClient.cache;
|
|
907
|
+
|
|
908
|
+
// A throwing audit sink must not surface out of _emit (drop-silent).
|
|
909
|
+
var throwingAudit = { safeEmit: function () { throw new Error("audit boom"); } };
|
|
910
|
+
var cacheA = cacheNs.create({ store: cacheNs.memoryStore(), audit: throwingAudit });
|
|
911
|
+
var emitOk = true;
|
|
912
|
+
try { cacheA._emit("httpclient.cache.test"); } // outcome + metadata defaulted
|
|
913
|
+
catch (_e) { emitOk = false; }
|
|
914
|
+
check("_emit: throwing audit sink swallowed (drop-silent)", emitOk);
|
|
915
|
+
|
|
916
|
+
// observability with only `event` (no safeEvent) uses the event fallback.
|
|
917
|
+
var obsCalls = [];
|
|
918
|
+
var cacheB = cacheNs.create({
|
|
919
|
+
store: cacheNs.memoryStore(),
|
|
920
|
+
observability: { event: function (name) { obsCalls.push(name); } },
|
|
921
|
+
});
|
|
922
|
+
cacheB._obsEvent("httpclient.cache.probe", 1, {});
|
|
923
|
+
check("_obsEvent: falls back to obs.event when safeEvent absent",
|
|
924
|
+
obsCalls.indexOf("httpclient.cache.probe") !== -1);
|
|
925
|
+
|
|
926
|
+
// A throwing observability sink must not surface out of _obsEvent.
|
|
927
|
+
var cacheC = cacheNs.create({
|
|
928
|
+
store: cacheNs.memoryStore(),
|
|
929
|
+
observability: { event: function () { throw new Error("obs boom"); } },
|
|
930
|
+
});
|
|
931
|
+
var obsOk = true;
|
|
932
|
+
try { cacheC._obsEvent("x", 1, {}); }
|
|
933
|
+
catch (_e) { obsOk = false; }
|
|
934
|
+
check("_obsEvent: throwing sink swallowed (drop-silent)", obsOk);
|
|
935
|
+
|
|
936
|
+
// observability present but exposing neither safeEvent nor event as a
|
|
937
|
+
// function → _obsEvent short-circuits without emitting.
|
|
938
|
+
var cacheD = cacheNs.create({
|
|
939
|
+
store: cacheNs.memoryStore(),
|
|
940
|
+
observability: { event: 123 }, // not a function
|
|
941
|
+
});
|
|
942
|
+
var obsNoFnOk = true;
|
|
943
|
+
try { cacheD._obsEvent("noop", 1, {}); }
|
|
944
|
+
catch (_e) { obsNoFnOk = false; }
|
|
945
|
+
check("_obsEvent: non-function sink is a no-op", obsNoFnOk);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// ---- Operator-facing miss / no-op helper branches -------------------
|
|
949
|
+
|
|
950
|
+
function testInspectInvalidateAndStoreMissBranches() {
|
|
951
|
+
var cache = _newCache();
|
|
952
|
+
|
|
953
|
+
// inspect with the requestHeaders arg omitted (defaults to {}).
|
|
954
|
+
var missed = cache.inspect("GET", "http://127.0.0.1:9/absent");
|
|
955
|
+
check("inspect: absent entry → { hit: false } (no requestHeaders arg)",
|
|
956
|
+
missed.hit === false);
|
|
957
|
+
|
|
958
|
+
// invalidate on a non-existent entry returns false.
|
|
959
|
+
check("invalidate: false when no entry present",
|
|
960
|
+
cache.invalidate("GET", "http://127.0.0.1:9/absent") === false);
|
|
961
|
+
|
|
962
|
+
// A Vary:* response cannot build an entry, so _store refuses it.
|
|
963
|
+
var stored = cache._store("GET", "http://127.0.0.1:9/vs", {}, 200,
|
|
964
|
+
{ vary: "*" }, Buffer.from("x"),
|
|
965
|
+
{ varyHeader: "*", freshnessMs: 60000, directives: {} });
|
|
966
|
+
check("_store: Vary:* response is not stored (buildEntry null)",
|
|
967
|
+
stored === false && cache.stats().entries === 0);
|
|
968
|
+
|
|
969
|
+
var okEval = { varyHeader: null, freshnessMs: 60000, directives: {} };
|
|
970
|
+
|
|
971
|
+
// Undefined method defaults to GET, and a non-Buffer (string) body is
|
|
972
|
+
// coerced to a Buffer on store.
|
|
973
|
+
var storedStr = cache._store(undefined, "http://127.0.0.1:9/str", {}, 200,
|
|
974
|
+
{ "cache-control": "max-age=60" }, "string-body", okEval);
|
|
975
|
+
var inspStr = cache.inspect("GET", "http://127.0.0.1:9/str", {});
|
|
976
|
+
check("_store: undefined method + string body stored under GET",
|
|
977
|
+
storedStr === true && inspStr.hit === true);
|
|
978
|
+
|
|
979
|
+
// A null body is stored as an empty buffer.
|
|
980
|
+
var storedNull = cache._store("GET", "http://127.0.0.1:9/nullbody", {}, 204,
|
|
981
|
+
{ "cache-control": "max-age=60" }, null, okEval);
|
|
982
|
+
check("_store: null body stored as empty buffer",
|
|
983
|
+
storedNull === true && cache.inspect("GET", "http://127.0.0.1:9/nullbody", {}).hit === true);
|
|
984
|
+
|
|
985
|
+
// _isFresh reflects the freshness evaluation of a synthetic entry.
|
|
986
|
+
var freshEntry = {
|
|
987
|
+
storedAtMs: Date.now(), dateMs: Date.now(), ageHeaderSec: 0,
|
|
988
|
+
freshnessMs: 60000, directives: {},
|
|
989
|
+
};
|
|
990
|
+
var staleEntry = {
|
|
991
|
+
storedAtMs: Date.now(), dateMs: Date.now(), ageHeaderSec: 0,
|
|
992
|
+
freshnessMs: 0, directives: {},
|
|
993
|
+
};
|
|
994
|
+
check("_isFresh: true for an in-lifetime entry, false past expiry",
|
|
995
|
+
cache._isFresh(freshEntry) === true && cache._isFresh(staleEntry) === false);
|
|
996
|
+
|
|
997
|
+
// An entry with no directives field is tolerated (defaults to {}).
|
|
998
|
+
var noDirectivesEntry = {
|
|
999
|
+
storedAtMs: Date.now(), dateMs: Date.now(), ageHeaderSec: 0,
|
|
1000
|
+
freshnessMs: 60000,
|
|
1001
|
+
};
|
|
1002
|
+
check("_isFresh: entry without a directives field defaults cleanly",
|
|
1003
|
+
cache._isFresh(noDirectivesEntry) === true);
|
|
1004
|
+
|
|
1005
|
+
// stats() reports null when the store's _stats implementation throws.
|
|
1006
|
+
var throwingStatsStore = {
|
|
1007
|
+
get: function () { return null; }, set: function () {},
|
|
1008
|
+
delete: function () {}, clear: function () {},
|
|
1009
|
+
_stats: function () { throw new Error("stats boom"); },
|
|
1010
|
+
};
|
|
1011
|
+
var cacheS = b.httpClient.cache.create({ store: throwingStatsStore });
|
|
1012
|
+
check("stats: throwing store._stats → null", cacheS.stats() === null);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// ---- Vary lookup with the varied header absent from the request -----
|
|
1016
|
+
|
|
1017
|
+
async function testVaryLookupMissingRequestHeader() {
|
|
1018
|
+
await _withServer(function (req, res) {
|
|
1019
|
+
res.writeHead(200, {
|
|
1020
|
+
"Content-Type": "text/plain",
|
|
1021
|
+
"Cache-Control": "public, max-age=60",
|
|
1022
|
+
"Date": _httpDate(Date.now()),
|
|
1023
|
+
"Vary": "Accept-Encoding",
|
|
1024
|
+
});
|
|
1025
|
+
res.end("varied");
|
|
1026
|
+
}, async function (baseUrl) {
|
|
1027
|
+
var cache = _newCache();
|
|
1028
|
+
// Store a variant keyed on Accept-Encoding: identity.
|
|
1029
|
+
await b.httpClient.request({
|
|
1030
|
+
url: baseUrl + "/v", cache: cache, headers: { "Accept-Encoding": "identity" },
|
|
1031
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1032
|
+
});
|
|
1033
|
+
// A lookup that omits the varied header projects it to null and must
|
|
1034
|
+
// NOT match the stored identity variant.
|
|
1035
|
+
check("Vary lookup: request missing the varied header does not match",
|
|
1036
|
+
cache.inspect("GET", baseUrl + "/v", {}).hit === false);
|
|
1037
|
+
// The exact variant still hits.
|
|
1038
|
+
check("Vary lookup: matching varied header still hits",
|
|
1039
|
+
cache.inspect("GET", baseUrl + "/v", { "Accept-Encoding": "identity" }).hit === true);
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// ---- §3.5 fail-closed for pre-upgrade persistent-store records -------
|
|
1044
|
+
|
|
1045
|
+
function testLegacyEntryFailClosedInSharedCache() {
|
|
1046
|
+
var store = b.httpClient.cache.memoryStore();
|
|
1047
|
+
var cache = b.httpClient.cache.create({ store: store, sharedCache: true });
|
|
1048
|
+
var url = "http://legacy.example/x";
|
|
1049
|
+
|
|
1050
|
+
// Seed a record written before `hadAuthorization` existed (flag absent).
|
|
1051
|
+
var key = b.httpClient.cache._buildCacheKey("GET", url, []);
|
|
1052
|
+
store.set(key, {
|
|
1053
|
+
statusCode: 200, headers: {}, body: Buffer.from("legacy"),
|
|
1054
|
+
storedAtMs: Date.now(), dateMs: Date.now(), ageHeaderSec: 0,
|
|
1055
|
+
freshnessMs: 60000, directives: {}, method: "GET", url: url,
|
|
1056
|
+
varyValues: [], varyHeader: null,
|
|
1057
|
+
// NOTE: no hadAuthorization field — the pre-upgrade shape.
|
|
1058
|
+
});
|
|
1059
|
+
check("legacy fail-closed: seeded entry present before lookup",
|
|
1060
|
+
store._stats().entries === 1);
|
|
1061
|
+
|
|
1062
|
+
var insp = cache.inspect("GET", url, {});
|
|
1063
|
+
check("legacy fail-closed: shared-cache lookup of flag-less entry reports miss",
|
|
1064
|
+
insp.hit === false);
|
|
1065
|
+
check("legacy fail-closed: flag-less entry evicted on lookup",
|
|
1066
|
+
store._stats().entries === 0);
|
|
1067
|
+
|
|
1068
|
+
// The eviction store.delete is drop-silent: an operator store whose
|
|
1069
|
+
// delete throws while purging a flag-less legacy entry must still yield a
|
|
1070
|
+
// clean miss rather than surfacing the error.
|
|
1071
|
+
var throwDelStore = {
|
|
1072
|
+
get: function () {
|
|
1073
|
+
return {
|
|
1074
|
+
statusCode: 200, headers: {}, body: Buffer.from("legacy"),
|
|
1075
|
+
storedAtMs: Date.now(), dateMs: Date.now(), ageHeaderSec: 0,
|
|
1076
|
+
freshnessMs: 60000, directives: {}, method: "GET", url: url,
|
|
1077
|
+
varyValues: [], varyHeader: null,
|
|
1078
|
+
// no hadAuthorization — pre-upgrade shape
|
|
1079
|
+
};
|
|
1080
|
+
},
|
|
1081
|
+
set: function () {},
|
|
1082
|
+
delete: function () { throw new Error("delete boom"); },
|
|
1083
|
+
clear: function () {},
|
|
1084
|
+
};
|
|
1085
|
+
var cache2 = b.httpClient.cache.create({ store: throwDelStore, sharedCache: true });
|
|
1086
|
+
var insp2;
|
|
1087
|
+
var lookupOk = true;
|
|
1088
|
+
try { insp2 = cache2.inspect("GET", url, {}); }
|
|
1089
|
+
catch (_e) { lookupOk = false; }
|
|
1090
|
+
check("legacy fail-closed: throwing store.delete during eviction is drop-silent → miss",
|
|
1091
|
+
lookupOk && insp2.hit === false);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// ---- Behavioral: shared-cache Authorization leak prevention ----------
|
|
1095
|
+
|
|
1096
|
+
async function testAuthorizationSharedCacheLeakPrevented() {
|
|
1097
|
+
var hits = 0;
|
|
1098
|
+
await _withServer(function (req, res) {
|
|
1099
|
+
hits += 1;
|
|
1100
|
+
// No public / s-maxage / must-revalidate → not shareable per §3.5.
|
|
1101
|
+
res.writeHead(200, {
|
|
1102
|
+
"Cache-Control": "max-age=60",
|
|
1103
|
+
"Date": _httpDate(Date.now()),
|
|
1104
|
+
});
|
|
1105
|
+
res.end("secret-" + hits);
|
|
1106
|
+
}, async function (baseUrl) {
|
|
1107
|
+
var cache = b.httpClient.cache.create({
|
|
1108
|
+
store: b.httpClient.cache.memoryStore(), sharedCache: true,
|
|
1109
|
+
});
|
|
1110
|
+
await b.httpClient.request({
|
|
1111
|
+
url: baseUrl + "/a", cache: cache, headers: { Authorization: "Bearer alice" },
|
|
1112
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1113
|
+
});
|
|
1114
|
+
await b.httpClient.request({
|
|
1115
|
+
url: baseUrl + "/a", cache: cache, headers: { Authorization: "Bearer bob" },
|
|
1116
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1117
|
+
});
|
|
1118
|
+
check("§3.5 leak guard: authed response without opt-in never served from shared cache",
|
|
1119
|
+
hits === 2);
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
// With `public`, the same response is shareable and the second call HITs.
|
|
1123
|
+
var hits2 = 0;
|
|
1124
|
+
await _withServer(function (req, res) {
|
|
1125
|
+
hits2 += 1;
|
|
1126
|
+
res.writeHead(200, {
|
|
1127
|
+
"Cache-Control": "public, max-age=60",
|
|
1128
|
+
"Date": _httpDate(Date.now()),
|
|
1129
|
+
});
|
|
1130
|
+
res.end("public-body");
|
|
1131
|
+
}, async function (baseUrl) {
|
|
1132
|
+
var cache = b.httpClient.cache.create({
|
|
1133
|
+
store: b.httpClient.cache.memoryStore(), sharedCache: true,
|
|
1134
|
+
});
|
|
1135
|
+
await b.httpClient.request({
|
|
1136
|
+
url: baseUrl + "/p", cache: cache, headers: { Authorization: "Bearer x" },
|
|
1137
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1138
|
+
});
|
|
1139
|
+
var r2 = await b.httpClient.request({
|
|
1140
|
+
url: baseUrl + "/p", cache: cache, headers: { Authorization: "Bearer x" },
|
|
1141
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1142
|
+
});
|
|
1143
|
+
check("§3.5 opt-in: public authed response is cacheable → HIT",
|
|
1144
|
+
r2.headers["x-blamejs-cache"] === "HIT" && hits2 === 1);
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// ---- Behavioral: uncacheable status / Vary:* bypass the store --------
|
|
1149
|
+
|
|
1150
|
+
async function testUncacheableStatusAndVaryStarBypass() {
|
|
1151
|
+
var hits = 0;
|
|
1152
|
+
await _withServer(function (req, res) {
|
|
1153
|
+
hits += 1;
|
|
1154
|
+
// 202 Accepted resolves (2xx) but is NOT in the cacheable status set.
|
|
1155
|
+
res.writeHead(202, {
|
|
1156
|
+
"Cache-Control": "public, max-age=60",
|
|
1157
|
+
"Date": _httpDate(Date.now()),
|
|
1158
|
+
});
|
|
1159
|
+
res.end("accepted");
|
|
1160
|
+
}, async function (baseUrl) {
|
|
1161
|
+
var cache = _newCache();
|
|
1162
|
+
var r1 = await b.httpClient.request({
|
|
1163
|
+
url: baseUrl + "/acc", cache: cache,
|
|
1164
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1165
|
+
});
|
|
1166
|
+
await b.httpClient.request({
|
|
1167
|
+
url: baseUrl + "/acc", cache: cache,
|
|
1168
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1169
|
+
});
|
|
1170
|
+
check("status-not-cacheable: 202 resolved but not stored → upstream hit twice",
|
|
1171
|
+
r1.statusCode === 202 && hits === 2);
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
var hits2 = 0;
|
|
1175
|
+
await _withServer(function (req, res) {
|
|
1176
|
+
hits2 += 1;
|
|
1177
|
+
res.writeHead(200, {
|
|
1178
|
+
"Cache-Control": "public, max-age=60",
|
|
1179
|
+
"Vary": "*",
|
|
1180
|
+
"Date": _httpDate(Date.now()),
|
|
1181
|
+
});
|
|
1182
|
+
res.end("varystar");
|
|
1183
|
+
}, async function (baseUrl) {
|
|
1184
|
+
var cache = _newCache();
|
|
1185
|
+
await b.httpClient.request({
|
|
1186
|
+
url: baseUrl + "/vs", cache: cache,
|
|
1187
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1188
|
+
});
|
|
1189
|
+
await b.httpClient.request({
|
|
1190
|
+
url: baseUrl + "/vs", cache: cache,
|
|
1191
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1192
|
+
});
|
|
1193
|
+
check("vary-star: Vary:* response uncacheable → upstream hit twice", hits2 === 2);
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// ---- Behavioral: Expires + s-maxage freshness computation ------------
|
|
1198
|
+
|
|
1199
|
+
async function testExpiresAndSMaxageFreshness() {
|
|
1200
|
+
// Expires (no max-age) drives freshness; a malformed Age header on the
|
|
1201
|
+
// stored response must not corrupt the entry.
|
|
1202
|
+
await _withServer(function (req, res) {
|
|
1203
|
+
res.writeHead(200, {
|
|
1204
|
+
"Date": _httpDate(Date.now()),
|
|
1205
|
+
"Expires": _httpDate(Date.now() + 60000),
|
|
1206
|
+
"Age": "not-a-number",
|
|
1207
|
+
});
|
|
1208
|
+
res.end("exp-body");
|
|
1209
|
+
}, async function (baseUrl) {
|
|
1210
|
+
var cache = _newCache();
|
|
1211
|
+
var r1 = await b.httpClient.request({
|
|
1212
|
+
url: baseUrl + "/e", cache: cache,
|
|
1213
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1214
|
+
});
|
|
1215
|
+
var r2 = await b.httpClient.request({
|
|
1216
|
+
url: baseUrl + "/e", cache: cache,
|
|
1217
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1218
|
+
});
|
|
1219
|
+
check("Expires freshness: first MISS then HIT (malformed Age tolerated)",
|
|
1220
|
+
r1.headers["x-blamejs-cache"] === "MISS" && r2.headers["x-blamejs-cache"] === "HIT");
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
// s-maxage wins over max-age=0 in a shared cache.
|
|
1224
|
+
var hits = 0;
|
|
1225
|
+
await _withServer(function (req, res) {
|
|
1226
|
+
hits += 1;
|
|
1227
|
+
res.writeHead(200, {
|
|
1228
|
+
"Cache-Control": "s-maxage=60, max-age=0",
|
|
1229
|
+
"Date": _httpDate(Date.now()),
|
|
1230
|
+
});
|
|
1231
|
+
res.end("smax");
|
|
1232
|
+
}, async function (baseUrl) {
|
|
1233
|
+
var cache = b.httpClient.cache.create({
|
|
1234
|
+
store: b.httpClient.cache.memoryStore(), sharedCache: true,
|
|
1235
|
+
});
|
|
1236
|
+
await b.httpClient.request({
|
|
1237
|
+
url: baseUrl + "/sm", cache: cache,
|
|
1238
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1239
|
+
});
|
|
1240
|
+
var r2 = await b.httpClient.request({
|
|
1241
|
+
url: baseUrl + "/sm", cache: cache,
|
|
1242
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1243
|
+
});
|
|
1244
|
+
check("s-maxage: shared cache treats s-maxage=60 as fresh → HIT",
|
|
1245
|
+
r2.headers["x-blamejs-cache"] === "HIT" && hits === 1);
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// ---- Behavioral: validator-only + no-validator storage decisions -----
|
|
1250
|
+
|
|
1251
|
+
async function testValidatorOnlyAndNoValidatorStorage() {
|
|
1252
|
+
// ETag with no lifetime → stored with freshness 0 → revalidates.
|
|
1253
|
+
var hits = 0;
|
|
1254
|
+
await _withServer(function (req, res) {
|
|
1255
|
+
hits += 1;
|
|
1256
|
+
if (req.headers["if-none-match"] === '"vo"') {
|
|
1257
|
+
// A hop-by-hop header (RFC 9111 §5) must be stripped on merge; a
|
|
1258
|
+
// normal header must be carried into the served response.
|
|
1259
|
+
res.writeHead(304, {
|
|
1260
|
+
"ETag": '"vo"',
|
|
1261
|
+
"Date": _httpDate(Date.now()),
|
|
1262
|
+
"Proxy-Authenticate": "Basic realm=x",
|
|
1263
|
+
"X-Merged-In": "yes",
|
|
1264
|
+
});
|
|
1265
|
+
res.end();
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
res.writeHead(200, { "ETag": '"vo"', "Date": _httpDate(Date.now()) });
|
|
1269
|
+
res.end("vobody");
|
|
1270
|
+
}, async function (baseUrl) {
|
|
1271
|
+
var cache = _newCache();
|
|
1272
|
+
var r1 = await b.httpClient.request({
|
|
1273
|
+
url: baseUrl + "/vo", cache: cache,
|
|
1274
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1275
|
+
});
|
|
1276
|
+
var r2 = await b.httpClient.request({
|
|
1277
|
+
url: baseUrl + "/vo", cache: cache,
|
|
1278
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1279
|
+
});
|
|
1280
|
+
check("validator-only: ETag-only response stored + revalidated",
|
|
1281
|
+
r1.headers["x-blamejs-cache"] === "MISS" &&
|
|
1282
|
+
r2.headers["x-blamejs-cache"] === "REVALIDATED" &&
|
|
1283
|
+
r2.body.toString("utf8") === "vobody" && hits === 2);
|
|
1284
|
+
check("304 merge: hop-by-hop header stripped, normal header carried through",
|
|
1285
|
+
r2.headers["proxy-authenticate"] === undefined &&
|
|
1286
|
+
r2.headers["x-merged-in"] === "yes");
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
// No lifetime AND no validator → not stored at all.
|
|
1290
|
+
var hits2 = 0;
|
|
1291
|
+
await _withServer(function (req, res) {
|
|
1292
|
+
hits2 += 1;
|
|
1293
|
+
res.writeHead(200, { "Date": _httpDate(Date.now()) });
|
|
1294
|
+
res.end("no-validator");
|
|
1295
|
+
}, async function (baseUrl) {
|
|
1296
|
+
var cache = _newCache();
|
|
1297
|
+
await b.httpClient.request({
|
|
1298
|
+
url: baseUrl + "/nv", cache: cache,
|
|
1299
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1300
|
+
});
|
|
1301
|
+
await b.httpClient.request({
|
|
1302
|
+
url: baseUrl + "/nv", cache: cache,
|
|
1303
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1304
|
+
});
|
|
1305
|
+
check("no-freshness-no-validator: nothing cached → upstream hit twice", hits2 === 2);
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// ---- Behavioral: oversized entry refused, not wiped ------------------
|
|
1310
|
+
|
|
1311
|
+
async function testEntryTooLargeRefusedOverNetwork() {
|
|
1312
|
+
var hits = 0;
|
|
1313
|
+
var audit = _mkAuditCapture();
|
|
1314
|
+
var store = b.httpClient.cache.memoryStore({ maxBytes: 200, maxEntries: 16 });
|
|
1315
|
+
var cache = b.httpClient.cache.create({ store: store, audit: audit });
|
|
1316
|
+
await _withServer(function (req, res) {
|
|
1317
|
+
hits += 1;
|
|
1318
|
+
res.writeHead(200, {
|
|
1319
|
+
"Cache-Control": "public, max-age=60",
|
|
1320
|
+
"Date": _httpDate(Date.now()),
|
|
1321
|
+
});
|
|
1322
|
+
res.end("y".repeat(2000)); // far larger than the 200-byte cap
|
|
1323
|
+
}, async function (baseUrl) {
|
|
1324
|
+
var r1 = await b.httpClient.request({
|
|
1325
|
+
url: baseUrl + "/big", cache: cache,
|
|
1326
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1327
|
+
});
|
|
1328
|
+
var r2 = await b.httpClient.request({
|
|
1329
|
+
url: baseUrl + "/big", cache: cache,
|
|
1330
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1331
|
+
});
|
|
1332
|
+
check("entry-too-large: oversized body refused → both requests MISS",
|
|
1333
|
+
r1.headers["x-blamejs-cache"] === "MISS" &&
|
|
1334
|
+
r2.headers["x-blamejs-cache"] === "MISS" && hits === 2);
|
|
1335
|
+
check("entry-too-large: store stays empty (no wipe-and-store)",
|
|
1336
|
+
store._stats().entries === 0);
|
|
1337
|
+
var tooLarge = audit.events.find(function (e) {
|
|
1338
|
+
return e.action === "httpclient.cache.evicted" &&
|
|
1339
|
+
e.metadata && e.metadata.reason === "entry-too-large";
|
|
1340
|
+
});
|
|
1341
|
+
check("entry-too-large: eviction audit records the entry-too-large reason",
|
|
1342
|
+
tooLarge != null);
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// ---- Behavioral: cache errors never surface as request failures ------
|
|
1347
|
+
|
|
1348
|
+
function _faultStore(faults) {
|
|
1349
|
+
return {
|
|
1350
|
+
get: function () { if (faults.get) throw new Error("store.get boom"); return null; },
|
|
1351
|
+
set: function () { if (faults.set) throw new Error("store.set boom"); },
|
|
1352
|
+
delete: function () { if (faults.delete) throw new Error("store.delete boom"); },
|
|
1353
|
+
clear: function () { if (faults.clear) throw new Error("store.clear boom"); },
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
async function testOperatorStoreFaultTolerance() {
|
|
1358
|
+
// A store whose get() throws is treated as a miss; the request succeeds.
|
|
1359
|
+
var hits = 0;
|
|
1360
|
+
await _withServer(function (req, res) {
|
|
1361
|
+
hits += 1;
|
|
1362
|
+
res.writeHead(200, {
|
|
1363
|
+
"Cache-Control": "public, max-age=60",
|
|
1364
|
+
"Date": _httpDate(Date.now()),
|
|
1365
|
+
});
|
|
1366
|
+
res.end("ok");
|
|
1367
|
+
}, async function (baseUrl) {
|
|
1368
|
+
var cache = b.httpClient.cache.create({ store: _faultStore({ get: true }) });
|
|
1369
|
+
var r1 = await b.httpClient.request({
|
|
1370
|
+
url: baseUrl + "/g", cache: cache,
|
|
1371
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1372
|
+
});
|
|
1373
|
+
check("store.get throw: request still resolves as MISS via the origin",
|
|
1374
|
+
r1.statusCode === 200 && r1.headers["x-blamejs-cache"] === "MISS" && hits === 1);
|
|
1375
|
+
check("store.get throw: stats() returns null for a store without _stats",
|
|
1376
|
+
cache.stats() === null);
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
// A store whose set() throws drops the entry silently → re-fetch on next call.
|
|
1380
|
+
var hits2 = 0;
|
|
1381
|
+
await _withServer(function (req, res) {
|
|
1382
|
+
hits2 += 1;
|
|
1383
|
+
res.writeHead(200, {
|
|
1384
|
+
"Cache-Control": "public, max-age=60",
|
|
1385
|
+
"Date": _httpDate(Date.now()),
|
|
1386
|
+
});
|
|
1387
|
+
res.end("ok");
|
|
1388
|
+
}, async function (baseUrl) {
|
|
1389
|
+
var cache = b.httpClient.cache.create({ store: _faultStore({ set: true }) });
|
|
1390
|
+
await b.httpClient.request({
|
|
1391
|
+
url: baseUrl + "/s", cache: cache,
|
|
1392
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1393
|
+
});
|
|
1394
|
+
await b.httpClient.request({
|
|
1395
|
+
url: baseUrl + "/s", cache: cache,
|
|
1396
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1397
|
+
});
|
|
1398
|
+
check("store.set throw: swallowed → nothing cached, upstream hit twice", hits2 === 2);
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1401
|
+
// invalidate whose delete() throws returns false; clear() swallows throws.
|
|
1402
|
+
var entry = {
|
|
1403
|
+
statusCode: 200, headers: {}, body: Buffer.from("x"), storedAtMs: Date.now(),
|
|
1404
|
+
freshnessMs: 60000, directives: {}, method: "GET", url: "u",
|
|
1405
|
+
varyValues: [], varyHeader: null, hadAuthorization: false,
|
|
1406
|
+
};
|
|
1407
|
+
var hitStore = {
|
|
1408
|
+
get: function () { return entry; },
|
|
1409
|
+
set: function () {},
|
|
1410
|
+
delete: function () { throw new Error("delete boom"); },
|
|
1411
|
+
clear: function () { throw new Error("clear boom"); },
|
|
1412
|
+
};
|
|
1413
|
+
var cacheI = b.httpClient.cache.create({ store: hitStore, sharedCache: false });
|
|
1414
|
+
check("invalidate: store.delete throw → returns false",
|
|
1415
|
+
cacheI.invalidate("GET", "http://h/z") === false);
|
|
1416
|
+
var clearOk = true;
|
|
1417
|
+
try { cacheI.clear(); }
|
|
1418
|
+
catch (_e) { clearOk = false; }
|
|
1419
|
+
check("clear: store.clear throw swallowed (drop-silent)", clearOk);
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// ---- Behavioral: §3.5 re-gate on a 304 that drops the share opt-in ---
|
|
1423
|
+
|
|
1424
|
+
async function testRefreshFrom304DropsShareOptInAuthEvict() {
|
|
1425
|
+
var hits = 0;
|
|
1426
|
+
await _withServer(function (req, res) {
|
|
1427
|
+
hits += 1;
|
|
1428
|
+
if (req.headers["if-none-match"] === '"e1"') {
|
|
1429
|
+
// The 304 replaces Cache-Control with a plain max-age — dropping the
|
|
1430
|
+
// must-revalidate opt-in that first permitted the authed entry into
|
|
1431
|
+
// the shared cache.
|
|
1432
|
+
res.writeHead(304, {
|
|
1433
|
+
"ETag": '"e1"',
|
|
1434
|
+
"Cache-Control": "max-age=60",
|
|
1435
|
+
"Date": _httpDate(Date.now()),
|
|
1436
|
+
});
|
|
1437
|
+
res.end();
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
res.writeHead(200, {
|
|
1441
|
+
"Cache-Control": "must-revalidate, max-age=0",
|
|
1442
|
+
"ETag": '"e1"',
|
|
1443
|
+
"Date": _httpDate(Date.now()),
|
|
1444
|
+
});
|
|
1445
|
+
res.end("authmerged");
|
|
1446
|
+
}, async function (baseUrl) {
|
|
1447
|
+
var store = b.httpClient.cache.memoryStore();
|
|
1448
|
+
var cache = b.httpClient.cache.create({ store: store, sharedCache: true });
|
|
1449
|
+
var r1 = await b.httpClient.request({
|
|
1450
|
+
url: baseUrl + "/m", cache: cache, headers: { Authorization: "Bearer alice" },
|
|
1451
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1452
|
+
});
|
|
1453
|
+
check("304 re-gate: authed must-revalidate response initially stored (MISS)",
|
|
1454
|
+
r1.headers["x-blamejs-cache"] === "MISS" && store._stats().entries >= 1);
|
|
1455
|
+
|
|
1456
|
+
var r2 = await b.httpClient.request({
|
|
1457
|
+
url: baseUrl + "/m", cache: cache, headers: { Authorization: "Bearer alice" },
|
|
1458
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1459
|
+
});
|
|
1460
|
+
check("304 re-gate: current caller still receives revalidated body",
|
|
1461
|
+
r2.headers["x-blamejs-cache"] === "REVALIDATED" &&
|
|
1462
|
+
r2.body.toString("utf8") === "authmerged");
|
|
1463
|
+
check("304 re-gate: entry evicted once the merged headers drop the opt-in",
|
|
1464
|
+
store._stats().entries === 0);
|
|
1465
|
+
|
|
1466
|
+
// A third request from a different principal must go to the origin.
|
|
1467
|
+
var r3 = await b.httpClient.request({
|
|
1468
|
+
url: baseUrl + "/m", cache: cache, headers: { Authorization: "Bearer bob" },
|
|
1469
|
+
allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL, allowInternal: true,
|
|
1470
|
+
});
|
|
1471
|
+
check("304 re-gate: subsequent principal is not served the evicted entry (MISS)",
|
|
1472
|
+
r3.headers["x-blamejs-cache"] === "MISS" && hits === 3);
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// ---- 304 refresh merge/vary/eviction branch coverage ----------------
|
|
1477
|
+
|
|
1478
|
+
function _storedEntryFixture(extra) {
|
|
1479
|
+
return Object.assign({
|
|
1480
|
+
method: "GET",
|
|
1481
|
+
url: "http://refresh.example/x",
|
|
1482
|
+
statusCode: 200,
|
|
1483
|
+
headers: { "content-type": "text/plain", "cache-control": "max-age=0", "date": _httpDate(Date.now() - 1000) },
|
|
1484
|
+
body: Buffer.from("stored-body"),
|
|
1485
|
+
storedAtMs: Date.now() - 1000,
|
|
1486
|
+
dateMs: Date.now() - 1000,
|
|
1487
|
+
ageHeaderSec: 0,
|
|
1488
|
+
freshnessMs: 0,
|
|
1489
|
+
directives: { "max-age": "0" },
|
|
1490
|
+
etag: '"old"', // field set; headers above deliberately omit ETag
|
|
1491
|
+
lastModified: null,
|
|
1492
|
+
varyHeader: null,
|
|
1493
|
+
varyValues: [],
|
|
1494
|
+
hadAuthorization: false,
|
|
1495
|
+
}, extra || {});
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
function testRefreshFrom304DirectBranches() {
|
|
1499
|
+
var cacheNs = b.httpClient.cache;
|
|
1500
|
+
|
|
1501
|
+
// Cacheable merge: hop-by-hop header stripped, a normal header carried,
|
|
1502
|
+
// a malformed Age clamped to 0, and — since the 304 omits ETag and the
|
|
1503
|
+
// stored headers omit it too — the stored entry's ETag is carried forward.
|
|
1504
|
+
var store1 = cacheNs.memoryStore();
|
|
1505
|
+
var cache1 = cacheNs.create({ store: store1, sharedCache: false });
|
|
1506
|
+
var refreshed1 = cache1._refreshFrom304(_storedEntryFixture(), {
|
|
1507
|
+
"cache-control": "max-age=60",
|
|
1508
|
+
"date": _httpDate(Date.now()),
|
|
1509
|
+
"proxy-authenticate": "Basic realm=x", // hop-by-hop → stripped
|
|
1510
|
+
"content-length": "999", // never overwrites the stored body length
|
|
1511
|
+
"x-new": "n", // normal → merged
|
|
1512
|
+
"age": "not-a-number", // malformed → 0
|
|
1513
|
+
});
|
|
1514
|
+
check("_refreshFrom304: hop-by-hop stripped, content-length not merged, normal header merged",
|
|
1515
|
+
refreshed1.headers["proxy-authenticate"] === undefined &&
|
|
1516
|
+
refreshed1.headers["content-length"] === undefined &&
|
|
1517
|
+
refreshed1.headers["x-new"] === "n");
|
|
1518
|
+
check("_refreshFrom304: malformed Age clamped, ETag carried forward, freshness re-derived",
|
|
1519
|
+
refreshed1.ageHeaderSec === 0 && refreshed1.etag === '"old"' &&
|
|
1520
|
+
refreshed1.freshnessMs === 60000);
|
|
1521
|
+
check("_refreshFrom304: cacheable refresh is re-stored",
|
|
1522
|
+
store1.get(cacheNs._buildCacheKey("GET", "http://refresh.example/x", [])) != null);
|
|
1523
|
+
|
|
1524
|
+
// A Vary'd entry is re-stored under its vary key after a 304.
|
|
1525
|
+
var store2 = cacheNs.memoryStore();
|
|
1526
|
+
var cache2 = cacheNs.create({ store: store2, sharedCache: false });
|
|
1527
|
+
cache2._refreshFrom304(
|
|
1528
|
+
_storedEntryFixture({ varyHeader: "accept", varyValues: [["accept", "text/html"]] }),
|
|
1529
|
+
{ "cache-control": "max-age=60", "date": _httpDate(Date.now()) });
|
|
1530
|
+
check("_refreshFrom304: Vary'd entry re-stored under its vary key",
|
|
1531
|
+
store2.get(cacheNs._buildCacheKey("GET", "http://refresh.example/x", [["accept", "text/html"]])) != null);
|
|
1532
|
+
|
|
1533
|
+
// store.set throwing during a cacheable refresh is drop-silent.
|
|
1534
|
+
var setThrowStore = {
|
|
1535
|
+
get: function () { return null; }, set: function () { throw new Error("set boom"); },
|
|
1536
|
+
delete: function () {}, clear: function () {},
|
|
1537
|
+
};
|
|
1538
|
+
var cache3 = cacheNs.create({ store: setThrowStore, sharedCache: false });
|
|
1539
|
+
var refresh3Ok = true;
|
|
1540
|
+
var refreshed3;
|
|
1541
|
+
try { refreshed3 = cache3._refreshFrom304(_storedEntryFixture(), { "cache-control": "max-age=60", "date": _httpDate(Date.now()) }); }
|
|
1542
|
+
catch (_e) { refresh3Ok = false; }
|
|
1543
|
+
check("_refreshFrom304: store.set throw swallowed, merged body still returned",
|
|
1544
|
+
refresh3Ok && refreshed3.freshnessMs === 60000);
|
|
1545
|
+
|
|
1546
|
+
// A shared-cache Authorization-bearing entry whose 304 drops the opt-in
|
|
1547
|
+
// is evicted; a throwing store.delete on that eviction is drop-silent and
|
|
1548
|
+
// the freshly-revalidated body is still returned to the current caller.
|
|
1549
|
+
var delThrowStore = {
|
|
1550
|
+
get: function () { return null; }, set: function () {},
|
|
1551
|
+
delete: function () { throw new Error("delete boom"); }, clear: function () {},
|
|
1552
|
+
};
|
|
1553
|
+
var cache4 = cacheNs.create({ store: delThrowStore, sharedCache: true });
|
|
1554
|
+
var refresh4Ok = true;
|
|
1555
|
+
var refreshed4;
|
|
1556
|
+
try {
|
|
1557
|
+
// hadAuthorization absent → §3.5 fail-closed treats it as authenticated.
|
|
1558
|
+
refreshed4 = cache4._refreshFrom304(
|
|
1559
|
+
_storedEntryFixture({ hadAuthorization: undefined }),
|
|
1560
|
+
{ "cache-control": "max-age=60", "date": _httpDate(Date.now()) });
|
|
1561
|
+
} catch (_e) { refresh4Ok = false; }
|
|
1562
|
+
check("_refreshFrom304: eviction store.delete throw swallowed, body still returned",
|
|
1563
|
+
refresh4Ok && refreshed4.body.toString("utf8") === "stored-body");
|
|
1564
|
+
}
|
|
1565
|
+
|
|
678
1566
|
// ---- Run ----------------------------------------------------------------
|
|
679
1567
|
|
|
680
1568
|
async function run() {
|
|
@@ -697,6 +1585,26 @@ async function run() {
|
|
|
697
1585
|
await testInvalidateInspectClear();
|
|
698
1586
|
await testAuditAndObservabilityWired();
|
|
699
1587
|
await test304MergesHeaders();
|
|
1588
|
+
|
|
1589
|
+
// Branch-coverage additions — error paths, adversarial inputs,
|
|
1590
|
+
// fallbacks, and the RFC 9111 §3.5 shared-cache guard.
|
|
1591
|
+
testParseCacheControlBranches();
|
|
1592
|
+
testEvaluateStorageBranches();
|
|
1593
|
+
testEvaluateStorageAuthAndSMaxage();
|
|
1594
|
+
testUrlKeyAndVaryHelpers();
|
|
1595
|
+
testMemoryStoreDirectBranches();
|
|
1596
|
+
testEmitAndObsSinkBranches();
|
|
1597
|
+
testInspectInvalidateAndStoreMissBranches();
|
|
1598
|
+
await testVaryLookupMissingRequestHeader();
|
|
1599
|
+
testLegacyEntryFailClosedInSharedCache();
|
|
1600
|
+
await testAuthorizationSharedCacheLeakPrevented();
|
|
1601
|
+
await testUncacheableStatusAndVaryStarBypass();
|
|
1602
|
+
await testExpiresAndSMaxageFreshness();
|
|
1603
|
+
await testValidatorOnlyAndNoValidatorStorage();
|
|
1604
|
+
await testEntryTooLargeRefusedOverNetwork();
|
|
1605
|
+
await testOperatorStoreFaultTolerance();
|
|
1606
|
+
await testRefreshFrom304DropsShareOptInAuthEvict();
|
|
1607
|
+
testRefreshFrom304DirectBranches();
|
|
700
1608
|
} finally {
|
|
701
1609
|
// Tear down the httpClient's keep-alive transport pool so the
|
|
702
1610
|
// client-side sockets it cached don't outlive run() and keep the
|