@blamejs/blamejs-shop 0.4.30 → 0.4.32
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 +4 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/checkout.js +8 -0
- package/lib/order.js +71 -11
- package/lib/vendor/MANIFEST.json +392 -278
- package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
- package/lib/vendor/blamejs/.gitignore +6 -0
- package/lib/vendor/blamejs/CHANGELOG.md +26 -0
- package/lib/vendor/blamejs/MIGRATING.md +43 -0
- package/lib/vendor/blamejs/README.md +8 -6
- package/lib/vendor/blamejs/SECURITY.md +19 -3
- package/lib/vendor/blamejs/api-snapshot.json +2190 -664
- package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
- package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
- package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
- package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
- package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
- package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
- package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
- package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
- package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
- package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
- package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
- package/lib/vendor/blamejs/index.js +4 -0
- package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
- package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
- package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
- package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
- package/lib/vendor/blamejs/lib/api-key.js +158 -77
- package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
- package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
- package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
- package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
- package/lib/vendor/blamejs/lib/audit.js +259 -123
- package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
- package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
- package/lib/vendor/blamejs/lib/backup/index.js +45 -10
- package/lib/vendor/blamejs/lib/break-glass.js +355 -147
- package/lib/vendor/blamejs/lib/cache.js +174 -105
- package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
- package/lib/vendor/blamejs/lib/cli.js +19 -14
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
- package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
- package/lib/vendor/blamejs/lib/cluster.js +119 -71
- package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
- package/lib/vendor/blamejs/lib/compliance.js +206 -4
- package/lib/vendor/blamejs/lib/consent.js +82 -29
- package/lib/vendor/blamejs/lib/constants.js +27 -11
- package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
- package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
- package/lib/vendor/blamejs/lib/db-query.js +882 -260
- package/lib/vendor/blamejs/lib/db-schema.js +228 -44
- package/lib/vendor/blamejs/lib/db.js +249 -99
- package/lib/vendor/blamejs/lib/dsr.js +385 -55
- package/lib/vendor/blamejs/lib/error-page.js +14 -1
- package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
- package/lib/vendor/blamejs/lib/external-db.js +549 -34
- package/lib/vendor/blamejs/lib/file-upload.js +52 -7
- package/lib/vendor/blamejs/lib/framework-error.js +20 -1
- package/lib/vendor/blamejs/lib/framework-files.js +73 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
- package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
- package/lib/vendor/blamejs/lib/guard-all.js +1 -0
- package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
- package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
- package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
- package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
- package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
- package/lib/vendor/blamejs/lib/guard-email.js +47 -69
- package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
- package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
- package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
- package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
- package/lib/vendor/blamejs/lib/guard-html.js +53 -108
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
- package/lib/vendor/blamejs/lib/guard-image.js +46 -103
- package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
- package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
- package/lib/vendor/blamejs/lib/guard-json.js +38 -108
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
- package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
- package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
- package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
- package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
- package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
- package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
- package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
- package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
- package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
- package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
- package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
- package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
- package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
- package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
- package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
- package/lib/vendor/blamejs/lib/guard-template.js +35 -172
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
- package/lib/vendor/blamejs/lib/guard-time.js +32 -154
- package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
- package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
- package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
- package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
- package/lib/vendor/blamejs/lib/http-client.js +37 -9
- package/lib/vendor/blamejs/lib/inbox.js +120 -107
- package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
- package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
- package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
- package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
- package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
- package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
- package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
- package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
- package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
- package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
- package/lib/vendor/blamejs/lib/mail-store.js +293 -154
- package/lib/vendor/blamejs/lib/mail.js +8 -4
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
- package/lib/vendor/blamejs/lib/migrations.js +108 -66
- package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
- package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
- package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
- package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
- package/lib/vendor/blamejs/lib/observability.js +124 -0
- package/lib/vendor/blamejs/lib/otel-export.js +12 -3
- package/lib/vendor/blamejs/lib/outbox.js +184 -83
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
- package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
- package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
- package/lib/vendor/blamejs/lib/queue-local.js +225 -140
- package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
- package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
- package/lib/vendor/blamejs/lib/queue.js +7 -0
- package/lib/vendor/blamejs/lib/redact.js +68 -11
- package/lib/vendor/blamejs/lib/redis-client.js +160 -31
- package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
- package/lib/vendor/blamejs/lib/retention.js +101 -40
- package/lib/vendor/blamejs/lib/router.js +212 -5
- package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
- package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
- package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
- package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
- package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
- package/lib/vendor/blamejs/lib/safe-url.js +170 -3
- package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
- package/lib/vendor/blamejs/lib/scheduler.js +35 -12
- package/lib/vendor/blamejs/lib/seeders.js +122 -74
- package/lib/vendor/blamejs/lib/session-stores.js +42 -14
- package/lib/vendor/blamejs/lib/session.js +175 -77
- package/lib/vendor/blamejs/lib/sql.js +3842 -0
- package/lib/vendor/blamejs/lib/sse.js +26 -0
- package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
- package/lib/vendor/blamejs/lib/static.js +177 -34
- package/lib/vendor/blamejs/lib/subject.js +96 -49
- package/lib/vendor/blamejs/lib/vault/index.js +3 -2
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
- package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
- package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
- package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
- package/lib/vendor/blamejs/lib/websocket.js +35 -5
- package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
- package/lib/vendor/blamejs/package.json +2 -2
- package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
- package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
- package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
- package/lib/vendor/blamejs/scripts/check-services.js +21 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
- package/lib/vendor/blamejs/scripts/release.js +398 -38
- package/lib/vendor/blamejs/test/00-primitives.js +117 -0
- package/lib/vendor/blamejs/test/10-state.js +140 -14
- package/lib/vendor/blamejs/test/20-db.js +65 -2
- package/lib/vendor/blamejs/test/helpers/db.js +9 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
- package/lib/vendor/blamejs/test/helpers/services.js +21 -0
- package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
- package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
- package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
- package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
- package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
- package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
- package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
- package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
- package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
- package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
- package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
- package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
- package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
- package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
- package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
- package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
- package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
- package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
- package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
- package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
- package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
- package/lib/vendor/blamejs/test/smoke.js +79 -21
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
- package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
- package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
- package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
- package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
- package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
- package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
- package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
- package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
- package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
- package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
- package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
- package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
- package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.auth.saml.sp.verifyResponse — SubjectConfirmationData NotOnOrAfter
|
|
4
|
+
* is mandatory and must fail closed.
|
|
5
|
+
*
|
|
6
|
+
* SAML 2.0 Web Browser SSO Profile §4.1.4.2 requires every Bearer
|
|
7
|
+
* SubjectConfirmationData to carry a `NotOnOrAfter` attribute that
|
|
8
|
+
* bounds the assertion's freshness window. A SubjectConfirmationData
|
|
9
|
+
* with no NotOnOrAfter — or an unparseable one — must be rejected:
|
|
10
|
+
* accepting it grants an unbounded, replay-forever confirmation. The
|
|
11
|
+
* Holder-of-Key confirmation (Profile §3.1, which incorporates the
|
|
12
|
+
* §3 time-bounding by reference) has the same requirement.
|
|
13
|
+
*
|
|
14
|
+
* These tests drive the shipped consumer path:
|
|
15
|
+
* sp = b.auth.saml.sp.create({ ... }); sp.verifyResponse(b64, vopts).
|
|
16
|
+
* They mint a self-signed RSA IdP cert, build a genuinely XMLDSig-
|
|
17
|
+
* signed SAML Response (digest + SignatureValue computed through the
|
|
18
|
+
* framework's own b.xmlC14n so the verifier's recomputation matches),
|
|
19
|
+
* and vary ONLY the SubjectConfirmationData's NotOnOrAfter between the
|
|
20
|
+
* accepted and refused cases.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
var helpers = require("../helpers");
|
|
24
|
+
var check = helpers.check;
|
|
25
|
+
var b = helpers.b;
|
|
26
|
+
var nodeCrypto = require("node:crypto");
|
|
27
|
+
var c14n = require("../../lib/xml-c14n");
|
|
28
|
+
|
|
29
|
+
var DS = "http://www.w3.org/2000/09/xmldsig#";
|
|
30
|
+
var EXC = "http://www.w3.org/2001/10/xml-exc-c14n#";
|
|
31
|
+
|
|
32
|
+
var IDP_ENTITY_ID = "https://idp.example";
|
|
33
|
+
var SP_ENTITY_ID = "https://sp.example";
|
|
34
|
+
var ACS_URL = "https://sp.example/saml/acs";
|
|
35
|
+
|
|
36
|
+
// Mint a self-signed RSA cert via the vendored @peculiar/x509 bundle.
|
|
37
|
+
// verifyResponse parses idpCertPem with nodeCrypto.createPublicKey and
|
|
38
|
+
// verifies an rsa-sha256 PKCS1 signature, so a real RSA cert + matching
|
|
39
|
+
// private key is required — there is no test bypass of the signature
|
|
40
|
+
// check.
|
|
41
|
+
async function _mintRsaCert(cn) {
|
|
42
|
+
var pki = require("../../lib/vendor/pki.cjs");
|
|
43
|
+
var x509 = pki.x509;
|
|
44
|
+
var keys = await nodeCrypto.webcrypto.subtle.generateKey(
|
|
45
|
+
{ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, // allow:raw-byte-literal — RFC 8301 §3.1 RSA bit floor
|
|
46
|
+
publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" },
|
|
47
|
+
true, ["sign", "verify"]);
|
|
48
|
+
var now = new Date();
|
|
49
|
+
var cert = await x509.X509CertificateGenerator.createSelfSigned({
|
|
50
|
+
serialNumber: "01",
|
|
51
|
+
name: "CN=" + cn,
|
|
52
|
+
notBefore: now,
|
|
53
|
+
notAfter: new Date(now.getTime() + 365 * 24 * 3600 * 1000), // allow:raw-time-literal — 1y fixture validity
|
|
54
|
+
signingAlgorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
|
|
55
|
+
keys: keys,
|
|
56
|
+
});
|
|
57
|
+
var pkcs8 = await nodeCrypto.webcrypto.subtle.exportKey("pkcs8", keys.privateKey);
|
|
58
|
+
var keyPem = "-----BEGIN PRIVATE KEY-----\n" +
|
|
59
|
+
Buffer.from(pkcs8).toString("base64").match(/.{1,64}/g).join("\n") +
|
|
60
|
+
"\n-----END PRIVATE KEY-----\n";
|
|
61
|
+
return { certPem: cert.toString("pem"), keyPem: keyPem };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function _isoFromNow(ms) { return new Date(Date.now() + ms).toISOString(); }
|
|
65
|
+
|
|
66
|
+
// Build a SAML Response with an Assertion-level enveloped XMLDSig
|
|
67
|
+
// signature. The Assertion's Signature child is stripped before the
|
|
68
|
+
// digest (the enveloped-signature transform), and both the digest and
|
|
69
|
+
// the SignedInfo are canonicalized through b.xmlC14n so the values
|
|
70
|
+
// match exactly what verifyResponse recomputes.
|
|
71
|
+
function _buildSignedResponse(idp, parts) {
|
|
72
|
+
var assertionId = "_assertion-" + parts.tag;
|
|
73
|
+
var responseId = "_response-" + parts.tag;
|
|
74
|
+
var issueInstant = _isoFromNow(0);
|
|
75
|
+
var subjectConfirmation =
|
|
76
|
+
"<saml:SubjectConfirmation Method=\"" + parts.method + "\">" +
|
|
77
|
+
parts.scd +
|
|
78
|
+
"</saml:SubjectConfirmation>";
|
|
79
|
+
|
|
80
|
+
var assertionInner =
|
|
81
|
+
"<saml:Issuer>" + IDP_ENTITY_ID + "</saml:Issuer>" +
|
|
82
|
+
"SIGNATURE_PLACEHOLDER" +
|
|
83
|
+
"<saml:Subject>" +
|
|
84
|
+
"<saml:NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">" +
|
|
85
|
+
parts.nameId + "</saml:NameID>" +
|
|
86
|
+
subjectConfirmation +
|
|
87
|
+
"</saml:Subject>" +
|
|
88
|
+
"<saml:Conditions NotBefore=\"" + _isoFromNow(-5 * 60 * 1000) + // allow:raw-time-literal — 5m skew window
|
|
89
|
+
"\" NotOnOrAfter=\"" + _isoFromNow(5 * 60 * 1000) + "\">" + // allow:raw-time-literal — 5m skew window
|
|
90
|
+
"<saml:AudienceRestriction><saml:Audience>" + SP_ENTITY_ID +
|
|
91
|
+
"</saml:Audience></saml:AudienceRestriction>" +
|
|
92
|
+
"</saml:Conditions>" +
|
|
93
|
+
"<saml:AuthnStatement SessionIndex=\"_sess-1\" AuthnInstant=\"" + issueInstant + "\">" +
|
|
94
|
+
"<saml:AuthnContext><saml:AuthnContextClassRef>" +
|
|
95
|
+
"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" +
|
|
96
|
+
"</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement>";
|
|
97
|
+
|
|
98
|
+
var assertionOpen =
|
|
99
|
+
"<saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" " +
|
|
100
|
+
"ID=\"" + assertionId + "\" Version=\"2.0\" IssueInstant=\"" + issueInstant + "\">";
|
|
101
|
+
var assertionClose = "</saml:Assertion>";
|
|
102
|
+
|
|
103
|
+
// Digest the assertion with no Signature child (enveloped-signature
|
|
104
|
+
// transform output), canonicalized through b.xmlC14n.
|
|
105
|
+
var assertionNoSig = assertionOpen +
|
|
106
|
+
assertionInner.replace("SIGNATURE_PLACEHOLDER", "") + assertionClose;
|
|
107
|
+
var digest = nodeCrypto.createHash("sha256")
|
|
108
|
+
.update(c14n.canonicalize(assertionNoSig)).digest("base64");
|
|
109
|
+
|
|
110
|
+
var signedInfo =
|
|
111
|
+
"<ds:SignedInfo xmlns:ds=\"" + DS + "\">" +
|
|
112
|
+
"<ds:CanonicalizationMethod Algorithm=\"" + EXC + "\"></ds:CanonicalizationMethod>" +
|
|
113
|
+
"<ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"></ds:SignatureMethod>" +
|
|
114
|
+
"<ds:Reference URI=\"#" + assertionId + "\">" +
|
|
115
|
+
"<ds:Transforms>" +
|
|
116
|
+
"<ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"></ds:Transform>" +
|
|
117
|
+
"<ds:Transform Algorithm=\"" + EXC + "\"></ds:Transform>" +
|
|
118
|
+
"</ds:Transforms>" +
|
|
119
|
+
"<ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"></ds:DigestMethod>" +
|
|
120
|
+
"<ds:DigestValue>" + digest + "</ds:DigestValue>" +
|
|
121
|
+
"</ds:Reference>" +
|
|
122
|
+
"</ds:SignedInfo>";
|
|
123
|
+
var priv = nodeCrypto.createPrivateKey({ key: idp.keyPem, format: "pem" });
|
|
124
|
+
var sigValue = nodeCrypto.sign("sha256", c14n.canonicalize(signedInfo),
|
|
125
|
+
{ key: priv, padding: nodeCrypto.constants.RSA_PKCS1_PADDING }).toString("base64");
|
|
126
|
+
|
|
127
|
+
var signatureXml =
|
|
128
|
+
"<ds:Signature xmlns:ds=\"" + DS + "\">" + signedInfo +
|
|
129
|
+
"<ds:SignatureValue>" + sigValue + "</ds:SignatureValue></ds:Signature>";
|
|
130
|
+
var assertionFull = assertionOpen +
|
|
131
|
+
assertionInner.replace("SIGNATURE_PLACEHOLDER", signatureXml) + assertionClose;
|
|
132
|
+
|
|
133
|
+
var response =
|
|
134
|
+
"<samlp:Response xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
|
|
135
|
+
"xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" " +
|
|
136
|
+
"ID=\"" + responseId + "\" Version=\"2.0\" IssueInstant=\"" + issueInstant + "\" " +
|
|
137
|
+
"Destination=\"" + ACS_URL + "\">" +
|
|
138
|
+
"<saml:Issuer>" + IDP_ENTITY_ID + "</saml:Issuer>" +
|
|
139
|
+
"<samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/></samlp:Status>" +
|
|
140
|
+
assertionFull + "</samlp:Response>";
|
|
141
|
+
|
|
142
|
+
return Buffer.from(response, "utf8").toString("base64");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function _bearerScd(notOnOrAfterAttr, inResponseTo) {
|
|
146
|
+
return "<saml:SubjectConfirmationData" + notOnOrAfterAttr +
|
|
147
|
+
" Recipient=\"" + ACS_URL + "\"" +
|
|
148
|
+
" InResponseTo=\"" + inResponseTo + "\"/>";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function _certBodyB64(pem) {
|
|
152
|
+
return pem.replace(/-----BEGIN CERTIFICATE-----/, "")
|
|
153
|
+
.replace(/-----END CERTIFICATE-----/, "")
|
|
154
|
+
.replace(/\s+/g, "");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function _hokScd(notOnOrAfterAttr, holderCertPem) {
|
|
158
|
+
return "<saml:SubjectConfirmationData" + notOnOrAfterAttr +
|
|
159
|
+
" Recipient=\"" + ACS_URL + "\">" +
|
|
160
|
+
"<ds:KeyInfo xmlns:ds=\"" + DS + "\"><ds:X509Data><ds:X509Certificate>" +
|
|
161
|
+
_certBodyB64(holderCertPem) +
|
|
162
|
+
"</ds:X509Certificate></ds:X509Data></ds:KeyInfo>" +
|
|
163
|
+
"</saml:SubjectConfirmationData>";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function _newSp(idp) {
|
|
167
|
+
return b.auth.saml.sp.create({
|
|
168
|
+
entityId: SP_ENTITY_ID,
|
|
169
|
+
assertionConsumerServiceUrl: ACS_URL,
|
|
170
|
+
idpEntityId: IDP_ENTITY_ID,
|
|
171
|
+
idpSsoUrl: "https://idp.example/sso",
|
|
172
|
+
idpCertPem: idp.certPem,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function _verifyThrows(sp, b64, vopts) {
|
|
177
|
+
try { sp.verifyResponse(b64, vopts); return null; }
|
|
178
|
+
catch (e) { return e.code || e.message; }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Case 1 — a valid future NotOnOrAfter succeeds. Proves the signer +
|
|
182
|
+
// harness are correct and the fail-closed fix does not break the
|
|
183
|
+
// happy path.
|
|
184
|
+
async function testBearerValidNotOnOrAfterAccepted() {
|
|
185
|
+
var idp = await _mintRsaCert("idp.example");
|
|
186
|
+
var sp = _newSp(idp);
|
|
187
|
+
var inResponseTo = "_req-valid";
|
|
188
|
+
var b64 = _buildSignedResponse(idp, {
|
|
189
|
+
tag: "bearer-valid",
|
|
190
|
+
method: "urn:oasis:names:tc:SAML:2.0:cm:bearer",
|
|
191
|
+
nameId: "alice@example.com",
|
|
192
|
+
scd: _bearerScd(" NotOnOrAfter=\"" + _isoFromNow(5 * 60 * 1000) + "\"", inResponseTo), // allow:raw-time-literal — 5m future
|
|
193
|
+
});
|
|
194
|
+
var info = sp.verifyResponse(b64, { expectedInResponseTo: inResponseTo });
|
|
195
|
+
check("Bearer with valid NotOnOrAfter verifies", info && typeof info === "object");
|
|
196
|
+
check("Bearer valid: nameId returned", info.nameId === "alice@example.com");
|
|
197
|
+
check("Bearer valid: issuer matches IdP entityID", info.issuer === IDP_ENTITY_ID);
|
|
198
|
+
check("Bearer valid: inResponseTo captured", info.inResponseTo === inResponseTo);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Case 2 — a Bearer SubjectConfirmationData with NO NotOnOrAfter must
|
|
202
|
+
// be refused (§4.1.4.2). This is the RED case: on the unfixed tree the
|
|
203
|
+
// confirmation is wrongly accepted as fresh-forever.
|
|
204
|
+
async function testBearerMissingNotOnOrAfterRefused() {
|
|
205
|
+
var idp = await _mintRsaCert("idp.example");
|
|
206
|
+
var sp = _newSp(idp);
|
|
207
|
+
var inResponseTo = "_req-missing";
|
|
208
|
+
var b64 = _buildSignedResponse(idp, {
|
|
209
|
+
tag: "bearer-missing",
|
|
210
|
+
method: "urn:oasis:names:tc:SAML:2.0:cm:bearer",
|
|
211
|
+
nameId: "alice@example.com",
|
|
212
|
+
scd: _bearerScd("", inResponseTo),
|
|
213
|
+
});
|
|
214
|
+
var code = _verifyThrows(sp, b64, { expectedInResponseTo: inResponseTo });
|
|
215
|
+
check("Bearer missing NotOnOrAfter is refused",
|
|
216
|
+
code === "auth-saml/no-valid-confirmation");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Case 3 — a Bearer SubjectConfirmationData with an UNPARSEABLE
|
|
220
|
+
// NotOnOrAfter must be refused (it cannot bound freshness).
|
|
221
|
+
async function testBearerUnparseableNotOnOrAfterRefused() {
|
|
222
|
+
var idp = await _mintRsaCert("idp.example");
|
|
223
|
+
var sp = _newSp(idp);
|
|
224
|
+
var inResponseTo = "_req-bad";
|
|
225
|
+
var b64 = _buildSignedResponse(idp, {
|
|
226
|
+
tag: "bearer-bad",
|
|
227
|
+
method: "urn:oasis:names:tc:SAML:2.0:cm:bearer",
|
|
228
|
+
nameId: "alice@example.com",
|
|
229
|
+
scd: _bearerScd(" NotOnOrAfter=\"not-a-date\"", inResponseTo),
|
|
230
|
+
});
|
|
231
|
+
var code = _verifyThrows(sp, b64, { expectedInResponseTo: inResponseTo });
|
|
232
|
+
check("Bearer unparseable NotOnOrAfter is refused",
|
|
233
|
+
code === "auth-saml/no-valid-confirmation");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Case 4 — the Holder-of-Key sibling: an HoK SubjectConfirmationData
|
|
237
|
+
// missing NotOnOrAfter (and one with an unparseable value) must be
|
|
238
|
+
// refused. On the unfixed tree both are wrongly accepted (the missing
|
|
239
|
+
// case skips the check; the unparseable case is masked by an `&&`
|
|
240
|
+
// short-circuit).
|
|
241
|
+
async function testHolderOfKeyNotOnOrAfterRefused() {
|
|
242
|
+
var idp = await _mintRsaCert("idp.example");
|
|
243
|
+
var holder = await _mintRsaCert("holder.example");
|
|
244
|
+
var sp = _newSp(idp);
|
|
245
|
+
var vopts = { holderOfKey: { presentedCertPem: holder.certPem } };
|
|
246
|
+
|
|
247
|
+
// Sanity — a valid HoK NotOnOrAfter verifies (harness correctness).
|
|
248
|
+
var b64ok = _buildSignedResponse(idp, {
|
|
249
|
+
tag: "hok-valid",
|
|
250
|
+
method: "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key",
|
|
251
|
+
nameId: "bob@example.com",
|
|
252
|
+
scd: _hokScd(" NotOnOrAfter=\"" + _isoFromNow(5 * 60 * 1000) + "\"", holder.certPem), // allow:raw-time-literal — 5m future
|
|
253
|
+
});
|
|
254
|
+
var info = sp.verifyResponse(b64ok, vopts);
|
|
255
|
+
check("HoK with valid NotOnOrAfter verifies", info && info.nameId === "bob@example.com");
|
|
256
|
+
|
|
257
|
+
var b64missing = _buildSignedResponse(idp, {
|
|
258
|
+
tag: "hok-missing",
|
|
259
|
+
method: "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key",
|
|
260
|
+
nameId: "bob@example.com",
|
|
261
|
+
scd: _hokScd("", holder.certPem),
|
|
262
|
+
});
|
|
263
|
+
check("HoK missing NotOnOrAfter is refused",
|
|
264
|
+
_verifyThrows(sp, b64missing, vopts) === "auth-saml/no-valid-confirmation");
|
|
265
|
+
|
|
266
|
+
var b64bad = _buildSignedResponse(idp, {
|
|
267
|
+
tag: "hok-bad",
|
|
268
|
+
method: "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key",
|
|
269
|
+
nameId: "bob@example.com",
|
|
270
|
+
scd: _hokScd(" NotOnOrAfter=\"not-a-date\"", holder.certPem),
|
|
271
|
+
});
|
|
272
|
+
check("HoK unparseable NotOnOrAfter is refused",
|
|
273
|
+
_verifyThrows(sp, b64bad, vopts) === "auth-saml/no-valid-confirmation");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async function run() {
|
|
277
|
+
await testBearerValidNotOnOrAfterAccepted();
|
|
278
|
+
await testBearerMissingNotOnOrAfterRefused();
|
|
279
|
+
await testBearerUnparseableNotOnOrAfterRefused();
|
|
280
|
+
await testHolderOfKeyNotOnOrAfterRefused();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (require.main === module) {
|
|
284
|
+
run().then(function () { process.exit(0); })
|
|
285
|
+
.catch(function (e) { console.error(e); process.exit(1); });
|
|
286
|
+
}
|
|
287
|
+
module.exports = { run: run };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #135: SD-JWT-VC ES256/ES384 signatures must be JOSE-encoded (raw r||s,
|
|
3
|
+
// "ieee-p1363"), not node:crypto's default DER. A DER signature (ASN.1
|
|
4
|
+
// SEQUENCE, leading 0x30, ~70-72 bytes for P-256) is rejected by every
|
|
5
|
+
// conformant JOSE / EUDI-wallet verifier, and this library would likewise
|
|
6
|
+
// reject a conformant wallet's raw-r||s signature. The issuer JWT and the
|
|
7
|
+
// holder KB-JWT both sign through the core _signJwt, so the format applies
|
|
8
|
+
// to both.
|
|
9
|
+
//
|
|
10
|
+
// RED on the buggy tree: the issuer-JWT signature is DER (length != 64,
|
|
11
|
+
// first byte 0x30) and a JOSE-conformant verifier (dsaEncoding ieee-p1363)
|
|
12
|
+
// rejects it. GREEN after the fix: signature is exactly 64 bytes and the
|
|
13
|
+
// conformant verifier accepts it.
|
|
14
|
+
|
|
15
|
+
var helpers = require("../helpers");
|
|
16
|
+
var b = helpers.b;
|
|
17
|
+
var check = helpers.check;
|
|
18
|
+
var nodeCrypto = require("node:crypto");
|
|
19
|
+
var sdJwtVc = b.auth.sdJwtVc;
|
|
20
|
+
|
|
21
|
+
function _b64uToBuf(s) {
|
|
22
|
+
return Buffer.from(s, "base64url");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function run() {
|
|
26
|
+
var issuer = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
27
|
+
|
|
28
|
+
var sd = sdJwtVc.issue({
|
|
29
|
+
issuer: "https://issuer.example.com",
|
|
30
|
+
subject: "did:web:alice",
|
|
31
|
+
vct: "https://example.com/vct/identity",
|
|
32
|
+
claims: { given_name: "Alice", country: "US" },
|
|
33
|
+
selectivelyDisclosed: ["given_name"],
|
|
34
|
+
issuerKey: issuer.privateKey,
|
|
35
|
+
algorithm: "ES256",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// An SD-JWT serializes as <issuerJWT>~<disclosure>~…~ — strip the
|
|
39
|
+
// tilde-joined disclosures to get the issuer JWT before splitting on ".".
|
|
40
|
+
var issuerJwt = sd.token.split("~")[0];
|
|
41
|
+
var parts = issuerJwt.split(".");
|
|
42
|
+
check("issuer JWT has 3 parts", parts.length === 3);
|
|
43
|
+
var signingInput = parts[0] + "." + parts[1];
|
|
44
|
+
var sig = _b64uToBuf(parts[2]);
|
|
45
|
+
|
|
46
|
+
// ES256 raw r||s is exactly 64 bytes; DER is variable (~70-72) and starts
|
|
47
|
+
// with the 0x30 SEQUENCE tag.
|
|
48
|
+
check("#135 ES256 signature is JOSE raw r||s (64 bytes), not DER",
|
|
49
|
+
sig.length === 64);
|
|
50
|
+
check("#135 ES256 signature does not carry the DER SEQUENCE tag",
|
|
51
|
+
sig[0] !== 0x30 || sig.length === 64);
|
|
52
|
+
|
|
53
|
+
// A JOSE-conformant verifier (explicit ieee-p1363) must accept it. On the
|
|
54
|
+
// buggy tree the signature is DER, so a conformant verifier rejects it.
|
|
55
|
+
var conformantOk = nodeCrypto.verify(
|
|
56
|
+
"sha256",
|
|
57
|
+
Buffer.from(signingInput, "ascii"),
|
|
58
|
+
{ key: issuer.publicKey, dsaEncoding: "ieee-p1363" },
|
|
59
|
+
sig);
|
|
60
|
+
check("#135 a JOSE-conformant verifier (ieee-p1363) accepts the issuer JWT",
|
|
61
|
+
conformantOk === true);
|
|
62
|
+
|
|
63
|
+
// ES384 carries the same root — exercise it too (96-byte raw r||s).
|
|
64
|
+
var issuer384 = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "P-384" });
|
|
65
|
+
var sd384 = sdJwtVc.issue({
|
|
66
|
+
issuer: "https://issuer.example.com",
|
|
67
|
+
subject: "did:web:carol",
|
|
68
|
+
vct: "https://example.com/vct/identity",
|
|
69
|
+
claims: { given_name: "Carol" },
|
|
70
|
+
selectivelyDisclosed: ["given_name"],
|
|
71
|
+
issuerKey: issuer384.privateKey,
|
|
72
|
+
algorithm: "ES384",
|
|
73
|
+
});
|
|
74
|
+
var sig384 = _b64uToBuf(sd384.token.split("~")[0].split(".")[2]);
|
|
75
|
+
check("#135 ES384 signature is JOSE raw r||s (96 bytes), not DER",
|
|
76
|
+
sig384.length === 96);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { run: run };
|
|
@@ -695,6 +695,55 @@ async function testHolderAlgFromKeyType() {
|
|
|
695
695
|
rsaThrew && rsaThrew.code === "auth-sd-jwt-vc/holder-key-unsupported");
|
|
696
696
|
}
|
|
697
697
|
|
|
698
|
+
// ---- FIX 4B: holder KB-JWT alg/kty cross-check ----
|
|
699
|
+
|
|
700
|
+
// The KB-JWT header alg is attacker-controllable (the holder mints the
|
|
701
|
+
// KB-JWT). The verifier must cross-check it against the holder's cnf.jwk
|
|
702
|
+
// key type BEFORE handing bytes to node:crypto.verify — the same
|
|
703
|
+
// CVE-2026-22817 defense the issuer path applies. An EC cnf key with a
|
|
704
|
+
// KB-JWT header claiming EdDSA (which requires kty=OKP) must be refused
|
|
705
|
+
// with the precise alg-mismatch error.
|
|
706
|
+
async function testKbAlgKtyCrossCheck() {
|
|
707
|
+
var issuer = _newKeyPair();
|
|
708
|
+
var holder = _newKeyPair(); // EC P-256
|
|
709
|
+
var sd = sdJwtVc.issue({
|
|
710
|
+
issuer: "https://issuer", vct: "x",
|
|
711
|
+
claims: { given_name: "Alice" },
|
|
712
|
+
selectivelyDisclosed: ["given_name"],
|
|
713
|
+
issuerKey: issuer.privateKey,
|
|
714
|
+
holderKey: _jwk(holder.publicKey),
|
|
715
|
+
});
|
|
716
|
+
// Build a valid presentation, then replace its real KB-JWT with a forged
|
|
717
|
+
// one whose header declares EdDSA against the EC holder key.
|
|
718
|
+
var pres = sdJwtVc.present({
|
|
719
|
+
sdJwt: sd.token,
|
|
720
|
+
disclosedClaimNames: ["given_name"],
|
|
721
|
+
audience: "https://verifier",
|
|
722
|
+
nonce: "n-1",
|
|
723
|
+
holderKey: holder.privateKey,
|
|
724
|
+
algorithm: "ES256",
|
|
725
|
+
});
|
|
726
|
+
var segs = pres.presentation.split("~");
|
|
727
|
+
// Forge a KB-JWT segment: EdDSA header (OKP), EC holder key → mismatch.
|
|
728
|
+
function b64u(obj) { return Buffer.from(JSON.stringify(obj), "utf8").toString("base64url"); }
|
|
729
|
+
var forgedHeader = b64u({ typ: "kb+jwt", alg: "EdDSA" });
|
|
730
|
+
var forgedPayload = b64u({ aud: "https://verifier", nonce: "n-1", iat: Math.floor(Date.now() / 1000), sd_hash: "x" });
|
|
731
|
+
segs[segs.length - 1] = forgedHeader + "." + forgedPayload + ".AAAA";
|
|
732
|
+
var forgedPres = segs.join("~");
|
|
733
|
+
|
|
734
|
+
var threw = null;
|
|
735
|
+
try {
|
|
736
|
+
await sdJwtVc.verify(forgedPres, {
|
|
737
|
+
issuerKeyResolver: async function () { return issuer.publicKey; },
|
|
738
|
+
audience: "https://verifier",
|
|
739
|
+
nonce: "n-1",
|
|
740
|
+
requireKeyBinding: true,
|
|
741
|
+
});
|
|
742
|
+
} catch (e) { threw = e; }
|
|
743
|
+
check("KB alg/kty: mismatched KB-JWT alg refused with precise error",
|
|
744
|
+
threw && threw.code === "auth-jwt-external/alg-kty-mismatch");
|
|
745
|
+
}
|
|
746
|
+
|
|
698
747
|
// ---- Module exports ----
|
|
699
748
|
|
|
700
749
|
function testExports() {
|
|
@@ -742,5 +791,6 @@ function testExports() {
|
|
|
742
791
|
await testHolderPresentNonexistent();
|
|
743
792
|
testHolderValidation();
|
|
744
793
|
await testHolderAlgFromKeyType();
|
|
794
|
+
await testKbAlgKtyCrossCheck();
|
|
745
795
|
testExports();
|
|
746
796
|
})().catch(function (e) { console.error(e); process.exit(1); });
|
|
@@ -168,8 +168,9 @@ function testReportOnlyHeadersEmittedWhenOptedIn() {
|
|
|
168
168
|
|
|
169
169
|
function testReportOnlyDoesNotTouchEnforcingHeaders() {
|
|
170
170
|
// Monitor-mode opt-ins must not alter the enforcing COOP / COEP /
|
|
171
|
-
// Document-Policy headers: COOP stays same-origin,
|
|
172
|
-
// default
|
|
171
|
+
// Document-Policy headers: COOP stays same-origin, the enforcing COEP
|
|
172
|
+
// keeps its default-on `credentialless` value (the report-only opt is a
|
|
173
|
+
// separate header), Document-Policy keeps its enforcing default.
|
|
173
174
|
var mw = b.middleware.securityHeaders({
|
|
174
175
|
coopReportOnly: "same-origin",
|
|
175
176
|
coepReportOnly: "require-corp",
|
|
@@ -178,8 +179,8 @@ function testReportOnlyDoesNotTouchEnforcingHeaders() {
|
|
|
178
179
|
mw({ headers: {} }, res, function () {});
|
|
179
180
|
check("enforcing COOP unchanged by coopReportOnly",
|
|
180
181
|
res._hdrs["Cross-Origin-Opener-Policy"] === "same-origin");
|
|
181
|
-
check("COEP
|
|
182
|
-
res._hdrs["Cross-Origin-Embedder-Policy"] ===
|
|
182
|
+
check("enforcing COEP keeps its default-on value despite coepReportOnly",
|
|
183
|
+
res._hdrs["Cross-Origin-Embedder-Policy"] === "credentialless");
|
|
183
184
|
check("enforcing Document-Policy unchanged by report-only opts",
|
|
184
185
|
res._hdrs["Document-Policy"] === b.middleware._modules.securityHeaders.DEFAULT_DOCUMENT_POLICY);
|
|
185
186
|
}
|
|
@@ -254,6 +255,31 @@ function testUnknownOptStillRefused() {
|
|
|
254
255
|
check("typo'd report-only opt refused at config-time", threw);
|
|
255
256
|
}
|
|
256
257
|
|
|
258
|
+
function testCoepDefaultOnAndOptOut() {
|
|
259
|
+
// Default-on (v0.15.0): the enforcing Cross-Origin-Embedder-Policy is
|
|
260
|
+
// emitted as `credentialless` with no operator action, so COOP+COEP
|
|
261
|
+
// together yield cross-origin isolation out of the box.
|
|
262
|
+
var resDefault = _mkRes();
|
|
263
|
+
b.middleware.securityHeaders()({ headers: {} }, resDefault, function () {});
|
|
264
|
+
check("COEP default-on: Cross-Origin-Embedder-Policy is credentialless",
|
|
265
|
+
resDefault._hdrs["Cross-Origin-Embedder-Policy"] === "credentialless");
|
|
266
|
+
check("COOP stays same-origin alongside the default COEP",
|
|
267
|
+
resDefault._hdrs["Cross-Origin-Opener-Policy"] === "same-origin");
|
|
268
|
+
|
|
269
|
+
// Tighten: operators serving only same-origin / CORP-marked subresources
|
|
270
|
+
// pass coep: "require-corp" for the strict enforcing mode.
|
|
271
|
+
var resStrict = _mkRes();
|
|
272
|
+
b.middleware.securityHeaders({ coep: "require-corp" })({ headers: {} }, resStrict, function () {});
|
|
273
|
+
check("COEP tighten: coep:'require-corp' overrides the default",
|
|
274
|
+
resStrict._hdrs["Cross-Origin-Embedder-Policy"] === "require-corp");
|
|
275
|
+
|
|
276
|
+
// Documented opt-out: coep:false disables COEP entirely (no header).
|
|
277
|
+
var resOff = _mkRes();
|
|
278
|
+
b.middleware.securityHeaders({ coep: false })({ headers: {} }, resOff, function () {});
|
|
279
|
+
check("COEP opt-out: coep:false emits no Cross-Origin-Embedder-Policy header",
|
|
280
|
+
resOff._hdrs["Cross-Origin-Embedder-Policy"] === undefined);
|
|
281
|
+
}
|
|
282
|
+
|
|
257
283
|
async function run() {
|
|
258
284
|
testFencedFrameSrcInDefaultCsp();
|
|
259
285
|
testDocumentPolicyDefault();
|
|
@@ -276,6 +302,7 @@ async function run() {
|
|
|
276
302
|
testServiceWorkerAllowedDefaultOff();
|
|
277
303
|
testNewOptsNonStringIgnored();
|
|
278
304
|
testUnknownOptStillRefused();
|
|
305
|
+
testCoepDefaultOnAndOptOut();
|
|
279
306
|
}
|
|
280
307
|
|
|
281
308
|
module.exports = { run: run };
|
|
@@ -273,6 +273,50 @@ async function testUpdateDataPreservesFingerprint() {
|
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
async function testRotateRekeysFingerprint() {
|
|
277
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "ses-rotate-fp-"));
|
|
278
|
+
try {
|
|
279
|
+
await setupTestDb(tmpDir);
|
|
280
|
+
var req = _makeReq({ "user-agent": "ua-rot-1", "x-forwarded-for": "203.0.113.10" });
|
|
281
|
+
var s = await b.session.create({
|
|
282
|
+
userId: "u-rot",
|
|
283
|
+
data: { roles: ["user"] },
|
|
284
|
+
req: req,
|
|
285
|
+
fingerprintFields: ["clientIp", "userAgent"],
|
|
286
|
+
});
|
|
287
|
+
var pre = await b.session.verify(s.token, { req: req, fingerprintFields: ["clientIp", "userAgent"] });
|
|
288
|
+
check("rotate-fp: pre-rotation no drift", pre && pre.fingerprintDrift === false);
|
|
289
|
+
|
|
290
|
+
// Rotation (login transition / role escalation) moves the sid. __bj_fingerprint
|
|
291
|
+
// is sid-keyed, so the new session must RE-KEY the binding to the new sid from
|
|
292
|
+
// the live request — otherwise verify(newToken, sameReq) recomputes against the
|
|
293
|
+
// new sid and falsely reports drift (logout under strict operators), or the
|
|
294
|
+
// binding silently breaks.
|
|
295
|
+
var rotated = await b.session.rotate(s.token, {
|
|
296
|
+
req: req, fingerprintFields: ["clientIp", "userAgent"],
|
|
297
|
+
});
|
|
298
|
+
check("rotate-fp: rotation returns a new token", rotated && typeof rotated.token === "string");
|
|
299
|
+
|
|
300
|
+
var sameDevice = await b.session.verify(rotated.token, {
|
|
301
|
+
req: req, fingerprintFields: ["clientIp", "userAgent"],
|
|
302
|
+
});
|
|
303
|
+
check("rotate-fp: same device → no drift after rotation (binding re-keyed)",
|
|
304
|
+
sameDevice && sameDevice.fingerprintDrift === false);
|
|
305
|
+
check("rotate-fp: operator data carried across rotation",
|
|
306
|
+
sameDevice && sameDevice.data && sameDevice.data.roles && sameDevice.data.roles[0] === "user");
|
|
307
|
+
|
|
308
|
+
// A different device must still drift — proves the binding is live, not dropped.
|
|
309
|
+
var otherReq = _makeReq({ "user-agent": "ua-OTHER", "x-forwarded-for": "198.51.100.7" });
|
|
310
|
+
var otherDevice = await b.session.verify(rotated.token, {
|
|
311
|
+
req: otherReq, fingerprintFields: ["clientIp", "userAgent"],
|
|
312
|
+
});
|
|
313
|
+
check("rotate-fp: different device → drift after rotation (binding still enforced)",
|
|
314
|
+
otherDevice && otherDevice.fingerprintDrift === true);
|
|
315
|
+
} finally {
|
|
316
|
+
await teardownTestDb(tmpDir);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
276
320
|
async function run() {
|
|
277
321
|
await testSealedCookieDefault();
|
|
278
322
|
await testSealedCookieRotateAndDestroy();
|
|
@@ -283,6 +327,7 @@ async function run() {
|
|
|
283
327
|
await testPluggableStoreValidation();
|
|
284
328
|
await testUpdateDataReplaceAndMerge();
|
|
285
329
|
await testUpdateDataPreservesFingerprint();
|
|
330
|
+
await testRotateRekeysFingerprint();
|
|
286
331
|
}
|
|
287
332
|
|
|
288
333
|
module.exports = { run: run };
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
var helpers = require("../helpers");
|
|
15
15
|
var http = require("http");
|
|
16
16
|
var bucketOps = require("../../lib/object-store/sigv4-bucket-ops");
|
|
17
|
+
var sigv4 = require("../../lib/object-store/sigv4");
|
|
17
18
|
var b = helpers.b;
|
|
18
19
|
var check = helpers.check;
|
|
19
20
|
var listenOnRandomPort = helpers.listenOnRandomPort;
|
|
@@ -969,8 +970,56 @@ async function testPerCallActorOverrideHonored() {
|
|
|
969
970
|
}
|
|
970
971
|
}
|
|
971
972
|
|
|
973
|
+
function testCanonicalPathSingleEncodeForS3() {
|
|
974
|
+
// Regression: S3 (and S3-compatible stores + GCS's V4) URI-encode the
|
|
975
|
+
// canonical path ONCE; the older code double-encoded it, so any object key
|
|
976
|
+
// with a space / + / & / unicode signed a path the wire never carried →
|
|
977
|
+
// SignatureDoesNotMatch (403). Drive the real signRequest path with a
|
|
978
|
+
// special-char key and assert the canonical path line equals the wire
|
|
979
|
+
// pathname byte-for-byte. (Pre-fix this matched only for ASCII keys, which
|
|
980
|
+
// is why every shipped test passed while real keys 403'd.)
|
|
981
|
+
var key = "my report (v2)+final & draft.txt";
|
|
982
|
+
var encodedKey = key.split("/").map(function (s) { return sigv4.awsUriEncode(s, true); }).join("/");
|
|
983
|
+
var url = new URL("https://bucket.s3.example.com");
|
|
984
|
+
url.pathname = "/" + encodedKey;
|
|
985
|
+
|
|
986
|
+
var s3 = sigv4.signRequest({
|
|
987
|
+
method: "GET", url: url, headers: {}, payloadHash: "UNSIGNED-PAYLOAD",
|
|
988
|
+
region: "us-east-1", accessKeyId: "AK", secretAccessKey: "sk", date: new Date(0),
|
|
989
|
+
});
|
|
990
|
+
var s3CanonPath = s3.canonicalRequest.split("\n")[1];
|
|
991
|
+
check("S3 canonical path single-encodes — equals the wire pathname (no double-encode)",
|
|
992
|
+
s3CanonPath === url.pathname);
|
|
993
|
+
check("S3 canonical path has no double-encoded %25 sequence",
|
|
994
|
+
s3CanonPath.indexOf("%25") === -1 && url.pathname.indexOf("%25") === -1);
|
|
995
|
+
|
|
996
|
+
// The non-S3 services (sqs/logs/sns) MUST keep the double-encode (AWS spec).
|
|
997
|
+
var u2 = new URL("https://sqs.us-east-1.amazonaws.com");
|
|
998
|
+
u2.pathname = "/a%20b";
|
|
999
|
+
var sqs = sigv4.signRequest({
|
|
1000
|
+
method: "GET", url: u2, headers: {}, payloadHash: sigv4.sha256Hex(""),
|
|
1001
|
+
region: "us-east-1", service: "sqs", accessKeyId: "AK", secretAccessKey: "sk", date: new Date(0),
|
|
1002
|
+
});
|
|
1003
|
+
var sqsCanonPath = sqs.canonicalRequest.split("\n")[1];
|
|
1004
|
+
check("non-S3 service still double-encodes the canonical path (spec-correct, unchanged)",
|
|
1005
|
+
sqsCanonPath === "/a%2520b");
|
|
1006
|
+
|
|
1007
|
+
// awsUriEncode escapes the AWS reserved set (!*'()) that encodeURIComponent
|
|
1008
|
+
// leaves alone, so the bucket-ops wire path matches the bytes S3 signs over.
|
|
1009
|
+
check("awsUriEncode escapes !*'() that encodeURIComponent leaves raw",
|
|
1010
|
+
sigv4.awsUriEncode("a!b*c'd(e)", true) === "a%21b%2Ac%27d%28e%29");
|
|
1011
|
+
|
|
1012
|
+
// A key with a non-BMP code point (emoji, CJK extension B, ...) must encode
|
|
1013
|
+
// by code point, not UTF-16 unit — otherwise the surrogate pair is split and
|
|
1014
|
+
// encodeURIComponent throws "URIError: URI malformed" before the request is
|
|
1015
|
+
// even signed.
|
|
1016
|
+
check("awsUriEncode encodes a non-BMP code point as one UTF-8 sequence (no URIError)",
|
|
1017
|
+
sigv4.awsUriEncode("photo-\u{1F600}.jpg", true) === "photo-%F0%9F%98%80.jpg");
|
|
1018
|
+
}
|
|
1019
|
+
|
|
972
1020
|
async function run() {
|
|
973
1021
|
testSurface();
|
|
1022
|
+
testCanonicalPathSingleEncodeForS3();
|
|
974
1023
|
testFactoryValidation();
|
|
975
1024
|
testBucketNameValidation();
|
|
976
1025
|
testLifecycleXml();
|