@blamejs/blamejs-shop 0.4.31 → 0.4.33
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/README.md +1 -1
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +400 -282
- 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 +28 -0
- package/lib/vendor/blamejs/MIGRATING.md +55 -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/elevation-grant.js +6 -2
- package/lib/vendor/blamejs/lib/auth/oauth.js +66 -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 +36 -7
- 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 +210 -4
- package/lib/vendor/blamejs/lib/consent.js +82 -29
- package/lib/vendor/blamejs/lib/constants.js +27 -11
- package/lib/vendor/blamejs/lib/credential-hash.js +9 -0
- 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 +117 -42
- 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 +47 -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 +169 -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/release-notes/v0.15.7.json +43 -0
- package/lib/vendor/blamejs/scripts/check-services.js +21 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +67 -0
- package/lib/vendor/blamejs/scripts/release.js +398 -38
- package/lib/vendor/blamejs/test/00-primitives.js +168 -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 +1196 -14
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +18 -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/retention-floor.test.js +59 -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 +362 -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/scheduler-watchdog-stale-settle.test.js +71 -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 +2 -2
- 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
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.auth.passkey — REAL WebAuthn signature verification.
|
|
4
|
+
*
|
|
5
|
+
* The companion suite (passkey.test.js) stubs the vendored verifier via a
|
|
6
|
+
* require-cache override, so the actual attestation / assertion signature
|
|
7
|
+
* verification never executes there — a forged-assertion-accepted
|
|
8
|
+
* regression would pass that suite green. This suite closes that gap: it
|
|
9
|
+
* drives verifyRegistration + verifyAuthentication through the vendored
|
|
10
|
+
* verifier UNTOUCHED, with genuine WebAuthn material produced by a
|
|
11
|
+
* software authenticator built on Node's crypto.
|
|
12
|
+
*
|
|
13
|
+
* The software authenticator mints a real EC P-256 keypair, builds a
|
|
14
|
+
* spec-shaped attestationObject ("none" fmt) + authenticatorData +
|
|
15
|
+
* clientDataJSON, and signs `authenticatorData || SHA-256(clientDataJSON)`
|
|
16
|
+
* with ECDSA/SHA-256 in DER form exactly as a hardware authenticator
|
|
17
|
+
* does. The vendor's real ECDSA path (DER unwrap to raw r||s, COSE key
|
|
18
|
+
* decode, WebCrypto subtle.verify) runs against this material.
|
|
19
|
+
*
|
|
20
|
+
* Load-bearing assertions: a genuine attestation/assertion VERIFIES, and
|
|
21
|
+
* every tamper — flipped signature byte, wrong challenge, wrong origin,
|
|
22
|
+
* wrong RP ID, mutated authenticatorData, a different signing key against
|
|
23
|
+
* the victim's stored public key — is REJECTED (either verified:false or
|
|
24
|
+
* a thrown binding error). The forged-key + tampered-signature cases are
|
|
25
|
+
* the phishing-resistance proof: they only hold if the cryptographic
|
|
26
|
+
* verification actually ran.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
var crypto = require("crypto");
|
|
30
|
+
var helpers = require("../helpers");
|
|
31
|
+
var b = helpers.b;
|
|
32
|
+
var check = helpers.check;
|
|
33
|
+
|
|
34
|
+
var passkey = b.auth.passkey;
|
|
35
|
+
|
|
36
|
+
var RP_ID = "example.test";
|
|
37
|
+
var ORIGIN = "https://example.test";
|
|
38
|
+
|
|
39
|
+
// ---- byte helpers ----
|
|
40
|
+
|
|
41
|
+
function b64url(buf) { return Buffer.from(buf).toString("base64url"); }
|
|
42
|
+
function sha256(buf) { return crypto.createHash("sha256").update(buf).digest(); }
|
|
43
|
+
|
|
44
|
+
// ---- minimal deterministic CBOR encoder (ints / neg-ints / bytes / text
|
|
45
|
+
// / maps) — only the subset COSE keys + the "none" attestationObject need.
|
|
46
|
+
// Not a general CBOR library; just enough to feed the real verifier.
|
|
47
|
+
|
|
48
|
+
function cborHead(major, n) {
|
|
49
|
+
if (n < 24) return Buffer.from([(major << 5) | n]);
|
|
50
|
+
if (n < 256) return Buffer.from([(major << 5) | 24, n]);
|
|
51
|
+
if (n < 65536) return Buffer.from([(major << 5) | 25, (n >> 8) & 0xff, n & 0xff]);
|
|
52
|
+
return Buffer.from([(major << 5) | 26,
|
|
53
|
+
(n >>> 24) & 0xff, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff]);
|
|
54
|
+
}
|
|
55
|
+
function cborInt(n) { return n >= 0 ? cborHead(0, n) : cborHead(1, -n - 1); }
|
|
56
|
+
function cborBytes(buf){ return Buffer.concat([cborHead(2, buf.length), buf]); }
|
|
57
|
+
function cborText(s) { var bb = Buffer.from(s, "utf8"); return Buffer.concat([cborHead(3, bb.length), bb]); }
|
|
58
|
+
function cborMap(pairs){
|
|
59
|
+
var parts = [cborHead(5, pairs.length)];
|
|
60
|
+
for (var i = 0; i < pairs.length; i++) { parts.push(pairs[i][0], pairs[i][1]); }
|
|
61
|
+
return Buffer.concat(parts);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// COSE_Key for an EC2 P-256 / ES256 public key, derived from the real
|
|
65
|
+
// JWK export of the Node KeyObject. { 1:2, 3:-7, -1:1, -2:x, -3:y }.
|
|
66
|
+
function coseEC2PublicKey(publicKey) {
|
|
67
|
+
var jwk = publicKey.export({ format: "jwk" });
|
|
68
|
+
var x = Buffer.from(jwk.x, "base64url");
|
|
69
|
+
var y = Buffer.from(jwk.y, "base64url");
|
|
70
|
+
return cborMap([
|
|
71
|
+
[cborInt(1), cborInt(2)], // kty: EC2
|
|
72
|
+
[cborInt(3), cborInt(-7)], // alg: ES256
|
|
73
|
+
[cborInt(-1), cborInt(1)], // crv: P-256
|
|
74
|
+
[cborInt(-2), cborBytes(x)], // x
|
|
75
|
+
[cborInt(-3), cborBytes(y)], // y
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// authenticatorData = rpIdHash(32) || flags(1) || signCount(4) [|| attestedCredentialData]
|
|
80
|
+
function buildAuthData(rpId, flags, signCount, attestedCredData) {
|
|
81
|
+
var rpIdHash = sha256(Buffer.from(rpId, "utf8"));
|
|
82
|
+
var f = Buffer.from([flags & 0xff]);
|
|
83
|
+
var c = Buffer.alloc(4); c.writeUInt32BE(signCount >>> 0, 0);
|
|
84
|
+
return attestedCredData
|
|
85
|
+
? Buffer.concat([rpIdHash, f, c, attestedCredData])
|
|
86
|
+
: Buffer.concat([rpIdHash, f, c]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// attestedCredentialData = aaguid(16) || credIdLen(2) || credId || COSE pubkey
|
|
90
|
+
function buildAttestedCredData(aaguid, credId, cosePub) {
|
|
91
|
+
var len = Buffer.alloc(2); len.writeUInt16BE(credId.length, 0);
|
|
92
|
+
return Buffer.concat([aaguid, len, credId, cosePub]);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ECDSA/SHA-256 over `data`, DER-encoded — the wire shape a real
|
|
96
|
+
// WebAuthn authenticator emits (the vendor unwraps DER to raw r||s).
|
|
97
|
+
function signDER(privateKey, data) {
|
|
98
|
+
return crypto.sign("sha256", data, { key: privateKey, dsaEncoding: "der" });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// authData flag bits: UP 0x01, UV 0x04, AT 0x40.
|
|
102
|
+
var FLAG_UP = 0x01, FLAG_UV = 0x04, FLAG_AT = 0x40;
|
|
103
|
+
|
|
104
|
+
// Build a fresh genuine credential + its registration response bound to a
|
|
105
|
+
// given challenge. Returns the keypair, credId, COSE pubkey, and the
|
|
106
|
+
// PublicKeyCredential-shaped registration response.
|
|
107
|
+
function makeRegistration(challenge) {
|
|
108
|
+
var kp = crypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
109
|
+
var cose = coseEC2PublicKey(kp.publicKey);
|
|
110
|
+
var credId = crypto.randomBytes(32);
|
|
111
|
+
var aaguid = Buffer.alloc(16, 0);
|
|
112
|
+
|
|
113
|
+
var authData = buildAuthData(RP_ID, FLAG_UP | FLAG_UV | FLAG_AT, 0,
|
|
114
|
+
buildAttestedCredData(aaguid, credId, cose));
|
|
115
|
+
var attObj = cborMap([
|
|
116
|
+
[cborText("fmt"), cborText("none")],
|
|
117
|
+
[cborText("attStmt"), cborMap([])],
|
|
118
|
+
[cborText("authData"), cborBytes(authData)],
|
|
119
|
+
]);
|
|
120
|
+
var clientData = Buffer.from(JSON.stringify({
|
|
121
|
+
type: "webauthn.create", challenge: challenge, origin: ORIGIN, crossOrigin: false,
|
|
122
|
+
}), "utf8");
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
keyPair: kp,
|
|
126
|
+
credId: credId,
|
|
127
|
+
response: {
|
|
128
|
+
id: b64url(credId),
|
|
129
|
+
rawId: b64url(credId),
|
|
130
|
+
type: "public-key",
|
|
131
|
+
response: {
|
|
132
|
+
clientDataJSON: b64url(clientData),
|
|
133
|
+
attestationObject: b64url(attObj),
|
|
134
|
+
},
|
|
135
|
+
clientExtensionResults: {},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Build a genuine authentication assertion for a stored credential, signed
|
|
141
|
+
// by `signingKey` (which is the real key for the genuine case, or an
|
|
142
|
+
// attacker key for the forged case). signCount controls counter advance.
|
|
143
|
+
function makeAssertion(signingKey, credId, challenge, signCount) {
|
|
144
|
+
var authData = buildAuthData(RP_ID, FLAG_UP | FLAG_UV, signCount, null);
|
|
145
|
+
var clientData = Buffer.from(JSON.stringify({
|
|
146
|
+
type: "webauthn.get", challenge: challenge, origin: ORIGIN, crossOrigin: false,
|
|
147
|
+
}), "utf8");
|
|
148
|
+
var signed = Buffer.concat([authData, sha256(clientData)]);
|
|
149
|
+
var sig = signDER(signingKey, signed);
|
|
150
|
+
return {
|
|
151
|
+
authData: authData,
|
|
152
|
+
clientData: clientData,
|
|
153
|
+
signed: signed,
|
|
154
|
+
sig: sig,
|
|
155
|
+
response: {
|
|
156
|
+
id: b64url(credId),
|
|
157
|
+
rawId: b64url(credId),
|
|
158
|
+
type: "public-key",
|
|
159
|
+
response: {
|
|
160
|
+
clientDataJSON: b64url(clientData),
|
|
161
|
+
authenticatorData: b64url(authData),
|
|
162
|
+
signature: b64url(sig),
|
|
163
|
+
},
|
|
164
|
+
clientExtensionResults: {},
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Read the stored public key off a registration result across
|
|
170
|
+
// vendor-version field shapes (credential.publicKey | credentialPublicKey).
|
|
171
|
+
function storedPublicKey(regResult) {
|
|
172
|
+
var ri = (regResult && regResult.registrationInfo) || {};
|
|
173
|
+
if (ri.credential && ri.credential.publicKey) return ri.credential.publicKey;
|
|
174
|
+
return ri.credentialPublicKey;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Verify and normalize the outcome to { ok, threw, code }. The verifier
|
|
178
|
+
// rejects either by returning verified:false OR by throwing a binding
|
|
179
|
+
// error (wrong challenge / origin / RP ID); both are "rejected".
|
|
180
|
+
async function authOutcome(args) {
|
|
181
|
+
try {
|
|
182
|
+
var rv = await passkey.verifyAuthentication(args);
|
|
183
|
+
return { ok: rv && rv.verified === true, threw: false, rv: rv };
|
|
184
|
+
} catch (e) {
|
|
185
|
+
return { ok: false, threw: true, code: e.code || e.message };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function regOutcome(args) {
|
|
189
|
+
try {
|
|
190
|
+
var rv = await passkey.verifyRegistration(args);
|
|
191
|
+
return { ok: rv && rv.verified === true, threw: false, rv: rv };
|
|
192
|
+
} catch (e) {
|
|
193
|
+
return { ok: false, threw: true, code: e.code || e.message };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ---- Registration: genuine attestation verifies; tampers rejected ----
|
|
198
|
+
|
|
199
|
+
async function testRegistrationGenuineAndTampered() {
|
|
200
|
+
// Mint the challenge through the real generateRegistrationOptions path.
|
|
201
|
+
var regOpts = await passkey.startRegistration({ rpName: "Example", rpId: RP_ID, userName: "alice" });
|
|
202
|
+
var challenge = regOpts.challenge;
|
|
203
|
+
check("startRegistration returns a base64url challenge",
|
|
204
|
+
typeof challenge === "string" && helpers.b.safeBuffer.BASE64URL_RE.test(challenge));
|
|
205
|
+
|
|
206
|
+
var reg = makeRegistration(challenge);
|
|
207
|
+
|
|
208
|
+
// Genuine attestation — the real verifier must accept it.
|
|
209
|
+
var good = await regOutcome({
|
|
210
|
+
response: reg.response,
|
|
211
|
+
expectedChallenge: challenge,
|
|
212
|
+
expectedOrigin: ORIGIN,
|
|
213
|
+
expectedRPID: RP_ID,
|
|
214
|
+
});
|
|
215
|
+
check("genuine registration verifies (real attestation path)", good.ok === true);
|
|
216
|
+
var pub = storedPublicKey(good.rv);
|
|
217
|
+
check("registration surfaces a COSE public key to persist",
|
|
218
|
+
Buffer.isBuffer(pub) || pub instanceof Uint8Array);
|
|
219
|
+
check("registration BE/BS flags map (single-device, not backed up)",
|
|
220
|
+
good.rv.backupEligible === false && good.rv.backupState === false);
|
|
221
|
+
|
|
222
|
+
// Wrong expected challenge — clientDataJSON's challenge no longer matches.
|
|
223
|
+
var badChallenge = await regOutcome({
|
|
224
|
+
response: reg.response,
|
|
225
|
+
expectedChallenge: b64url(crypto.randomBytes(32)),
|
|
226
|
+
expectedOrigin: ORIGIN,
|
|
227
|
+
expectedRPID: RP_ID,
|
|
228
|
+
});
|
|
229
|
+
check("registration with wrong expectedChallenge is rejected", badChallenge.ok === false);
|
|
230
|
+
|
|
231
|
+
// Wrong expected origin.
|
|
232
|
+
var badOrigin = await regOutcome({
|
|
233
|
+
response: reg.response,
|
|
234
|
+
expectedChallenge: challenge,
|
|
235
|
+
expectedOrigin: "https://evil.test",
|
|
236
|
+
expectedRPID: RP_ID,
|
|
237
|
+
});
|
|
238
|
+
check("registration with wrong expectedOrigin is rejected", badOrigin.ok === false);
|
|
239
|
+
|
|
240
|
+
// Wrong expected RP ID — the rpIdHash inside authData won't match.
|
|
241
|
+
var badRpId = await regOutcome({
|
|
242
|
+
response: reg.response,
|
|
243
|
+
expectedChallenge: challenge,
|
|
244
|
+
expectedOrigin: ORIGIN,
|
|
245
|
+
expectedRPID: "evil.test",
|
|
246
|
+
});
|
|
247
|
+
check("registration with wrong expectedRPID is rejected", badRpId.ok === false);
|
|
248
|
+
|
|
249
|
+
// Substituted clientDataJSON challenge — re-encode the client data with
|
|
250
|
+
// a different challenge than the server expects. The verifier compares
|
|
251
|
+
// the challenge embedded in clientDataJSON against expectedChallenge, so
|
|
252
|
+
// a credential captured for one ceremony can't be replayed into another.
|
|
253
|
+
// (fmt:"none" carries no attestation signature, so attestationObject-byte
|
|
254
|
+
// integrity is out of scope by spec — clientData binding is what guards
|
|
255
|
+
// the registration ceremony.)
|
|
256
|
+
var swapped = JSON.parse(JSON.stringify(reg.response));
|
|
257
|
+
var otherChallenge = b64url(crypto.randomBytes(32));
|
|
258
|
+
swapped.response.clientDataJSON = b64url(Buffer.from(JSON.stringify({
|
|
259
|
+
type: "webauthn.create", challenge: otherChallenge, origin: ORIGIN, crossOrigin: false,
|
|
260
|
+
}), "utf8"));
|
|
261
|
+
var badClientData = await regOutcome({
|
|
262
|
+
response: swapped,
|
|
263
|
+
expectedChallenge: challenge, // server still expects the original
|
|
264
|
+
expectedOrigin: ORIGIN,
|
|
265
|
+
expectedRPID: RP_ID,
|
|
266
|
+
});
|
|
267
|
+
check("registration with a substituted clientDataJSON challenge is rejected",
|
|
268
|
+
badClientData.ok === false);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ---- Authentication: genuine assertion verifies; tampers rejected ----
|
|
272
|
+
|
|
273
|
+
async function testAuthenticationGenuineAndTampered() {
|
|
274
|
+
// Register a real credential first so we have a genuine stored pubkey.
|
|
275
|
+
var regOpts = await passkey.startRegistration({ rpName: "Example", rpId: RP_ID, userName: "bob" });
|
|
276
|
+
var reg = makeRegistration(regOpts.challenge);
|
|
277
|
+
var regRes = await regOutcome({
|
|
278
|
+
response: reg.response,
|
|
279
|
+
expectedChallenge: regOpts.challenge,
|
|
280
|
+
expectedOrigin: ORIGIN,
|
|
281
|
+
expectedRPID: RP_ID,
|
|
282
|
+
});
|
|
283
|
+
check("setup: credential registers", regRes.ok === true);
|
|
284
|
+
var storedPub = storedPublicKey(regRes.rv);
|
|
285
|
+
|
|
286
|
+
// Mint an authentication challenge via the real options path.
|
|
287
|
+
var authOpts = await passkey.startAuthentication({ rpId: RP_ID });
|
|
288
|
+
var challenge = authOpts.challenge;
|
|
289
|
+
|
|
290
|
+
var credential = function () {
|
|
291
|
+
return { id: b64url(reg.credId), publicKey: storedPub, counter: 0 };
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// Genuine assertion signed with the real private key — must verify, and
|
|
295
|
+
// the signature counter must advance (clone-detection material).
|
|
296
|
+
var assertion = makeAssertion(reg.keyPair.privateKey, reg.credId, challenge, 7);
|
|
297
|
+
var good = await authOutcome({
|
|
298
|
+
response: assertion.response,
|
|
299
|
+
expectedChallenge: challenge,
|
|
300
|
+
expectedOrigin: ORIGIN,
|
|
301
|
+
expectedRPID: RP_ID,
|
|
302
|
+
credential: credential(),
|
|
303
|
+
});
|
|
304
|
+
check("genuine assertion verifies (real ECDSA signature path)", good.ok === true);
|
|
305
|
+
check("genuine assertion advances the signature counter",
|
|
306
|
+
good.rv.authenticationInfo && good.rv.authenticationInfo.newCounter === 7);
|
|
307
|
+
|
|
308
|
+
// --- THE LOAD-BEARING TAMPER CASES ---
|
|
309
|
+
|
|
310
|
+
// 1. Flipped signature byte — the cryptographic core must reject it.
|
|
311
|
+
var flipped = JSON.parse(JSON.stringify(assertion.response));
|
|
312
|
+
var sigBuf = Buffer.from(flipped.response.signature, "base64url");
|
|
313
|
+
sigBuf[sigBuf.length - 1] ^= 0x01;
|
|
314
|
+
flipped.response.signature = b64url(sigBuf);
|
|
315
|
+
var t1 = await authOutcome({
|
|
316
|
+
response: flipped,
|
|
317
|
+
expectedChallenge: challenge,
|
|
318
|
+
expectedOrigin: ORIGIN,
|
|
319
|
+
expectedRPID: RP_ID,
|
|
320
|
+
credential: credential(),
|
|
321
|
+
});
|
|
322
|
+
check("tampered signature (1 byte flipped) is REJECTED", t1.ok === false);
|
|
323
|
+
check("tampered signature rejection comes from verification, not a throw",
|
|
324
|
+
t1.threw === false && t1.rv.verified === false);
|
|
325
|
+
|
|
326
|
+
// 2. Wrong expected challenge — replay/binding defense.
|
|
327
|
+
var t2 = await authOutcome({
|
|
328
|
+
response: assertion.response,
|
|
329
|
+
expectedChallenge: b64url(crypto.randomBytes(32)),
|
|
330
|
+
expectedOrigin: ORIGIN,
|
|
331
|
+
expectedRPID: RP_ID,
|
|
332
|
+
credential: credential(),
|
|
333
|
+
});
|
|
334
|
+
check("assertion with wrong expectedChallenge is REJECTED", t2.ok === false);
|
|
335
|
+
|
|
336
|
+
// 3. Wrong expected origin — phishing-resistance binding.
|
|
337
|
+
var t3 = await authOutcome({
|
|
338
|
+
response: assertion.response,
|
|
339
|
+
expectedChallenge: challenge,
|
|
340
|
+
expectedOrigin: "https://evil.test",
|
|
341
|
+
expectedRPID: RP_ID,
|
|
342
|
+
credential: credential(),
|
|
343
|
+
});
|
|
344
|
+
check("assertion with wrong expectedOrigin is REJECTED (phishing-resistance)", t3.ok === false);
|
|
345
|
+
|
|
346
|
+
// 4. Wrong expected RP ID — rpIdHash binding.
|
|
347
|
+
var t4 = await authOutcome({
|
|
348
|
+
response: assertion.response,
|
|
349
|
+
expectedChallenge: challenge,
|
|
350
|
+
expectedOrigin: ORIGIN,
|
|
351
|
+
expectedRPID: "evil.test",
|
|
352
|
+
credential: credential(),
|
|
353
|
+
});
|
|
354
|
+
check("assertion with wrong expectedRPID is REJECTED", t4.ok === false);
|
|
355
|
+
|
|
356
|
+
// 5. Mutated authenticatorData — the signature covers authData, so a
|
|
357
|
+
// flipped counter byte must break verification.
|
|
358
|
+
var mutAd = JSON.parse(JSON.stringify(assertion.response));
|
|
359
|
+
var adBuf = Buffer.from(mutAd.response.authenticatorData, "base64url");
|
|
360
|
+
adBuf[adBuf.length - 1] ^= 0xff;
|
|
361
|
+
mutAd.response.authenticatorData = b64url(adBuf);
|
|
362
|
+
var t5 = await authOutcome({
|
|
363
|
+
response: mutAd,
|
|
364
|
+
expectedChallenge: challenge,
|
|
365
|
+
expectedOrigin: ORIGIN,
|
|
366
|
+
expectedRPID: RP_ID,
|
|
367
|
+
credential: credential(),
|
|
368
|
+
});
|
|
369
|
+
check("assertion with mutated authenticatorData is REJECTED", t5.ok === false);
|
|
370
|
+
|
|
371
|
+
// 6. FORGED KEY — an attacker signs with their own private key but
|
|
372
|
+
// presents the victim's stored public key. Only a real signature
|
|
373
|
+
// check rejects this; a stubbed verifier would accept it.
|
|
374
|
+
var attacker = crypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
375
|
+
var forged = makeAssertion(attacker.privateKey, reg.credId, challenge, 7);
|
|
376
|
+
var t6 = await authOutcome({
|
|
377
|
+
response: forged.response,
|
|
378
|
+
expectedChallenge: challenge,
|
|
379
|
+
expectedOrigin: ORIGIN,
|
|
380
|
+
expectedRPID: RP_ID,
|
|
381
|
+
credential: credential(),
|
|
382
|
+
});
|
|
383
|
+
check("FORGED assertion (attacker key vs victim pubkey) is REJECTED", t6.ok === false);
|
|
384
|
+
check("forged-key rejection comes from signature verification, not a throw",
|
|
385
|
+
t6.threw === false && t6.rv.verified === false);
|
|
386
|
+
|
|
387
|
+
// 7. Sanity: the same forged assertion, verified against the ATTACKER's
|
|
388
|
+
// own public key, DOES verify — proving the forged case above fails
|
|
389
|
+
// specifically because the key doesn't match, not for an unrelated
|
|
390
|
+
// reason (the test is exercising the real verification, not a no-op).
|
|
391
|
+
var attackerCose = coseEC2PublicKey(attacker.publicKey);
|
|
392
|
+
var attackerReg = makeRegistration(regOpts.challenge);
|
|
393
|
+
// build a stored-pubkey for the attacker by registering it
|
|
394
|
+
var attReg = await regOutcome({
|
|
395
|
+
response: attackerReg.response,
|
|
396
|
+
expectedChallenge: regOpts.challenge,
|
|
397
|
+
expectedOrigin: ORIGIN,
|
|
398
|
+
expectedRPID: RP_ID,
|
|
399
|
+
});
|
|
400
|
+
// Re-sign with the attacker's actual registered key for an apples-to-apples check.
|
|
401
|
+
var attChallengeOpts = await passkey.startAuthentication({ rpId: RP_ID });
|
|
402
|
+
var attAssertion = makeAssertion(attackerReg.keyPair.privateKey, attackerReg.credId,
|
|
403
|
+
attChallengeOpts.challenge, 3);
|
|
404
|
+
var attGood = await authOutcome({
|
|
405
|
+
response: attAssertion.response,
|
|
406
|
+
expectedChallenge: attChallengeOpts.challenge,
|
|
407
|
+
expectedOrigin: ORIGIN,
|
|
408
|
+
expectedRPID: RP_ID,
|
|
409
|
+
credential: { id: b64url(attackerReg.credId), publicKey: storedPublicKey(attReg.rv), counter: 0 },
|
|
410
|
+
});
|
|
411
|
+
check("control: a genuine assertion under its OWN key verifies (proves the verifier isn't a no-op)",
|
|
412
|
+
attGood.ok === true);
|
|
413
|
+
// Reference the encoded attacker COSE key so lint sees it consumed.
|
|
414
|
+
check("attacker COSE key encodes to bytes", Buffer.isBuffer(attackerCose) && attackerCose.length > 0);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ---- run ----
|
|
418
|
+
|
|
419
|
+
async function run() {
|
|
420
|
+
await testRegistrationGenuineAndTampered();
|
|
421
|
+
await testAuthenticationGenuineAndTampered();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
module.exports = { run: run };
|
|
425
|
+
|
|
426
|
+
if (require.main === module) {
|
|
427
|
+
run().then(function () { console.log("OK", helpers.getChecks(), "checks"); })
|
|
428
|
+
.catch(function (e) { console.error(e.stack || e); process.exit(1); });
|
|
429
|
+
}
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* b.pqcAgent — ecdhCurve negotiation surface.
|
|
4
4
|
*
|
|
5
|
-
* Covers the framework-default
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* non-default groups.
|
|
5
|
+
* Covers the framework-default outbound group preference (the three
|
|
6
|
+
* ML-KEM hybrids plus the trailing classical X25519 fallback for peers
|
|
7
|
+
* that support no hybrid), the narrowing path (subset of the default),
|
|
8
|
+
* and the operator-supplied-group escape hatch (allowOperatorGroups:
|
|
9
|
+
* true) including the audit emit on accepted non-default groups.
|
|
10
10
|
*
|
|
11
11
|
* Run standalone: `node test/layer-0-primitives/pqc-agent-curve.test.js`
|
|
12
12
|
* Or via smoke: `node test/smoke.js`
|
|
@@ -35,6 +35,11 @@ async function testDefaultGroupList() {
|
|
|
35
35
|
check("default ecdhCurve preserves preference order",
|
|
36
36
|
ec.indexOf("SecP384r1MLKEM1024") < ec.indexOf("X25519MLKEM768") &&
|
|
37
37
|
ec.indexOf("X25519MLKEM768") < ec.indexOf("SecP256r1MLKEM768"));
|
|
38
|
+
// The classical X25519 fallback is the LAST group — hybrids are always
|
|
39
|
+
// preferred; classical is only negotiated when the peer offers no hybrid.
|
|
40
|
+
var groups = ec.split(":");
|
|
41
|
+
check("default ecdhCurve ends with the classical X25519 fallback",
|
|
42
|
+
groups[groups.length - 1] === "X25519");
|
|
38
43
|
agent.destroy();
|
|
39
44
|
}
|
|
40
45
|
|
|
@@ -55,7 +60,10 @@ function testNarrowToFrameworkSubset() {
|
|
|
55
60
|
function testRefuseUnknownGroupByDefault() {
|
|
56
61
|
var threw = false;
|
|
57
62
|
try {
|
|
58
|
-
|
|
63
|
+
// secp256r1 (classical P-256) is a KNOWN_TLS_GROUPS entry but NOT in the
|
|
64
|
+
// framework outbound preference (the hybrids + the X25519 fallback), so
|
|
65
|
+
// it's refused without allowOperatorGroups.
|
|
66
|
+
b.pqcAgent.create({ ecdhCurve: "secp256r1" });
|
|
59
67
|
} catch (e) {
|
|
60
68
|
threw = e instanceof TypeError &&
|
|
61
69
|
e.message.indexOf("not in the framework PQC-hybrid") !== -1 &&
|
|
@@ -86,12 +94,14 @@ async function testAllowOperatorGroupsAuditEmit() {
|
|
|
86
94
|
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-pqcagent-"));
|
|
87
95
|
try {
|
|
88
96
|
await setupTestDb(tmpDir);
|
|
97
|
+
// secp256r1 (classical P-256) is outside the framework preference, so it
|
|
98
|
+
// exercises the operator-group escape hatch + the acceptance audit.
|
|
89
99
|
var agent = b.pqcAgent.create({
|
|
90
|
-
ecdhCurve: "
|
|
100
|
+
ecdhCurve: "secp256r1",
|
|
91
101
|
allowOperatorGroups: true,
|
|
92
102
|
});
|
|
93
|
-
check("
|
|
94
|
-
agent.options.ecdhCurve === "
|
|
103
|
+
check("secp256r1 accepted under allowOperatorGroups",
|
|
104
|
+
agent.options.ecdhCurve === "secp256r1");
|
|
95
105
|
agent.destroy();
|
|
96
106
|
|
|
97
107
|
await b.audit.flush();
|
|
@@ -99,8 +109,8 @@ async function testAllowOperatorGroupsAuditEmit() {
|
|
|
99
109
|
check("audit row written for operator-group acceptance", rows.length >= 1);
|
|
100
110
|
var meta = typeof rows[0].metadata === "string"
|
|
101
111
|
? JSON.parse(rows[0].metadata) : rows[0].metadata;
|
|
102
|
-
check("audit metadata carries group=
|
|
103
|
-
check("audit metadata carries ecdhCurve", meta.ecdhCurve === "
|
|
112
|
+
check("audit metadata carries group=secp256r1", meta.group === "secp256r1");
|
|
113
|
+
check("audit metadata carries ecdhCurve", meta.ecdhCurve === "secp256r1");
|
|
104
114
|
} finally {
|
|
105
115
|
await teardownTestDb(tmpDir);
|
|
106
116
|
}
|
|
@@ -292,6 +292,45 @@ async function testFlowSecondPassTargetsCustomTable() {
|
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
// ---- Self-registration: seal at rest without db.init ----
|
|
296
|
+
|
|
297
|
+
// A standalone redis/sqs queue node may never run db.init in-process, so
|
|
298
|
+
// the _blamejs_jobs sealed-column declaration would be absent and
|
|
299
|
+
// cryptoField.sealRow would silently pass the payload through in cleartext.
|
|
300
|
+
// queue.init must self-register the seal table so payloads seal regardless.
|
|
301
|
+
async function testSealRegistersWithoutDbInit() {
|
|
302
|
+
var tmpDir = _tmp();
|
|
303
|
+
await setupTestDb(tmpDir);
|
|
304
|
+
try {
|
|
305
|
+
// Simulate a process where db.init never registered _blamejs_jobs with
|
|
306
|
+
// cryptoField — clear the registry, then confirm it is genuinely gone.
|
|
307
|
+
b.cryptoField.clearForTest();
|
|
308
|
+
check("self-register precondition: _blamejs_jobs schema cleared",
|
|
309
|
+
b.cryptoField.getSchema("_blamejs_jobs") === null);
|
|
310
|
+
|
|
311
|
+
// queue.init must self-register the seal table.
|
|
312
|
+
b.queue.init({ backends: { primary: { protocol: "local" } } });
|
|
313
|
+
check("self-register: queue.init re-registered _blamejs_jobs seal map",
|
|
314
|
+
b.cryptoField.getSchema("_blamejs_jobs") !== null);
|
|
315
|
+
|
|
316
|
+
await b.queue.enqueue("seal-q", { secret: "ssn-7777", token: "bearer-abc" });
|
|
317
|
+
|
|
318
|
+
// Read the RAW payload column. b.db.from("_blamejs_jobs") would auto-
|
|
319
|
+
// UNSEAL (it's a registered table), masking the at-rest state — so use
|
|
320
|
+
// a raw prepared SELECT to inspect the bytes actually on disk.
|
|
321
|
+
var raw = b.db.prepare("SELECT payload FROM _blamejs_jobs WHERE queueName = ?").all("seal-q");
|
|
322
|
+
check("self-register: a row exists", raw.length === 1);
|
|
323
|
+
var stored = String(raw[0].payload || "");
|
|
324
|
+
check("self-register: payload sealed at rest (no plaintext secret)",
|
|
325
|
+
stored.length > 0 && stored.indexOf("ssn-7777") === -1 && stored.indexOf("bearer-abc") === -1);
|
|
326
|
+
check("self-register: payload carries the vault seal prefix",
|
|
327
|
+
stored.indexOf("vault:") === 0);
|
|
328
|
+
} finally {
|
|
329
|
+
try { await b.queue.shutdown({ timeoutMs: 500 }); } catch (_e) {}
|
|
330
|
+
await teardownTestDb(tmpDir);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
295
334
|
async function run() {
|
|
296
335
|
await testDefaultConfigUsesFrameworkJobsTable();
|
|
297
336
|
await testCustomTableRoutesRowsThere();
|
|
@@ -300,6 +339,7 @@ async function run() {
|
|
|
300
339
|
await testSchemaQualifierIsQuoted();
|
|
301
340
|
await testRejectsUnsafeTableAndSchemaAndHandle();
|
|
302
341
|
await testFlowSecondPassTargetsCustomTable();
|
|
342
|
+
await testSealRegistersWithoutDbInit();
|
|
303
343
|
}
|
|
304
344
|
|
|
305
345
|
module.exports = { run: run };
|
|
@@ -219,6 +219,85 @@ async function testIdempotentInstall() {
|
|
|
219
219
|
h1.uninstall();
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
// Bug A — installForPosture is NOT auto-called by b.compliance.set; the
|
|
223
|
+
// install tracking + the boot warning replace the false advertisement.
|
|
224
|
+
function testIsOutboundDlpInstalledSurface() {
|
|
225
|
+
b.redact._resetForTest();
|
|
226
|
+
check("b.redact.isOutboundDlpInstalled is a function",
|
|
227
|
+
typeof b.redact.isOutboundDlpInstalled === "function");
|
|
228
|
+
check("isOutboundDlpInstalled false before any install",
|
|
229
|
+
b.redact.isOutboundDlpInstalled() === false);
|
|
230
|
+
var http = _fakeHttpClient();
|
|
231
|
+
var dlp = b.redact.installForPosture("hipaa", { httpClient: http });
|
|
232
|
+
check("isOutboundDlpInstalled true after installForPosture",
|
|
233
|
+
b.redact.isOutboundDlpInstalled() === true);
|
|
234
|
+
dlp.uninstall();
|
|
235
|
+
check("isOutboundDlpInstalled false after uninstall",
|
|
236
|
+
b.redact.isOutboundDlpInstalled() === false);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function testComplianceSetWarnsWhenDlpUnwired() {
|
|
240
|
+
b.compliance._resetForTest();
|
|
241
|
+
b.redact._resetForTest();
|
|
242
|
+
var captured = [];
|
|
243
|
+
var origAudit = b.audit.safeEmit;
|
|
244
|
+
b.audit.safeEmit = function (e) { captured.push(e); };
|
|
245
|
+
try {
|
|
246
|
+
b.compliance.set("hipaa");
|
|
247
|
+
} finally {
|
|
248
|
+
b.audit.safeEmit = origAudit;
|
|
249
|
+
}
|
|
250
|
+
var warns = captured.filter(function (e) {
|
|
251
|
+
return e.action === "compliance.posture.outbound_dlp_unwired";
|
|
252
|
+
});
|
|
253
|
+
check("compliance.set('hipaa') warns when no outbound DLP wired",
|
|
254
|
+
warns.length === 1 && warns[0].outcome === "warning" && warns[0].metadata.posture === "hipaa");
|
|
255
|
+
b.compliance._resetForTest();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function testComplianceSetNoWarnWhenDlpWired() {
|
|
259
|
+
b.compliance._resetForTest();
|
|
260
|
+
b.redact._resetForTest();
|
|
261
|
+
// Wire DLP first, then pin the posture — set() must NOT warn.
|
|
262
|
+
var http = _fakeHttpClient();
|
|
263
|
+
var dlp = b.redact.installForPosture("pci-dss", { httpClient: http });
|
|
264
|
+
var captured = [];
|
|
265
|
+
var origAudit = b.audit.safeEmit;
|
|
266
|
+
b.audit.safeEmit = function (e) { captured.push(e); };
|
|
267
|
+
try {
|
|
268
|
+
b.compliance.set("pci-dss");
|
|
269
|
+
} finally {
|
|
270
|
+
b.audit.safeEmit = origAudit;
|
|
271
|
+
}
|
|
272
|
+
var warns = captured.filter(function (e) {
|
|
273
|
+
return e.action === "compliance.posture.outbound_dlp_unwired";
|
|
274
|
+
});
|
|
275
|
+
check("compliance.set('pci-dss') does not warn when DLP wired",
|
|
276
|
+
warns.length === 0);
|
|
277
|
+
dlp.uninstall();
|
|
278
|
+
b.compliance._resetForTest();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function testComplianceSetNoWarnForNonDlpPosture() {
|
|
282
|
+
b.compliance._resetForTest();
|
|
283
|
+
b.redact._resetForTest();
|
|
284
|
+
var captured = [];
|
|
285
|
+
var origAudit = b.audit.safeEmit;
|
|
286
|
+
b.audit.safeEmit = function (e) { captured.push(e); };
|
|
287
|
+
try {
|
|
288
|
+
// sox is regulated but has no outbound-DLP classifier preset.
|
|
289
|
+
b.compliance.set("sox");
|
|
290
|
+
} finally {
|
|
291
|
+
b.audit.safeEmit = origAudit;
|
|
292
|
+
}
|
|
293
|
+
var warns = captured.filter(function (e) {
|
|
294
|
+
return e.action === "compliance.posture.outbound_dlp_unwired";
|
|
295
|
+
});
|
|
296
|
+
check("compliance.set('sox') (non-DLP-floor posture) does not warn",
|
|
297
|
+
warns.length === 0);
|
|
298
|
+
b.compliance._resetForTest();
|
|
299
|
+
}
|
|
300
|
+
|
|
222
301
|
async function run() {
|
|
223
302
|
testSurface();
|
|
224
303
|
testClassifierRejectsBadOpts();
|
|
@@ -232,6 +311,10 @@ async function run() {
|
|
|
232
311
|
await testInstallWebhook();
|
|
233
312
|
await testPostureWiring();
|
|
234
313
|
await testIdempotentInstall();
|
|
314
|
+
testIsOutboundDlpInstalledSurface();
|
|
315
|
+
testComplianceSetWarnsWhenDlpUnwired();
|
|
316
|
+
testComplianceSetNoWarnWhenDlpWired();
|
|
317
|
+
testComplianceSetNoWarnForNonDlpPosture();
|
|
235
318
|
}
|
|
236
319
|
|
|
237
320
|
if (require.main === module) {
|