@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
|
@@ -146,10 +146,14 @@ function testFederationParseAndPolicy() {
|
|
|
146
146
|
check("federation: parse returns header.typ", parsed.header.typ === "entity-statement+jwt");
|
|
147
147
|
check("federation: parse returns claims.iss", parsed.claims.iss === "https://leaf.example");
|
|
148
148
|
|
|
149
|
-
// metadata_policy operators — value/default/one_of/subset_of/superset_of
|
|
149
|
+
// metadata_policy operators — value/default/one_of/subset_of/superset_of.
|
|
150
|
+
// Per OIDF §6.2 the policy comes from the SUPERIOR-SIGNED subordinate
|
|
151
|
+
// statement about the entity (`chain[i].subordinate.metadata_policy`),
|
|
152
|
+
// NOT the entity's own self-config — so each chain node carries the
|
|
153
|
+
// policy under `.subordinate`.
|
|
150
154
|
var meta = { application_type: "web", redirect_uris: ["https://leaf/cb"] };
|
|
151
155
|
var chain = [
|
|
152
|
-
{
|
|
156
|
+
{ subordinate: { metadata_policy: { openid_relying_party: {
|
|
153
157
|
application_type: { one_of: ["web", "native"] },
|
|
154
158
|
grant_types: { default: ["authorization_code"] },
|
|
155
159
|
} } } },
|
|
@@ -162,7 +166,7 @@ function testFederationParseAndPolicy() {
|
|
|
162
166
|
var threw = false;
|
|
163
167
|
try {
|
|
164
168
|
b.auth.openidFederation.applyMetadataPolicy({ application_type: "bad" }, [
|
|
165
|
-
{
|
|
169
|
+
{ subordinate: { metadata_policy: { openid_relying_party: { application_type: { one_of: ["web", "native"] } } } } },
|
|
166
170
|
], "openid_relying_party");
|
|
167
171
|
} catch (e) { threw = /not in/.test(e.message); }
|
|
168
172
|
check("federation: one_of rejects out-of-set value", threw);
|
|
@@ -171,10 +175,230 @@ function testFederationParseAndPolicy() {
|
|
|
171
175
|
threw = false;
|
|
172
176
|
try {
|
|
173
177
|
b.auth.openidFederation.applyMetadataPolicy({ x: 1 }, [
|
|
174
|
-
{
|
|
178
|
+
{ subordinate: { metadata_policy: { openid_relying_party: { x: { bogus: 1 } } } } },
|
|
175
179
|
], "openid_relying_party");
|
|
176
180
|
} catch (e) { threw = /unknown operator/.test(e.message); }
|
|
177
181
|
check("federation: unknown policy op refused", threw);
|
|
182
|
+
|
|
183
|
+
// M2: a leaf's OWN self-config metadata_policy is IGNORED — only the
|
|
184
|
+
// superior-signed subordinate statement constrains the entity. A leaf
|
|
185
|
+
// that self-declares a widening policy can't drop the anchor's rules.
|
|
186
|
+
var ignored = b.auth.openidFederation.applyMetadataPolicy(
|
|
187
|
+
{ application_type: "web" },
|
|
188
|
+
[{ claims: { metadata_policy: { openid_relying_party: {
|
|
189
|
+
application_type: { value: "native" } } } } }],
|
|
190
|
+
"openid_relying_party");
|
|
191
|
+
check("federation M2: leaf self-config metadata_policy ignored",
|
|
192
|
+
ignored.application_type === "web");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ---- OpenID Federation trust-chain (multi-element, real signatures) ---
|
|
196
|
+
|
|
197
|
+
// Mint an EC P-256 entity: keypair + a single-key JWKS carrying `kid`.
|
|
198
|
+
function _fedEntity(kid) {
|
|
199
|
+
var kp = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
200
|
+
var jwk = kp.publicKey.export({ format: "jwk" });
|
|
201
|
+
jwk.kid = kid;
|
|
202
|
+
jwk.use = "sig";
|
|
203
|
+
jwk.alg = "ES256";
|
|
204
|
+
return { kp: kp, kid: kid, jwks: { keys: [jwk] } };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Sign an entity-statement+jwt (ES256) with the given signing entity.
|
|
208
|
+
function _signEntityStatement(signer, claims) {
|
|
209
|
+
var header = { typ: "entity-statement+jwt", alg: "ES256", kid: signer.kid };
|
|
210
|
+
var iat = Math.floor(Date.now() / 1000);
|
|
211
|
+
var full = Object.assign({ iat: iat, exp: iat + 3600 }, claims);
|
|
212
|
+
var input = _b64url(JSON.stringify(header)) + "." + _b64url(JSON.stringify(full));
|
|
213
|
+
var sig = nodeCrypto.sign("sha256", Buffer.from(input, "ascii"),
|
|
214
|
+
{ key: signer.kp.privateKey, dsaEncoding: "ieee-p1363" });
|
|
215
|
+
return input + "." + _b64url(sig);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function testFederationTrustChainMultiElement() {
|
|
219
|
+
var ANCHOR = "https://anchor.example";
|
|
220
|
+
var INT = "https://intermediate.example";
|
|
221
|
+
var LEAF = "https://leaf.example";
|
|
222
|
+
|
|
223
|
+
var anchor = _fedEntity("anchor-1");
|
|
224
|
+
var inter = _fedEntity("inter-1");
|
|
225
|
+
var leaf = _fedEntity("leaf-1");
|
|
226
|
+
|
|
227
|
+
// Self-configs (each self-signed over its own jwks).
|
|
228
|
+
var anchorCfg = _signEntityStatement(anchor, { iss: ANCHOR, sub: ANCHOR, jwks: anchor.jwks });
|
|
229
|
+
var interCfg = _signEntityStatement(inter, { iss: INT, sub: INT, jwks: inter.jwks, authority_hints: [ANCHOR] });
|
|
230
|
+
// Leaf self-declares a wide scope ("openid profile email") + a single
|
|
231
|
+
// redirect_uri; the superior policy will force the scope down and
|
|
232
|
+
// confirm the redirect_uri is within the allowed subset.
|
|
233
|
+
var leafMeta = { openid_relying_party: { redirect_uris: ["https://leaf.example/cb"],
|
|
234
|
+
scope: "openid profile email" } };
|
|
235
|
+
var leafCfg = _signEntityStatement(leaf, { iss: LEAF, sub: LEAF, jwks: leaf.jwks,
|
|
236
|
+
authority_hints: [INT], metadata: leafMeta });
|
|
237
|
+
|
|
238
|
+
// Superior-signed subordinate statements.
|
|
239
|
+
// anchor → about intermediate (pins intermediate's real jwks)
|
|
240
|
+
var subAboutInter = _signEntityStatement(anchor, { iss: ANCHOR, sub: INT, jwks: inter.jwks });
|
|
241
|
+
// intermediate → about leaf (pins leaf's jwks + constrains the leaf
|
|
242
|
+
// via metadata_policy: subset_of on redirect_uris + value on scope)
|
|
243
|
+
var leafPolicy = { openid_relying_party: {
|
|
244
|
+
redirect_uris: { subset_of: ["https://leaf.example/cb", "https://leaf.example/cb2"] },
|
|
245
|
+
scope: { value: "openid" },
|
|
246
|
+
} };
|
|
247
|
+
var subAboutLeaf = _signEntityStatement(inter, { iss: INT, sub: LEAF, jwks: leaf.jwks,
|
|
248
|
+
metadata_policy: leafPolicy });
|
|
249
|
+
|
|
250
|
+
function _fetcher(url) {
|
|
251
|
+
if (url === ANCHOR + "/.well-known/openid-federation") return Promise.resolve(anchorCfg);
|
|
252
|
+
if (url === INT + "/.well-known/openid-federation") return Promise.resolve(interCfg);
|
|
253
|
+
if (url === LEAF + "/.well-known/openid-federation") return Promise.resolve(leafCfg);
|
|
254
|
+
return Promise.reject(new Error("404 " + url));
|
|
255
|
+
}
|
|
256
|
+
function _fetchSubordinate(authority, sub) {
|
|
257
|
+
if (authority === ANCHOR && sub === INT) return Promise.resolve(subAboutInter);
|
|
258
|
+
if (authority === INT && sub === LEAF) return Promise.resolve(subAboutLeaf);
|
|
259
|
+
return Promise.reject(new Error("no subordinate " + authority + "→" + sub));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
var opts = {
|
|
263
|
+
leafEntityId: LEAF,
|
|
264
|
+
trustAnchors: { "https://anchor.example": anchor.jwks },
|
|
265
|
+
fetcher: _fetcher,
|
|
266
|
+
fetchSubordinate: _fetchSubordinate,
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// Success path: chain builds leaf→anchor, roles assigned, attested
|
|
270
|
+
// jwks flowed down from the pinned anchor.
|
|
271
|
+
var chain = await b.auth.openidFederation.buildTrustChain(opts);
|
|
272
|
+
check("federation chain: leaf-first, anchor-last",
|
|
273
|
+
chain.length === 3 && chain[0].role === "leaf" &&
|
|
274
|
+
chain[1].role === "intermediate" && chain[2].role === "trust_anchor");
|
|
275
|
+
check("federation chain: leaf carries superior-signed subordinate statement",
|
|
276
|
+
chain[0].subordinate && chain[0].subordinate.iss === INT && chain[0].subordinate.sub === LEAF);
|
|
277
|
+
|
|
278
|
+
// M2: the SUPERIOR-SIGNED metadata_policy is enforced (anchor→leaf,
|
|
279
|
+
// narrow-only). `value` forces scope down to "openid"; subset_of
|
|
280
|
+
// confirms the leaf's lone redirect_uri is within the allowed set.
|
|
281
|
+
var resolved = await b.auth.openidFederation.resolveLeaf(Object.assign({ kind: "openid_relying_party" }, opts));
|
|
282
|
+
check("federation M2: superior-signed value narrows leaf scope",
|
|
283
|
+
resolved.effectiveMetadata.scope === "openid");
|
|
284
|
+
check("federation M2: leaf redirect_uri within superior subset_of accepted",
|
|
285
|
+
Array.isArray(resolved.effectiveMetadata.redirect_uris) &&
|
|
286
|
+
resolved.effectiveMetadata.redirect_uris.length === 1 &&
|
|
287
|
+
resolved.effectiveMetadata.redirect_uris[0] === "https://leaf.example/cb");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function testFederationSubsetOfRefusesWidening() {
|
|
291
|
+
// M2 refutation: a leaf that declares a redirect_uri OUTSIDE the
|
|
292
|
+
// superior-signed subset_of must be REFUSED — the leaf cannot widen
|
|
293
|
+
// past the anchor/intermediate constraint by self-declaration.
|
|
294
|
+
var threw = null;
|
|
295
|
+
try {
|
|
296
|
+
b.auth.openidFederation.applyMetadataPolicy(
|
|
297
|
+
{ redirect_uris: ["https://leaf.example/cb", "https://leaf.example/evil"] },
|
|
298
|
+
[{ subordinate: { metadata_policy: { openid_relying_party: {
|
|
299
|
+
redirect_uris: { subset_of: ["https://leaf.example/cb"] } } } } }],
|
|
300
|
+
"openid_relying_party");
|
|
301
|
+
} catch (e) { threw = e; }
|
|
302
|
+
check("federation M2: leaf redirect_uri outside superior subset_of refused",
|
|
303
|
+
threw && threw.code === "auth-openid-federation/policy-subset-of-failed");
|
|
304
|
+
|
|
305
|
+
// And the leaf's OWN self-config can't relax that subset — self-config
|
|
306
|
+
// policy is never read, so a self-declared wide subset is ignored while
|
|
307
|
+
// the superior subset still binds.
|
|
308
|
+
var eff = b.auth.openidFederation.applyMetadataPolicy(
|
|
309
|
+
{ scope: "openid" },
|
|
310
|
+
[{ claims: { metadata_policy: { openid_relying_party: { scope: { value: "admin" } } } },
|
|
311
|
+
subordinate: { metadata_policy: { openid_relying_party: { scope: { value: "openid" } } } } }],
|
|
312
|
+
"openid_relying_party");
|
|
313
|
+
check("federation M2: superior value wins over leaf self-config",
|
|
314
|
+
eff.scope === "openid");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async function testFederationSubordinateVerifiedAgainstAttestedKeys() {
|
|
318
|
+
// M5: the intermediate's SELF-PUBLISHED config jwks differ from the
|
|
319
|
+
// jwks the anchor ATTESTS for it. An attacker controlling the
|
|
320
|
+
// intermediate endpoint serves attacker keys in the self-config AND
|
|
321
|
+
// signs the leaf's subordinate statement with those attacker keys.
|
|
322
|
+
// The verifier MUST verify the leaf statement against the
|
|
323
|
+
// anchor-attested (genuine) intermediate keys, so the attacker-signed
|
|
324
|
+
// statement is rejected.
|
|
325
|
+
var ANCHOR = "https://anchor.example";
|
|
326
|
+
var INT = "https://intermediate.example";
|
|
327
|
+
var LEAF = "https://leaf.example";
|
|
328
|
+
|
|
329
|
+
var anchor = _fedEntity("anchor-1");
|
|
330
|
+
var interReal = _fedEntity("inter-1"); // anchor attests THESE keys
|
|
331
|
+
// The attacker self-publishes a DIFFERENT keypair under the SAME kid so
|
|
332
|
+
// the kid lookup matches the attested key slot — the only thing that
|
|
333
|
+
// can stop it is verifying against the attested key bytes (signature
|
|
334
|
+
// mismatch), which is exactly what the fix does.
|
|
335
|
+
var interEvil = _fedEntity("inter-1"); // same kid, different key
|
|
336
|
+
var leaf = _fedEntity("leaf-1");
|
|
337
|
+
|
|
338
|
+
var anchorCfg = _signEntityStatement(anchor, { iss: ANCHOR, sub: ANCHOR, jwks: anchor.jwks });
|
|
339
|
+
// Intermediate self-config: ATTACKER keys (TOCTOU — served at the
|
|
340
|
+
// self-config fetch). Self-signed with the attacker key so the
|
|
341
|
+
// self-statement integrity check passes.
|
|
342
|
+
var interCfg = _signEntityStatement(interEvil, { iss: INT, sub: INT, jwks: interEvil.jwks, authority_hints: [ANCHOR] });
|
|
343
|
+
var leafCfg = _signEntityStatement(leaf, { iss: LEAF, sub: LEAF, jwks: leaf.jwks, authority_hints: [INT] });
|
|
344
|
+
|
|
345
|
+
// Anchor attests the REAL intermediate keys.
|
|
346
|
+
var subAboutInter = _signEntityStatement(anchor, { iss: ANCHOR, sub: INT, jwks: interReal.jwks });
|
|
347
|
+
// The leaf's subordinate statement is signed with the ATTACKER key
|
|
348
|
+
// (interEvil) — it verifies against interEvil's self-published jwks
|
|
349
|
+
// (the pre-fix path) but NOT against the anchor-attested interReal keys.
|
|
350
|
+
var subAboutLeaf = _signEntityStatement(interEvil, { iss: INT, sub: LEAF, jwks: leaf.jwks });
|
|
351
|
+
|
|
352
|
+
function _fetcher(url) {
|
|
353
|
+
if (url === ANCHOR + "/.well-known/openid-federation") return Promise.resolve(anchorCfg);
|
|
354
|
+
if (url === INT + "/.well-known/openid-federation") return Promise.resolve(interCfg);
|
|
355
|
+
if (url === LEAF + "/.well-known/openid-federation") return Promise.resolve(leafCfg);
|
|
356
|
+
return Promise.reject(new Error("404 " + url));
|
|
357
|
+
}
|
|
358
|
+
function _fetchSubordinate(authority, sub) {
|
|
359
|
+
if (authority === ANCHOR && sub === INT) return Promise.resolve(subAboutInter);
|
|
360
|
+
if (authority === INT && sub === LEAF) return Promise.resolve(subAboutLeaf);
|
|
361
|
+
return Promise.reject(new Error("no subordinate " + authority + "→" + sub));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
var threw = null;
|
|
365
|
+
try {
|
|
366
|
+
await b.auth.openidFederation.buildTrustChain({
|
|
367
|
+
leafEntityId: LEAF,
|
|
368
|
+
trustAnchors: { "https://anchor.example": anchor.jwks },
|
|
369
|
+
fetcher: _fetcher,
|
|
370
|
+
fetchSubordinate: _fetchSubordinate,
|
|
371
|
+
});
|
|
372
|
+
} catch (e) { threw = e; }
|
|
373
|
+
// A cryptographic refusal proves the attested-key gate held: the
|
|
374
|
+
// attacker-signed statement fails against the anchor-attested key bytes.
|
|
375
|
+
check("federation M5: leaf statement signed by attacker self-jwks is refused",
|
|
376
|
+
threw && (threw.code === "auth-openid-federation/bad-signature" ||
|
|
377
|
+
threw.code === "auth-openid-federation/no-matching-kid"));
|
|
378
|
+
|
|
379
|
+
// Positive control: when the leaf statement is signed by the REAL
|
|
380
|
+
// (anchor-attested) intermediate key, the chain builds.
|
|
381
|
+
var subAboutLeafGood = _signEntityStatement(interReal, { iss: INT, sub: LEAF, jwks: leaf.jwks });
|
|
382
|
+
function _fetchSubordinateGood(authority, sub) {
|
|
383
|
+
if (authority === ANCHOR && sub === INT) return Promise.resolve(subAboutInter);
|
|
384
|
+
if (authority === INT && sub === LEAF) return Promise.resolve(subAboutLeafGood);
|
|
385
|
+
return Promise.reject(new Error("no subordinate " + authority + "→" + sub));
|
|
386
|
+
}
|
|
387
|
+
var built = await b.auth.openidFederation.buildTrustChain({
|
|
388
|
+
leafEntityId: LEAF,
|
|
389
|
+
trustAnchors: { "https://anchor.example": anchor.jwks },
|
|
390
|
+
fetcher: _fetcher,
|
|
391
|
+
fetchSubordinate: _fetchSubordinateGood,
|
|
392
|
+
});
|
|
393
|
+
check("federation M5: leaf statement signed by anchor-attested key builds",
|
|
394
|
+
built.length === 3 && built[2].role === "trust_anchor");
|
|
395
|
+
// The attested intermediate key bytes (interReal), not the self-
|
|
396
|
+
// published attacker key bytes (interEvil), are reflected on the
|
|
397
|
+
// intermediate node — same kid, so compare the EC x-coordinate.
|
|
398
|
+
check("federation M5: intermediate node carries anchor-attested key bytes",
|
|
399
|
+
built[1].claims.jwks &&
|
|
400
|
+
built[1].claims.jwks.keys[0].x === interReal.jwks.keys[0].x &&
|
|
401
|
+
built[1].claims.jwks.keys[0].x !== interEvil.jwks.keys[0].x);
|
|
178
402
|
}
|
|
179
403
|
|
|
180
404
|
// ---- OID4VP DCQL ------------------------------------------------------
|
|
@@ -559,7 +783,12 @@ async function testOid4vciX5cProof() {
|
|
|
559
783
|
await _refusedX5c([""], "empty string entry");
|
|
560
784
|
await _refusedX5c(["@@@not-base64@@@"], "non-base64 entry");
|
|
561
785
|
// base64url chars (- / _) are invalid for x5c (standard base64 only).
|
|
562
|
-
|
|
786
|
+
// Inject them unconditionally: leafB64's '+' / '/' population varies per
|
|
787
|
+
// generated cert, so replacing only the first such char is a no-op on the
|
|
788
|
+
// ~0.4% of certs that carry neither — which would leave a still-valid cert
|
|
789
|
+
// that is correctly accepted, making this refusal assertion flake. Prepend
|
|
790
|
+
// the base64url-only chars so the malformed entry is guaranteed every run.
|
|
791
|
+
await _refusedX5c(["-_" + leafB64.slice(2)], "base64url-charset entry");
|
|
563
792
|
// valid base64 but not a parseable DER certificate.
|
|
564
793
|
await _refusedX5c([Buffer.from("not a certificate").toString("base64")], "non-DER-cert entry");
|
|
565
794
|
|
|
@@ -790,6 +1019,9 @@ async function run() {
|
|
|
790
1019
|
testSamlSpAuthnRequest();
|
|
791
1020
|
testSamlSpRefusesUnsigned();
|
|
792
1021
|
testFederationParseAndPolicy();
|
|
1022
|
+
await testFederationTrustChainMultiElement();
|
|
1023
|
+
await testFederationSubsetOfRefusesWidening();
|
|
1024
|
+
await testFederationSubordinateVerifiedAgainstAttestedKeys();
|
|
793
1025
|
testDcqlMatch();
|
|
794
1026
|
testOid4vciIssuerConfig();
|
|
795
1027
|
await testOid4vciKidResolver();
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* - default behavior unchanged when the new opts are unset;
|
|
9
9
|
* - deniedDest refuses a "webidentity" (FedCM) Sec-Fetch-Dest on a
|
|
10
10
|
* non-identity route, regardless of Sec-Fetch-Site;
|
|
11
|
-
* -
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* - the cross-site Sec-Fetch-Storage-Access: active|inactive escalation
|
|
12
|
+
* is REFUSED BY DEFAULT (v0.15.0), "none" passes through, and
|
|
13
|
+
* allowStorageAccess:true opts back in for Storage-Access-flow routes;
|
|
14
14
|
* - strictDest throws at config time on an unknown destination value;
|
|
15
15
|
* - membership tests are exact (no substring / prototype-pollution
|
|
16
16
|
* bypass).
|
|
@@ -92,9 +92,9 @@ async function testDeniedDestWebIdentity() {
|
|
|
92
92
|
allowed.next === true);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
// Storage Access API escalation: active|inactive
|
|
96
|
-
//
|
|
97
|
-
//
|
|
95
|
+
// Storage Access API escalation: active|inactive REFUSED BY DEFAULT
|
|
96
|
+
// (v0.15.0); none passes through; allowStorageAccess:true opts back in for
|
|
97
|
+
// Storage-Access-flow routes.
|
|
98
98
|
async function testStorageAccessGate() {
|
|
99
99
|
var strict = b.middleware.fetchMetadata({ allowStorageAccess: false });
|
|
100
100
|
|
|
@@ -117,12 +117,23 @@ async function testStorageAccessGate() {
|
|
|
117
117
|
check("storage-access none on same-origin passes through",
|
|
118
118
|
none.next === true);
|
|
119
119
|
|
|
120
|
-
// Default (opt unset)
|
|
120
|
+
// Default (opt unset) REFUSES the escalation (v0.15.0) — checked before
|
|
121
|
+
// the allowCrossSite shortcut, so even a cross-site-permitting mount
|
|
122
|
+
// refuses the storage-access escalation by default.
|
|
121
123
|
var dflt = b.middleware.fetchMetadata({ allowCrossSite: true });
|
|
122
|
-
var
|
|
124
|
+
var refusedByDefault = await _run(dflt, _post({
|
|
123
125
|
"sec-fetch-site": "cross-site", "sec-fetch-storage-access": "active",
|
|
124
126
|
}), _bodyRes());
|
|
125
|
-
check("storage-access escalation
|
|
127
|
+
check("storage-access escalation refused by default (opt unset)",
|
|
128
|
+
refusedByDefault.next === false && refusedByDefault.status === 403);
|
|
129
|
+
|
|
130
|
+
// Documented opt-in: allowStorageAccess:true lets the Storage-Access-flow
|
|
131
|
+
// route through.
|
|
132
|
+
var optedIn = b.middleware.fetchMetadata({ allowCrossSite: true, allowStorageAccess: true });
|
|
133
|
+
var permitted = await _run(optedIn, _post({
|
|
134
|
+
"sec-fetch-site": "cross-site", "sec-fetch-storage-access": "active",
|
|
135
|
+
}), _bodyRes());
|
|
136
|
+
check("allowStorageAccess:true opts back in (escalation permitted)",
|
|
126
137
|
permitted.next === true);
|
|
127
138
|
}
|
|
128
139
|
|
package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* file-upload — content-safety SKIP is audited.
|
|
4
|
+
*
|
|
5
|
+
* When the byte-level content-safety scan does not run for a finalized
|
|
6
|
+
* upload, an operator reviewing the audit log must be able to tell the
|
|
7
|
+
* upload bypassed scanning, and WHY. Each skip path emits exactly one
|
|
8
|
+
* `fileUpload.content_safety_skipped` audit naming the reason:
|
|
9
|
+
*
|
|
10
|
+
* - content-safety-disabled (contentSafety: null opt-out)
|
|
11
|
+
* - no-gate-for-extension (no gate registered for the ext)
|
|
12
|
+
* - streamed-over-reassembly-cap (upload streamed past the cap)
|
|
13
|
+
*
|
|
14
|
+
* The audit is observability-only: a throwing audit sink must NOT crash
|
|
15
|
+
* the upload (the finalize still returns its result).
|
|
16
|
+
*/
|
|
17
|
+
var nodeOs = require("node:os");
|
|
18
|
+
var nodePath = require("node:path");
|
|
19
|
+
var nodeFs = require("node:fs");
|
|
20
|
+
var nodeCrypto = require("node:crypto");
|
|
21
|
+
var helpers = require("../helpers");
|
|
22
|
+
var check = helpers.check;
|
|
23
|
+
var b = helpers.b;
|
|
24
|
+
|
|
25
|
+
function _tmpDir(suffix) {
|
|
26
|
+
var dir = nodePath.join(nodeOs.tmpdir(), "fileupload-skipaudit-" + suffix + "-" +
|
|
27
|
+
nodeCrypto.randomBytes(6).toString("hex"));
|
|
28
|
+
nodeFs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
29
|
+
return dir;
|
|
30
|
+
}
|
|
31
|
+
function _chunkSha3(buf) { return require("../../lib/crypto").sha3Hash(buf); }
|
|
32
|
+
function _fullSha3(pieces) {
|
|
33
|
+
var h = nodeCrypto.createHash("sha3-512");
|
|
34
|
+
for (var i = 0; i < pieces.length; i++) h.update(pieces[i]);
|
|
35
|
+
return h.digest("hex");
|
|
36
|
+
}
|
|
37
|
+
function _skipEvents(emitted) {
|
|
38
|
+
return emitted.filter(function (e) {
|
|
39
|
+
return e.action === "fileUpload.content_safety_skipped";
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Drive one small (in-memory) upload end-to-end through finalize.
|
|
44
|
+
async function _uploadAndFinalize(u, uploadId, pieces, extra) {
|
|
45
|
+
await u.init({ uploadId: uploadId, actor: { id: uploadId },
|
|
46
|
+
metadata: (extra && extra.metadata) || {} });
|
|
47
|
+
var total = 0;
|
|
48
|
+
for (var i = 0; i < pieces.length; i++) {
|
|
49
|
+
await u.acceptChunk({ uploadId: uploadId, index: i, body: pieces[i],
|
|
50
|
+
sha3: _chunkSha3(pieces[i]), actor: { id: uploadId } });
|
|
51
|
+
total += pieces[i].length;
|
|
52
|
+
}
|
|
53
|
+
var manifest = {
|
|
54
|
+
totalBytes: total,
|
|
55
|
+
sha3: _fullSha3(pieces),
|
|
56
|
+
chunks: pieces.map(function (p, idx) { return { index: idx, sha3: _chunkSha3(p) }; }),
|
|
57
|
+
};
|
|
58
|
+
return u.finalize({ uploadId: uploadId, manifest: manifest, actor: { id: uploadId } });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function run() {
|
|
62
|
+
// ---- Skip reason: content-safety-disabled (contentSafety: null) ----
|
|
63
|
+
{
|
|
64
|
+
var emitted = [];
|
|
65
|
+
var fakeAudit = { safeEmit: function (e) { emitted.push(e); } };
|
|
66
|
+
var u = b.fileUpload.create({
|
|
67
|
+
stagingDir: _tmpDir("disabled"),
|
|
68
|
+
contentSafety: null,
|
|
69
|
+
filenameSafety: null,
|
|
70
|
+
audit: fakeAudit,
|
|
71
|
+
});
|
|
72
|
+
emitted.length = 0; // drop the create-time disable audits
|
|
73
|
+
var c0 = Buffer.from("plain text body", "utf8");
|
|
74
|
+
await _uploadAndFinalize(u, "u-disabled", [c0],
|
|
75
|
+
{ metadata: { filename: "doc.txt" } });
|
|
76
|
+
var skips = _skipEvents(emitted);
|
|
77
|
+
check("skip[disabled]: exactly one skip audit", skips.length === 1);
|
|
78
|
+
check("skip[disabled]: reason names the disable",
|
|
79
|
+
skips.length === 1 && skips[0].reason === "content-safety-disabled" &&
|
|
80
|
+
skips[0].metadata && skips[0].metadata.reason === "content-safety-disabled");
|
|
81
|
+
check("skip[disabled]: outcome is an accepted audit outcome",
|
|
82
|
+
skips.length === 1 && skips[0].outcome === "success");
|
|
83
|
+
check("skip[disabled]: metadata carries size",
|
|
84
|
+
skips.length === 1 && skips[0].metadata.size === c0.length);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ---- Skip reason: no-gate-for-extension ----
|
|
88
|
+
// contentSafety wired with a gate ONLY for ".csv"; upload a ".txt" so
|
|
89
|
+
// no gate matches the extension.
|
|
90
|
+
{
|
|
91
|
+
var emitted2 = [];
|
|
92
|
+
var fakeAudit2 = { safeEmit: function (e) { emitted2.push(e); } };
|
|
93
|
+
var csvGateChecked = false;
|
|
94
|
+
var u2 = b.fileUpload.create({
|
|
95
|
+
stagingDir: _tmpDir("nogate"),
|
|
96
|
+
filenameSafety: null,
|
|
97
|
+
contentSafety: {
|
|
98
|
+
".csv": { check: function () { csvGateChecked = true; return { ok: true, action: "serve" }; } },
|
|
99
|
+
},
|
|
100
|
+
audit: fakeAudit2,
|
|
101
|
+
});
|
|
102
|
+
emitted2.length = 0;
|
|
103
|
+
var t0 = Buffer.from("not a csv", "utf8");
|
|
104
|
+
await _uploadAndFinalize(u2, "u-nogate", [t0],
|
|
105
|
+
{ metadata: { filename: "notes.txt" } });
|
|
106
|
+
var skips2 = _skipEvents(emitted2);
|
|
107
|
+
check("skip[no-gate]: the .csv gate was NOT invoked for a .txt", csvGateChecked === false);
|
|
108
|
+
check("skip[no-gate]: exactly one skip audit", skips2.length === 1);
|
|
109
|
+
check("skip[no-gate]: reason names the missing gate",
|
|
110
|
+
skips2.length === 1 && skips2[0].reason === "no-gate-for-extension");
|
|
111
|
+
check("skip[no-gate]: metadata carries ext",
|
|
112
|
+
skips2.length === 1 && skips2[0].metadata.ext === ".txt");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---- A gate that DOES match must NOT emit a skip audit ----
|
|
116
|
+
{
|
|
117
|
+
var emitted3 = [];
|
|
118
|
+
var fakeAudit3 = { safeEmit: function (e) { emitted3.push(e); } };
|
|
119
|
+
var u3 = b.fileUpload.create({
|
|
120
|
+
stagingDir: _tmpDir("matched"),
|
|
121
|
+
filenameSafety: null,
|
|
122
|
+
contentSafety: {
|
|
123
|
+
".txt": { check: function () { return { ok: true, action: "serve" }; } },
|
|
124
|
+
},
|
|
125
|
+
audit: fakeAudit3,
|
|
126
|
+
});
|
|
127
|
+
emitted3.length = 0;
|
|
128
|
+
var m0 = Buffer.from("scanned body", "utf8");
|
|
129
|
+
await _uploadAndFinalize(u3, "u-matched", [m0],
|
|
130
|
+
{ metadata: { filename: "scan.txt" } });
|
|
131
|
+
check("scan[matched]: gate ran → NO skip audit emitted",
|
|
132
|
+
_skipEvents(emitted3).length === 0);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ---- Skip reason: streamed-over-reassembly-cap ----
|
|
136
|
+
// Force the streaming path by setting maxStreamReassemblyBytes below the
|
|
137
|
+
// upload size, while a gate IS registered for the extension.
|
|
138
|
+
{
|
|
139
|
+
var emitted4 = [];
|
|
140
|
+
var fakeAudit4 = { safeEmit: function (e) { emitted4.push(e); } };
|
|
141
|
+
var streamGateChecked = false;
|
|
142
|
+
var u4 = b.fileUpload.create({
|
|
143
|
+
stagingDir: _tmpDir("streamed"),
|
|
144
|
+
filenameSafety: null,
|
|
145
|
+
maxStreamReassemblyBytes: 4, // tiny — anything bigger streams
|
|
146
|
+
contentSafety: {
|
|
147
|
+
".bin": { check: function () { streamGateChecked = true; return { ok: true, action: "serve" }; } },
|
|
148
|
+
},
|
|
149
|
+
audit: fakeAudit4,
|
|
150
|
+
onFinalize: async function (info) {
|
|
151
|
+
// Drain the stream so the readable doesn't leak open.
|
|
152
|
+
if (info.stream) { for await (var _c of info.stream) { void _c; } }
|
|
153
|
+
return { ok: true, sha3: info.sha3, size: info.size };
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
emitted4.length = 0;
|
|
157
|
+
var big0 = Buffer.from("0123456789", "utf8"); // 10 bytes > 4-byte cap
|
|
158
|
+
await _uploadAndFinalize(u4, "u-streamed", [big0],
|
|
159
|
+
{ metadata: { filename: "blob.bin" } });
|
|
160
|
+
var skips4 = _skipEvents(emitted4);
|
|
161
|
+
check("skip[streamed]: gate could not run on a streamed body", streamGateChecked === false);
|
|
162
|
+
check("skip[streamed]: exactly one skip audit", skips4.length === 1);
|
|
163
|
+
check("skip[streamed]: reason names the reassembly cap",
|
|
164
|
+
skips4.length === 1 && skips4[0].reason === "streamed-over-reassembly-cap");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ---- A throwing audit sink must NOT crash the upload ----
|
|
168
|
+
{
|
|
169
|
+
var u5 = b.fileUpload.create({
|
|
170
|
+
stagingDir: _tmpDir("throwing"),
|
|
171
|
+
contentSafety: null,
|
|
172
|
+
filenameSafety: null,
|
|
173
|
+
audit: { safeEmit: function () { throw new Error("audit sink down"); } },
|
|
174
|
+
});
|
|
175
|
+
var s0 = Buffer.from("survives a broken audit sink", "utf8");
|
|
176
|
+
var rv = null, threw = false;
|
|
177
|
+
try {
|
|
178
|
+
rv = await _uploadAndFinalize(u5, "u-throwing", [s0],
|
|
179
|
+
{ metadata: { filename: "ok.txt" } });
|
|
180
|
+
} catch (_e) { threw = true; }
|
|
181
|
+
check("skip[throwing-sink]: upload finalized despite throwing audit sink",
|
|
182
|
+
threw === false && rv && rv.ok === true && rv.size === s0.length);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = { run: run };
|
|
187
|
+
|
|
188
|
+
if (require.main === module) {
|
|
189
|
+
run().then(
|
|
190
|
+
function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
|
|
191
|
+
function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
|
|
192
|
+
);
|
|
193
|
+
}
|
|
@@ -630,6 +630,94 @@ async function testGuardCsvGateAuditEmission() {
|
|
|
630
630
|
/\.serve$/.test(emitted[0].action));
|
|
631
631
|
}
|
|
632
632
|
|
|
633
|
+
async function testGateContractDefineGuard() {
|
|
634
|
+
// defineGuard assembles a full content-guard module from a spec: error
|
|
635
|
+
// class, registry exports, buildProfile / compliancePosture /
|
|
636
|
+
// loadRulePack wiring, the default gate, and the extras pass-through.
|
|
637
|
+
var GuardDemoError = b.gateContract.GateContractError;
|
|
638
|
+
var PROFILES = {
|
|
639
|
+
strict: { reject: true },
|
|
640
|
+
balanced: { reject: false },
|
|
641
|
+
permissive: { reject: false },
|
|
642
|
+
};
|
|
643
|
+
var POSTURES = b.gateContract.ALL_STRICT_POSTURES;
|
|
644
|
+
var mod = b.gateContract.defineGuard({
|
|
645
|
+
name: "demo",
|
|
646
|
+
kind: "content",
|
|
647
|
+
errorClass: GuardDemoError,
|
|
648
|
+
profiles: PROFILES,
|
|
649
|
+
defaults: PROFILES.strict,
|
|
650
|
+
postures: POSTURES,
|
|
651
|
+
mimeTypes: ["application/x-demo"],
|
|
652
|
+
extensions: [".demo"],
|
|
653
|
+
validate: function (text) {
|
|
654
|
+
if (text.indexOf("BAD") !== -1) {
|
|
655
|
+
return { ok: false, issues: [{ kind: "demo.bad", severity: "high", snippet: "BAD token" }] };
|
|
656
|
+
}
|
|
657
|
+
return { ok: true, issues: [] };
|
|
658
|
+
},
|
|
659
|
+
extra: { TOKENS: ["BAD"] },
|
|
660
|
+
});
|
|
661
|
+
check("defineGuard: NAME / KIND set", mod.NAME === "demo" && mod.KIND === "content");
|
|
662
|
+
check("defineGuard: MIME_TYPES / EXTENSIONS frozen",
|
|
663
|
+
Object.isFrozen(mod.MIME_TYPES) && mod.MIME_TYPES[0] === "application/x-demo" &&
|
|
664
|
+
mod.EXTENSIONS[0] === ".demo");
|
|
665
|
+
check("defineGuard: registry triplet assembled",
|
|
666
|
+
typeof mod.buildProfile === "function" &&
|
|
667
|
+
typeof mod.compliancePosture === "function" &&
|
|
668
|
+
typeof mod.loadRulePack === "function" &&
|
|
669
|
+
typeof mod.gate === "function" && typeof mod.validate === "function");
|
|
670
|
+
check("defineGuard: extras passed through", Array.isArray(mod.TOKENS) && mod.TOKENS[0] === "BAD");
|
|
671
|
+
check("defineGuard: compliancePosture clones overlay",
|
|
672
|
+
mod.compliancePosture("hipaa") === "strict" || typeof mod.compliancePosture("hipaa") === "object");
|
|
673
|
+
// Default gate runs the standard serve / refuse chain on ctx.bytes.
|
|
674
|
+
var g = mod.gate({ profile: "strict" });
|
|
675
|
+
var serve = await g.check({ bytes: Buffer.from("ok bytes") });
|
|
676
|
+
var refuse = await g.check({ bytes: Buffer.from("a BAD value") });
|
|
677
|
+
check("defineGuard: default gate serves clean bytes", serve.action === "serve");
|
|
678
|
+
check("defineGuard: default gate refuses on high issue", !refuse.ok && refuse.action === "refuse");
|
|
679
|
+
// Prototype-safe extras copy — __proto__ in spec.extra is dropped.
|
|
680
|
+
var poisoned = b.gateContract.defineGuard({
|
|
681
|
+
name: "demo2", kind: "filename", errorClass: GuardDemoError,
|
|
682
|
+
profiles: PROFILES, postures: POSTURES,
|
|
683
|
+
validate: function () { return { ok: true, issues: [] }; },
|
|
684
|
+
extra: JSON.parse('{"__proto__":{"polluted":true},"safe":1}'),
|
|
685
|
+
});
|
|
686
|
+
check("defineGuard: prototype-pollution key dropped from extras",
|
|
687
|
+
poisoned.safe === 1 && !({}).polluted);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
async function testGateContractDefineParser() {
|
|
691
|
+
// defineParser assembles the minimal command / safe-* parser shape:
|
|
692
|
+
// the entry point, PROFILES, COMPLIANCE_POSTURES, a profile-name
|
|
693
|
+
// compliancePosture, and extras — no gate / buildProfile / loadRulePack.
|
|
694
|
+
var PROFILES = { strict: { cap: 1 }, balanced: { cap: 2 }, permissive: { cap: 3 } };
|
|
695
|
+
var mod = b.gateContract.defineParser({
|
|
696
|
+
name: "demo-command",
|
|
697
|
+
entry: function (line) { return { verb: String(line).toUpperCase() }; },
|
|
698
|
+
errorClass: b.gateContract.GateContractError,
|
|
699
|
+
profiles: PROFILES,
|
|
700
|
+
postures: b.gateContract.ALL_STRICT_POSTURES,
|
|
701
|
+
extra: { KNOWN: { NOOP: true } },
|
|
702
|
+
});
|
|
703
|
+
check("defineParser: entry exported as validate", typeof mod.validate === "function");
|
|
704
|
+
check("defineParser: entry runs", mod.validate("noop").verb === "NOOP");
|
|
705
|
+
check("defineParser: PROFILES / COMPLIANCE_POSTURES exported",
|
|
706
|
+
mod.PROFILES.strict.cap === 1 && mod.COMPLIANCE_POSTURES.hipaa === "strict");
|
|
707
|
+
check("defineParser: compliancePosture returns profile name",
|
|
708
|
+
mod.compliancePosture("hipaa") === "strict");
|
|
709
|
+
check("defineParser: compliancePosture('nope') → null", mod.compliancePosture("nope") === null);
|
|
710
|
+
check("defineParser: no gate / buildProfile / loadRulePack",
|
|
711
|
+
mod.gate === undefined && mod.buildProfile === undefined && mod.loadRulePack === undefined);
|
|
712
|
+
check("defineParser: extras passed through", mod.KNOWN.NOOP === true);
|
|
713
|
+
// Custom entryName.
|
|
714
|
+
var parserMod = b.gateContract.defineParser({
|
|
715
|
+
name: "demo-doc", entry: function () { return { ast: true }; }, entryName: "parse",
|
|
716
|
+
errorClass: b.gateContract.GateContractError, profiles: PROFILES,
|
|
717
|
+
});
|
|
718
|
+
check("defineParser: entryName overrides export key", typeof parserMod.parse === "function" && parserMod.validate === undefined);
|
|
719
|
+
}
|
|
720
|
+
|
|
633
721
|
async function run() {
|
|
634
722
|
// gateContract foundation
|
|
635
723
|
testGateContractSurface();
|
|
@@ -647,6 +735,8 @@ async function run() {
|
|
|
647
735
|
await testGateContractContentTypeMux();
|
|
648
736
|
await testGateContractBuildProfile();
|
|
649
737
|
await testGateContractBuildProfileCycleDetection();
|
|
738
|
+
await testGateContractDefineGuard();
|
|
739
|
+
await testGateContractDefineParser();
|
|
650
740
|
|
|
651
741
|
// guardCsv surface + behavior
|
|
652
742
|
testGuardCsvSurface();
|