@blamejs/blamejs-shop 0.4.31 → 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 +2 -0
- package/lib/asset-manifest.json +1 -1
- 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
|
@@ -255,6 +255,221 @@ function testV0882NewPostures() {
|
|
|
255
255
|
euP.indexOf("dsa") !== -1 && euP.indexOf("dga") !== -1 && euP.indexOf("eu-cer") !== -1);
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
// ---- Seal-envelope floor (POSTURE_DEFAULTS data + registerTable gate) ----
|
|
259
|
+
|
|
260
|
+
function testSealEnvelopeFloorData() {
|
|
261
|
+
check("postureDefault(hipaa, sealEnvelopeFloor) === 'aad'",
|
|
262
|
+
b.compliance.postureDefault("hipaa", "sealEnvelopeFloor") === "aad");
|
|
263
|
+
check("postureDefault(pci-dss, sealEnvelopeFloor) === 'aad'",
|
|
264
|
+
b.compliance.postureDefault("pci-dss", "sealEnvelopeFloor") === "aad");
|
|
265
|
+
// Absent on non-regulated / unfloored postures → null (back-compat).
|
|
266
|
+
check("postureDefault(gdpr, sealEnvelopeFloor) === null",
|
|
267
|
+
b.compliance.postureDefault("gdpr", "sealEnvelopeFloor") === null);
|
|
268
|
+
check("postureDefault(soc2, sealEnvelopeFloor) === null",
|
|
269
|
+
b.compliance.postureDefault("soc2", "sealEnvelopeFloor") === null);
|
|
270
|
+
check("postureDefault(dora, sealEnvelopeFloor) === null",
|
|
271
|
+
b.compliance.postureDefault("dora", "sealEnvelopeFloor") === null);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function testRegisterTableFloorBackCompatUnpinned() {
|
|
275
|
+
// No posture pinned: a plain sealed table registers exactly as before.
|
|
276
|
+
_resetState();
|
|
277
|
+
b.cryptoField.clearForTest();
|
|
278
|
+
var threw = null;
|
|
279
|
+
try {
|
|
280
|
+
b.cryptoField.registerTable("ct_floor_unpinned", { sealedFields: ["x"] });
|
|
281
|
+
} catch (e) { threw = e; }
|
|
282
|
+
check("plain sealed table registers under no posture (back-compat)",
|
|
283
|
+
threw === null);
|
|
284
|
+
b.cryptoField.clearForTest();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function testRegisterTableFloorThrowsUnderHipaa() {
|
|
288
|
+
_resetState();
|
|
289
|
+
b.cryptoField.clearForTest();
|
|
290
|
+
b.compliance.set("hipaa");
|
|
291
|
+
var threw = null;
|
|
292
|
+
try {
|
|
293
|
+
b.cryptoField.registerTable("ct_floor_hipaa_plain", { sealedFields: ["ssn"] });
|
|
294
|
+
} catch (e) { threw = e; }
|
|
295
|
+
check("plain sealed table under hipaa throws seal-envelope-below-floor",
|
|
296
|
+
threw && threw.code === "crypto-field/seal-envelope-below-floor");
|
|
297
|
+
b.cryptoField.clearForTest();
|
|
298
|
+
_resetState();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function testRegisterTableFloorAadSatisfiesHipaa() {
|
|
302
|
+
_resetState();
|
|
303
|
+
b.cryptoField.clearForTest();
|
|
304
|
+
b.compliance.set("hipaa");
|
|
305
|
+
var threw = null;
|
|
306
|
+
try {
|
|
307
|
+
b.cryptoField.registerTable("ct_floor_hipaa_aad",
|
|
308
|
+
{ sealedFields: ["ssn"], aad: true, rowIdField: "id" });
|
|
309
|
+
} catch (e) { threw = e; }
|
|
310
|
+
check("aad-bound sealed table satisfies hipaa floor (no throw)",
|
|
311
|
+
threw === null);
|
|
312
|
+
b.cryptoField.clearForTest();
|
|
313
|
+
_resetState();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function testRegisterTableFloorNoSealedFieldsPasses() {
|
|
317
|
+
_resetState();
|
|
318
|
+
b.cryptoField.clearForTest();
|
|
319
|
+
b.compliance.set("pci-dss");
|
|
320
|
+
var threw = null;
|
|
321
|
+
try {
|
|
322
|
+
// No sealed columns → no envelope to gate.
|
|
323
|
+
b.cryptoField.registerTable("ct_floor_pci_nosealed", { sealedFields: [] });
|
|
324
|
+
} catch (e) { threw = e; }
|
|
325
|
+
check("table with no sealed fields passes under pci-dss floor",
|
|
326
|
+
threw === null);
|
|
327
|
+
b.cryptoField.clearForTest();
|
|
328
|
+
_resetState();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function testRegisterTableFloorPerRowKeySatisfies() {
|
|
332
|
+
_resetState();
|
|
333
|
+
b.cryptoField.clearForTest();
|
|
334
|
+
b.compliance.set("hipaa");
|
|
335
|
+
// declarePerRowKey before registerTable: the table's declared envelope
|
|
336
|
+
// is per-row-key, which is ABOVE the aad floor.
|
|
337
|
+
b.cryptoField.declarePerRowKey("ct_floor_hipaa_prk", { keySize: 32 });
|
|
338
|
+
var threw = null;
|
|
339
|
+
try {
|
|
340
|
+
b.cryptoField.registerTable("ct_floor_hipaa_prk", { sealedFields: ["ssn"] });
|
|
341
|
+
} catch (e) { threw = e; }
|
|
342
|
+
check("per-row-key table satisfies hipaa floor (no throw)",
|
|
343
|
+
threw === null);
|
|
344
|
+
b.cryptoField.clearForTest();
|
|
345
|
+
_resetState();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function testRegisterTableFloorUnflooredPosturePasses() {
|
|
349
|
+
// gdpr is regulated but declares no sealEnvelopeFloor → plain passes.
|
|
350
|
+
_resetState();
|
|
351
|
+
b.cryptoField.clearForTest();
|
|
352
|
+
b.compliance.set("gdpr");
|
|
353
|
+
var threw = null;
|
|
354
|
+
try {
|
|
355
|
+
b.cryptoField.registerTable("ct_floor_gdpr_plain", { sealedFields: ["email"] });
|
|
356
|
+
} catch (e) { threw = e; }
|
|
357
|
+
check("plain sealed table under gdpr (no floor) passes (back-compat)",
|
|
358
|
+
threw === null);
|
|
359
|
+
b.cryptoField.clearForTest();
|
|
360
|
+
_resetState();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ---- Region-tag normalization + compatibility helpers (additive) ----
|
|
364
|
+
|
|
365
|
+
function testNormalizeRegionTag() {
|
|
366
|
+
check("normalizeRegionTag('EU') === normalizeRegionTag('eu')",
|
|
367
|
+
b.compliance.normalizeRegionTag("EU") === b.compliance.normalizeRegionTag("eu"));
|
|
368
|
+
check("normalizeRegionTag(' eu ') trims + lowercases",
|
|
369
|
+
b.compliance.normalizeRegionTag(" eu ") === "eu");
|
|
370
|
+
check("normalizeRegionTag('global') folds to 'unrestricted'",
|
|
371
|
+
b.compliance.normalizeRegionTag("global") === "unrestricted");
|
|
372
|
+
check("normalizeRegionTag('unrestricted') === 'unrestricted'",
|
|
373
|
+
b.compliance.normalizeRegionTag("unrestricted") === "unrestricted");
|
|
374
|
+
check("normalizeRegionTag(null) === null",
|
|
375
|
+
b.compliance.normalizeRegionTag(null) === null);
|
|
376
|
+
check("normalizeRegionTag('') === null",
|
|
377
|
+
b.compliance.normalizeRegionTag("") === null);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function testIsRegionCompatible() {
|
|
381
|
+
check("isRegionCompatible('EU','eu') === true (case-insensitive)",
|
|
382
|
+
b.compliance.isRegionCompatible("EU", "eu") === true);
|
|
383
|
+
check("isRegionCompatible('eu','global') === true (wildcard)",
|
|
384
|
+
b.compliance.isRegionCompatible("eu", "global") === true);
|
|
385
|
+
check("isRegionCompatible('unrestricted','us') === true (wildcard)",
|
|
386
|
+
b.compliance.isRegionCompatible("unrestricted", "us") === true);
|
|
387
|
+
check("isRegionCompatible('eu','us') === false (distinct regions)",
|
|
388
|
+
b.compliance.isRegionCompatible("eu", "us") === false);
|
|
389
|
+
check("isRegionCompatible('EU',null) === true (no constraint)",
|
|
390
|
+
b.compliance.isRegionCompatible("EU", null) === true);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ---- Bug C1: gate-contract unmapped-posture warning ----
|
|
394
|
+
|
|
395
|
+
function _gcCfg() {
|
|
396
|
+
return {
|
|
397
|
+
profiles: { strict: { a: 1 } },
|
|
398
|
+
compliancePostures: { hipaa: { piiPolicy: "redact" }, "pci-dss": { piiPolicy: "refuse" } },
|
|
399
|
+
defaults: { piiPolicy: "serve" },
|
|
400
|
+
errCodePrefix: "ct_c1",
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function testGateContractUnmappedPostureWarns() {
|
|
405
|
+
_resetState();
|
|
406
|
+
b.gateContract._resetForTest();
|
|
407
|
+
// fedramp-rev5-moderate is a real posture with no overlay in _gcCfg.
|
|
408
|
+
b.compliance.set("fedramp-rev5-moderate");
|
|
409
|
+
var captured = [];
|
|
410
|
+
var origAudit = b.audit.safeEmit;
|
|
411
|
+
b.audit.safeEmit = function (e) { captured.push(e); };
|
|
412
|
+
var resolved;
|
|
413
|
+
try {
|
|
414
|
+
resolved = b.gateContract.resolveProfileAndPosture({}, _gcCfg());
|
|
415
|
+
// Second call same posture+guard must NOT re-warn (dedupe).
|
|
416
|
+
b.gateContract.resolveProfileAndPosture({}, _gcCfg());
|
|
417
|
+
} finally {
|
|
418
|
+
b.audit.safeEmit = origAudit;
|
|
419
|
+
}
|
|
420
|
+
var warns = captured.filter(function (e) {
|
|
421
|
+
return e.action === "gateContract.posture.unmapped";
|
|
422
|
+
});
|
|
423
|
+
check("unmapped global posture emits exactly one warning (deduped)",
|
|
424
|
+
warns.length === 1 && warns[0].metadata.posture === "fedramp-rev5-moderate");
|
|
425
|
+
check("unmapped posture keeps the safe (unposture-d) default",
|
|
426
|
+
resolved.piiPolicy === "serve");
|
|
427
|
+
b.gateContract._resetForTest();
|
|
428
|
+
_resetState();
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function testGateContractMappedPostureNoWarn() {
|
|
432
|
+
_resetState();
|
|
433
|
+
b.gateContract._resetForTest();
|
|
434
|
+
b.compliance.set("hipaa");
|
|
435
|
+
var captured = [];
|
|
436
|
+
var origAudit = b.audit.safeEmit;
|
|
437
|
+
b.audit.safeEmit = function (e) { captured.push(e); };
|
|
438
|
+
var resolved;
|
|
439
|
+
try {
|
|
440
|
+
resolved = b.gateContract.resolveProfileAndPosture({}, _gcCfg());
|
|
441
|
+
} finally {
|
|
442
|
+
b.audit.safeEmit = origAudit;
|
|
443
|
+
}
|
|
444
|
+
var warns = captured.filter(function (e) {
|
|
445
|
+
return e.action === "gateContract.posture.unmapped";
|
|
446
|
+
});
|
|
447
|
+
check("mapped global posture does not warn", warns.length === 0);
|
|
448
|
+
check("mapped global posture applies overlay", resolved.piiPolicy === "redact");
|
|
449
|
+
b.gateContract._resetForTest();
|
|
450
|
+
_resetState();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function testGateContractUnpinnedNoWarn() {
|
|
454
|
+
_resetState();
|
|
455
|
+
b.gateContract._resetForTest();
|
|
456
|
+
var captured = [];
|
|
457
|
+
var origAudit = b.audit.safeEmit;
|
|
458
|
+
b.audit.safeEmit = function (e) { captured.push(e); };
|
|
459
|
+
var resolved;
|
|
460
|
+
try {
|
|
461
|
+
resolved = b.gateContract.resolveProfileAndPosture({}, _gcCfg());
|
|
462
|
+
} finally {
|
|
463
|
+
b.audit.safeEmit = origAudit;
|
|
464
|
+
}
|
|
465
|
+
var warns = captured.filter(function (e) {
|
|
466
|
+
return e.action === "gateContract.posture.unmapped";
|
|
467
|
+
});
|
|
468
|
+
check("unpinned deployment does not warn", warns.length === 0);
|
|
469
|
+
check("unpinned deployment keeps default", resolved.piiPolicy === "serve");
|
|
470
|
+
b.gateContract._resetForTest();
|
|
471
|
+
}
|
|
472
|
+
|
|
258
473
|
async function run() {
|
|
259
474
|
testSurface();
|
|
260
475
|
testSetThenCurrent();
|
|
@@ -266,7 +481,21 @@ async function run() {
|
|
|
266
481
|
testV0870NewPostures();
|
|
267
482
|
testV0881NewPostures();
|
|
268
483
|
testV0882NewPostures();
|
|
484
|
+
testSealEnvelopeFloorData();
|
|
485
|
+
testRegisterTableFloorBackCompatUnpinned();
|
|
486
|
+
testRegisterTableFloorThrowsUnderHipaa();
|
|
487
|
+
testRegisterTableFloorAadSatisfiesHipaa();
|
|
488
|
+
testRegisterTableFloorNoSealedFieldsPasses();
|
|
489
|
+
testRegisterTableFloorPerRowKeySatisfies();
|
|
490
|
+
testRegisterTableFloorUnflooredPosturePasses();
|
|
491
|
+
testNormalizeRegionTag();
|
|
492
|
+
testIsRegionCompatible();
|
|
493
|
+
testGateContractUnmappedPostureWarns();
|
|
494
|
+
testGateContractMappedPostureNoWarn();
|
|
495
|
+
testGateContractUnpinnedNoWarn();
|
|
269
496
|
// Reset at end so other tests don't see leaked posture.
|
|
497
|
+
b.cryptoField.clearForTest();
|
|
498
|
+
b.gateContract._resetForTest();
|
|
270
499
|
_resetState();
|
|
271
500
|
}
|
|
272
501
|
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Equality-lookup ("derived") hashes for sealed columns can be computed
|
|
6
6
|
* two ways:
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* - hmac-shake256 (default, v0.15.0) — keyed MAC off
|
|
8
|
+
* vault.getDerivedHashMacKey, so an attacker
|
|
9
|
+
* who recovers the per-deployment salt alone
|
|
10
|
+
* cannot correlate low-entropy plaintexts.
|
|
11
|
+
* - salted-sha3 (opt-out) — SHA3-512 over a per-deployment salt;
|
|
12
|
+
* byte-compatible with the legacy index.
|
|
11
13
|
*
|
|
12
14
|
* The mode is chosen per-table (derivedHashMode) or per-column
|
|
13
15
|
* (spec.mode). The decision lives in _computeDerivedHash; call sites
|
|
@@ -25,17 +27,31 @@ async function run() {
|
|
|
25
27
|
var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cf-dh-"));
|
|
26
28
|
await b.vault.init({ mode: "plaintext", dataDir: dir });
|
|
27
29
|
|
|
28
|
-
// ----
|
|
30
|
+
// ---- DEFAULT (v0.15.0) is the keyed MAC: hmac-shake256 → 64 hex chars.
|
|
31
|
+
// A table that declares no derivedHashMode gets the keyed digest so an
|
|
32
|
+
// attacker who recovers the salt alone can't correlate plaintexts. ----
|
|
33
|
+
b.cryptoField.registerTable("cf_dh_default", {
|
|
34
|
+
sealedFields: ["email"],
|
|
35
|
+
derivedHashes: { emailHash: { from: "email" } },
|
|
36
|
+
});
|
|
37
|
+
var defaultH = b.cryptoField.lookupHash("cf_dh_default", "email", "a@b.com").value;
|
|
38
|
+
check("derivedHashMode default is keyed hmac-shake256 (64 hex)", defaultH.length === 64);
|
|
39
|
+
check("default keyed hash is deterministic",
|
|
40
|
+
defaultH === b.cryptoField.lookupHash("cf_dh_default", "email", "a@b.com").value);
|
|
41
|
+
|
|
42
|
+
// ---- documented opt-out: derivedHashMode:'salted-sha3' restores the
|
|
43
|
+
// deterministic-per-deployment SHA3-512 digest → 128 hex chars. ----
|
|
29
44
|
b.cryptoField.registerTable("cf_dh_t1", {
|
|
30
45
|
sealedFields: ["email"],
|
|
31
46
|
derivedHashes: { emailHash: { from: "email" } },
|
|
47
|
+
derivedHashMode: "salted-sha3",
|
|
32
48
|
});
|
|
33
49
|
var saltedH = b.cryptoField.lookupHash("cf_dh_t1", "email", "a@b.com").value;
|
|
34
|
-
check("salted-sha3
|
|
50
|
+
check("salted-sha3 opt-out is 128 hex (SHA3-512)", saltedH.length === 128);
|
|
35
51
|
check("salted-sha3 is deterministic",
|
|
36
52
|
saltedH === b.cryptoField.lookupHash("cf_dh_t1", "email", "a@b.com").value);
|
|
37
53
|
|
|
38
|
-
// ---- hmac-shake256 table mode: SHAKE256/32 → 64 hex chars ----
|
|
54
|
+
// ---- hmac-shake256 table mode (explicit): SHAKE256/32 → 64 hex chars ----
|
|
39
55
|
b.cryptoField.registerTable("cf_dh_t2", {
|
|
40
56
|
sealedFields: ["email"],
|
|
41
57
|
derivedHashes: { emailHash: { from: "email" } },
|
|
@@ -46,6 +62,7 @@ async function run() {
|
|
|
46
62
|
check("hmac-shake256 is deterministic",
|
|
47
63
|
keyedH === b.cryptoField.lookupHash("cf_dh_t2", "email", "a@b.com").value);
|
|
48
64
|
check("keyed hash differs from salted hash", keyedH !== saltedH);
|
|
65
|
+
check("explicit keyed mode matches the new default", keyedH === defaultH);
|
|
49
66
|
|
|
50
67
|
// ---- per-column mode override on a salted-default table ----
|
|
51
68
|
b.cryptoField.registerTable("cf_dh_t3", {
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.cryptoField derived-hash dual-read + upgrade-on-read auto-migrate.
|
|
4
|
+
*
|
|
5
|
+
* The derived-hash default flipped from salted-sha3 (128 hex) to the keyed
|
|
6
|
+
* MAC hmac-shake256 (64 hex) in v0.15.0. The mode is resolved at
|
|
7
|
+
* registerTable from the current default and is NOT persisted, so on upgrade
|
|
8
|
+
* a row written under the OLD default still carries the salted-sha3 digest in
|
|
9
|
+
* its lookup column while new lookups compute the keyed-MAC digest — a silent
|
|
10
|
+
* index miss (find-by-email / destroyAllForUser would skip the legacy row).
|
|
11
|
+
*
|
|
12
|
+
* This proves the non-breaking landing:
|
|
13
|
+
* 1. lookupHashCandidates returns BOTH the keyed-MAC and the legacy
|
|
14
|
+
* salted-sha3 digest, so a match-EITHER query finds the legacy row.
|
|
15
|
+
* 2. unsealRow upgrade-on-read re-hashes a row whose derived-hash column
|
|
16
|
+
* holds the legacy salted digest to the keyed-MAC form (in the returned
|
|
17
|
+
* row AND, with a writable db handle, durably on disk), so the candidate
|
|
18
|
+
* set collapses back to a single value over time.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
var helpers = require("../helpers");
|
|
22
|
+
var b = helpers.b;
|
|
23
|
+
var check = helpers.check;
|
|
24
|
+
var fs = require("fs");
|
|
25
|
+
var os = require("os");
|
|
26
|
+
var path = require("path");
|
|
27
|
+
var { setupTestDb, teardownTestDb } = require("../helpers/db");
|
|
28
|
+
|
|
29
|
+
async function run() {
|
|
30
|
+
var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cf-dual-"));
|
|
31
|
+
try {
|
|
32
|
+
// The default users fixture: sealedFields ["email","name"],
|
|
33
|
+
// derivedHashes { emailHash: { from: "email", normalize: lowercase } },
|
|
34
|
+
// keyed-MAC default mode.
|
|
35
|
+
await setupTestDb(dir);
|
|
36
|
+
|
|
37
|
+
var email = "Alice@Example.com"; // mixed-case → normalize lowercases it
|
|
38
|
+
|
|
39
|
+
// ---- 1. dual-read: lookupHashCandidates names both digests ----
|
|
40
|
+
var keyed = b.cryptoField.lookupHash("users", "email", email);
|
|
41
|
+
check("active lookup hash is the keyed MAC (64 hex)",
|
|
42
|
+
keyed && keyed.value.length === 64);
|
|
43
|
+
check("lookupHash surfaces the legacy salted digest as legacyValue (128 hex)",
|
|
44
|
+
typeof keyed.legacyValue === "string" && keyed.legacyValue.length === 128);
|
|
45
|
+
|
|
46
|
+
// The legacy digest the lib computes is the byte-form a pre-v0.15.0 row
|
|
47
|
+
// carries; treat it as ground truth for the forged legacy row below.
|
|
48
|
+
var legacyHash = keyed.legacyValue;
|
|
49
|
+
check("legacy digest is the salted-sha3 SHA3-512 width (128 hex), distinct from the keyed MAC",
|
|
50
|
+
legacyHash.length === 128 && legacyHash !== keyed.value);
|
|
51
|
+
|
|
52
|
+
var cands = b.cryptoField.lookupHashCandidates("users", "email", email);
|
|
53
|
+
check("lookupHashCandidates returns the derived field name",
|
|
54
|
+
cands && cands.field === "emailHash");
|
|
55
|
+
check("candidates list carries BOTH the keyed and legacy digests (match-either)",
|
|
56
|
+
cands.values.length === 2 &&
|
|
57
|
+
cands.values.indexOf(keyed.value) !== -1 &&
|
|
58
|
+
cands.values.indexOf(legacyHash) !== -1);
|
|
59
|
+
|
|
60
|
+
// ---- 2. forge a LEGACY-indexed row on disk ----
|
|
61
|
+
// Seal the email under the live envelope but OVERWRITE the derived-hash
|
|
62
|
+
// column with the legacy salted digest, mimicking a row written before
|
|
63
|
+
// the default flipped. Insert via the raw handle so the framework's
|
|
64
|
+
// write boundary doesn't recompute emailHash to the keyed form.
|
|
65
|
+
var sealed = b.cryptoField.sealRow("users", { _id: "u-legacy", email: email, name: "Alice" });
|
|
66
|
+
sealed.emailHash = legacyHash; // pin the LEGACY digest, not the keyed one
|
|
67
|
+
b.db.prepare(
|
|
68
|
+
'INSERT INTO "users" ("_id","email","emailHash","name") VALUES (?,?,?,?)'
|
|
69
|
+
).run(sealed._id, sealed.email, sealed.emailHash, sealed.name);
|
|
70
|
+
|
|
71
|
+
var onDiskBefore = b.db.prepare('SELECT "emailHash" AS h FROM "users" WHERE _id = ?').get("u-legacy");
|
|
72
|
+
check("forged row stores the legacy salted-sha3 emailHash on disk",
|
|
73
|
+
onDiskBefore.h === legacyHash && onDiskBefore.h.length === 128);
|
|
74
|
+
|
|
75
|
+
// A match-either query (the candidate list) FINDS the legacy row even
|
|
76
|
+
// though the keyed-only lookup would have missed it.
|
|
77
|
+
var foundLegacy = b.db.prepare(
|
|
78
|
+
'SELECT _id FROM "users" WHERE "emailHash" = ?'
|
|
79
|
+
).get(legacyHash);
|
|
80
|
+
check("legacy-indexed row is found via the legacy candidate hash",
|
|
81
|
+
foundLegacy && foundLegacy._id === "u-legacy");
|
|
82
|
+
var missedByKeyed = b.db.prepare(
|
|
83
|
+
'SELECT _id FROM "users" WHERE "emailHash" = ?'
|
|
84
|
+
).get(keyed.value);
|
|
85
|
+
check("keyed-only lookup MISSES the legacy row (why dual-read is needed)",
|
|
86
|
+
missedByKeyed === undefined || missedByKeyed === null);
|
|
87
|
+
|
|
88
|
+
// ---- 3. upgrade-on-read: unsealRow re-hashes to the keyed MAC ----
|
|
89
|
+
var rawRow = b.db.prepare('SELECT * FROM "users" WHERE _id = ?').get("u-legacy");
|
|
90
|
+
// Pass the writable local db handle so the durable rewrite fires.
|
|
91
|
+
var unsealed = b.cryptoField.unsealRow("users", rawRow, "actor-1", b.db);
|
|
92
|
+
check("unsealRow decrypted the sealed email back to plaintext",
|
|
93
|
+
unsealed.email === email);
|
|
94
|
+
check("upgrade-on-read: returned row's emailHash is now the keyed MAC",
|
|
95
|
+
unsealed.emailHash === keyed.value && unsealed.emailHash.length === 64);
|
|
96
|
+
|
|
97
|
+
var onDiskAfter = b.db.prepare('SELECT "emailHash" AS h FROM "users" WHERE _id = ?').get("u-legacy");
|
|
98
|
+
check("upgrade-on-read: the row's emailHash was durably re-written to the keyed MAC",
|
|
99
|
+
onDiskAfter.h === keyed.value && onDiskAfter.h.length === 64);
|
|
100
|
+
|
|
101
|
+
// After the upgrade, the keyed-only lookup now finds the row directly.
|
|
102
|
+
var foundKeyed = b.db.prepare(
|
|
103
|
+
'SELECT _id FROM "users" WHERE "emailHash" = ?'
|
|
104
|
+
).get(keyed.value);
|
|
105
|
+
check("post-migrate: keyed-only lookup now finds the upgraded row",
|
|
106
|
+
foundKeyed && foundKeyed._id === "u-legacy");
|
|
107
|
+
|
|
108
|
+
// ---- 4. idempotence: a row already keyed is left untouched ----
|
|
109
|
+
var rawRow2 = b.db.prepare('SELECT * FROM "users" WHERE _id = ?').get("u-legacy");
|
|
110
|
+
var unsealed2 = b.cryptoField.unsealRow("users", rawRow2, "actor-1", b.db);
|
|
111
|
+
check("re-reading an already-keyed row leaves its emailHash unchanged",
|
|
112
|
+
unsealed2.emailHash === keyed.value);
|
|
113
|
+
|
|
114
|
+
// ---- 5. no-handle read resolves the framework db for the rewrite ----
|
|
115
|
+
// Re-forge a legacy row, unseal WITHOUT an explicit dbHandle: unsealRow
|
|
116
|
+
// resolves the framework's local db itself (the same fallback the K_row
|
|
117
|
+
// read path uses), so the returned row carries the keyed hash AND the
|
|
118
|
+
// durable rewrite still lands — keyed reads must work on every path.
|
|
119
|
+
var sealed3 = b.cryptoField.sealRow("users", { _id: "u-legacy-2", email: email, name: "Bob" });
|
|
120
|
+
sealed3.emailHash = legacyHash;
|
|
121
|
+
b.db.prepare(
|
|
122
|
+
'INSERT INTO "users" ("_id","email","emailHash","name") VALUES (?,?,?,?)'
|
|
123
|
+
).run(sealed3._id, sealed3.email, sealed3.emailHash, sealed3.name);
|
|
124
|
+
var raw3 = b.db.prepare('SELECT * FROM "users" WHERE _id = ?').get("u-legacy-2");
|
|
125
|
+
var unsealedNoHandle = b.cryptoField.unsealRow("users", raw3); // no explicit dbHandle
|
|
126
|
+
check("no-handle unseal surfaces the keyed hash on the returned row",
|
|
127
|
+
unsealedNoHandle.emailHash === keyed.value);
|
|
128
|
+
var disk3 = b.db.prepare('SELECT "emailHash" AS h FROM "users" WHERE _id = ?').get("u-legacy-2");
|
|
129
|
+
check("no-handle unseal still durably upgrades via the resolved framework db",
|
|
130
|
+
disk3.h === keyed.value);
|
|
131
|
+
|
|
132
|
+
// ---- 6. THE REAL CONSUMER PATH: b.db.from().where on a sealed field ----
|
|
133
|
+
// Operators — and the framework's own api-key / session / audit / mail
|
|
134
|
+
// stores — find a row by a sealed field through the equality rewrite,
|
|
135
|
+
// which MUST dual-read across the keyed-MAC flip or it silently drops
|
|
136
|
+
// un-migrated rows (the keyed-only lookup misses the legacy digest).
|
|
137
|
+
// Forge a FRESH legacy row (not yet upgraded) and find it via the real
|
|
138
|
+
// query path — this is the path the primitive-only test above never
|
|
139
|
+
// exercised.
|
|
140
|
+
var sealed6 = b.cryptoField.sealRow("users", { _id: "u-legacy-q", email: email, name: "Dave" });
|
|
141
|
+
sealed6.emailHash = legacyHash;
|
|
142
|
+
b.db.prepare(
|
|
143
|
+
'INSERT INTO "users" ("_id","email","emailHash","name") VALUES (?,?,?,?)'
|
|
144
|
+
).run(sealed6._id, sealed6.email, sealed6.emailHash, sealed6.name);
|
|
145
|
+
var viaQuery = await b.db.from("users").where("email", email).all();
|
|
146
|
+
check("real consumer path (b.db.from().where on a sealed field) finds the un-migrated legacy row",
|
|
147
|
+
Array.isArray(viaQuery) && viaQuery.some(function (r) { return r._id === "u-legacy-q"; }));
|
|
148
|
+
|
|
149
|
+
// b.db.hashCandidatesFor — the db-level dual-read helper the framework's
|
|
150
|
+
// bespoke stores (consent/subject) compose for whereIn lookups.
|
|
151
|
+
var dbCands = b.db.hashCandidatesFor("users", "email", email);
|
|
152
|
+
check("b.db.hashCandidatesFor returns both the keyed and legacy digests",
|
|
153
|
+
dbCands && dbCands.field === "emailHash" && dbCands.values.length === 2);
|
|
154
|
+
|
|
155
|
+
console.log("OK — crypto-field dual-read + auto-migrate tests");
|
|
156
|
+
} finally {
|
|
157
|
+
await teardownTestDb(dir);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = { run: run };
|
|
162
|
+
if (require.main === module) {
|
|
163
|
+
run().then(function () { process.exit(0); })
|
|
164
|
+
.catch(function (err) { process.exitCode = 1; throw err; });
|
|
165
|
+
}
|