@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.
Files changed (343) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -1
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/vendor/MANIFEST.json +400 -282
  5. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  6. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  7. package/lib/vendor/blamejs/.gitignore +6 -0
  8. package/lib/vendor/blamejs/CHANGELOG.md +28 -0
  9. package/lib/vendor/blamejs/MIGRATING.md +55 -0
  10. package/lib/vendor/blamejs/README.md +8 -6
  11. package/lib/vendor/blamejs/SECURITY.md +19 -3
  12. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  13. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  14. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  15. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  16. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  17. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  18. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  19. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  20. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  21. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  22. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  23. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  24. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  25. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  26. package/lib/vendor/blamejs/index.js +4 -0
  27. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  28. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  29. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  30. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  31. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  32. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  33. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  34. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  35. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  36. package/lib/vendor/blamejs/lib/audit.js +259 -123
  37. package/lib/vendor/blamejs/lib/auth/elevation-grant.js +6 -2
  38. package/lib/vendor/blamejs/lib/auth/oauth.js +66 -9
  39. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  40. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  41. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +36 -7
  42. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  43. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  44. package/lib/vendor/blamejs/lib/cache.js +174 -105
  45. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  46. package/lib/vendor/blamejs/lib/cli.js +19 -14
  47. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  48. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  49. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  50. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  51. package/lib/vendor/blamejs/lib/compliance.js +210 -4
  52. package/lib/vendor/blamejs/lib/consent.js +82 -29
  53. package/lib/vendor/blamejs/lib/constants.js +27 -11
  54. package/lib/vendor/blamejs/lib/credential-hash.js +9 -0
  55. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  56. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  57. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  58. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  59. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  60. package/lib/vendor/blamejs/lib/db.js +249 -99
  61. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  62. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  63. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  64. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  65. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  66. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  67. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  68. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  69. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  70. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  71. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  72. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  73. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  74. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  75. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  76. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  77. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  78. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  79. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  80. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  81. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  82. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  83. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  84. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  85. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  86. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  87. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  88. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  89. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  90. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  91. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  92. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  93. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  94. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  95. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  96. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  97. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  98. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  99. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  100. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  101. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  102. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  103. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  104. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  105. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  106. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  107. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  108. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  109. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  110. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  111. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  112. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  113. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  114. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  115. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  116. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  117. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  118. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  119. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  120. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  121. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  122. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  123. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  124. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  125. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  126. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  127. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  128. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  129. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  131. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  132. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  133. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  134. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  135. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  136. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  137. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  138. package/lib/vendor/blamejs/lib/mail.js +8 -4
  139. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  140. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  141. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  142. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  143. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  144. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  145. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  146. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  147. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  148. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  149. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  150. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  151. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  152. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  153. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  154. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  155. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  156. package/lib/vendor/blamejs/lib/observability.js +124 -0
  157. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  158. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  159. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  160. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  161. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  162. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  163. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  164. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  165. package/lib/vendor/blamejs/lib/queue.js +7 -0
  166. package/lib/vendor/blamejs/lib/redact.js +68 -11
  167. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  168. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  169. package/lib/vendor/blamejs/lib/retention.js +117 -42
  170. package/lib/vendor/blamejs/lib/router.js +212 -5
  171. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  172. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  173. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  174. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  175. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  176. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  177. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  178. package/lib/vendor/blamejs/lib/scheduler.js +47 -12
  179. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  180. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  181. package/lib/vendor/blamejs/lib/session.js +175 -77
  182. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  183. package/lib/vendor/blamejs/lib/sse.js +26 -0
  184. package/lib/vendor/blamejs/lib/ssrf-guard.js +169 -4
  185. package/lib/vendor/blamejs/lib/static.js +177 -34
  186. package/lib/vendor/blamejs/lib/subject.js +96 -49
  187. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  188. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  189. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  190. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  191. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  192. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  193. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  194. package/lib/vendor/blamejs/package.json +2 -2
  195. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  200. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  201. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  202. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  203. package/lib/vendor/blamejs/release-notes/v0.15.7.json +43 -0
  204. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  205. package/lib/vendor/blamejs/scripts/gen-migrating.js +67 -0
  206. package/lib/vendor/blamejs/scripts/release.js +398 -38
  207. package/lib/vendor/blamejs/test/00-primitives.js +168 -0
  208. package/lib/vendor/blamejs/test/10-state.js +140 -14
  209. package/lib/vendor/blamejs/test/20-db.js +65 -2
  210. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  211. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  212. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  213. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  214. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  215. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  216. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  217. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  220. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  221. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  222. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  223. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  224. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  225. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  226. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  227. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  228. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  229. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  230. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  231. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  232. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  233. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  234. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  235. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  236. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  237. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  238. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  239. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  240. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  242. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  248. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  250. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  251. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  252. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1196 -14
  253. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +18 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  256. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  259. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  267. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  271. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  272. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  273. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  275. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  276. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  277. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  278. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  282. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  283. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  285. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  286. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  287. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  288. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  296. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/retention-floor.test.js +59 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +362 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-watchdog-stale-settle.test.js +71 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  308. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  309. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  310. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  312. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  313. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  314. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  315. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  316. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  317. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  318. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  319. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  320. package/lib/vendor/blamejs/test/smoke.js +79 -21
  321. package/package.json +2 -2
  322. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  323. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  324. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  325. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  326. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  327. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  328. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  329. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  330. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  331. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  332. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  333. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  334. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  335. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  336. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  337. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  338. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  339. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  340. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  341. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  342. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  343. 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
 
@@ -52,6 +52,24 @@ async function testShake256ConfigurableLength() {
52
52
  check("verify 64-byte envelope", (await ch.verify("hi", env64)) === true);
53
53
  check("verify 192-byte envelope", (await ch.verify("hi", env192)) === true);
54
54
  check("64 envelope can't verify against 192", env64 !== env192);
55
+
56
+ // #111 — needsRehash must drive the advertised SHAKE256 length-rotation: a
57
+ // digest stored at the old length must be flagged for rehash when the
58
+ // configured/default length is larger. needsRehash ignored payload length,
59
+ // so raising the output length was a silent no-op.
60
+ check("#111 a 64-byte digest needs rehash under the 128-byte default",
61
+ ch.needsRehash(env64) === true);
62
+ check("#111 a 64-byte digest needs rehash when the target length is raised to 192",
63
+ ch.needsRehash(env64, { params: { length: 192 } }) === true);
64
+ check("#111 a 64-byte digest does NOT need rehash when the target stays 64",
65
+ ch.needsRehash(env64, { params: { length: 64 } }) === false);
66
+ check("#111 a default-length (128) digest does not need rehash at the default",
67
+ ch.needsRehash(await ch.hash("hi")) === false);
68
+ // Upgrade-only, matching the Argon2 needsRehash convention (rehash when the
69
+ // stored strength is BELOW target, never to actively shorten): a 192-byte
70
+ // digest must NOT be rehashed down to a 128-byte target.
71
+ check("#111 a longer (192) digest is NOT rehashed down to a smaller target (128)",
72
+ ch.needsRehash(env192, { params: { length: 128 } }) === false);
55
73
  }
56
74
 
57
75
  async function testShake256BufferSecret() {
@@ -4,10 +4,12 @@
4
4
  *
5
5
  * Equality-lookup ("derived") hashes for sealed columns can be computed
6
6
  * two ways:
7
- * - salted-sha3 (default) — SHA3-512 over a per-deployment salt.
8
- * - hmac-shake256 (opt-in) — keyed MAC off vault.getDerivedHashMacKey,
9
- * so the hash is unforgeable without the
10
- * per-deployment MAC key.
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
- // ---- salted-sha3 is the default: SHA3-512128 hex chars ----
30
+ // ---- DEFAULT (v0.15.0) is the keyed MAC: hmac-shake25664 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 default is 128 hex (SHA3-512)", saltedH.length === 128);
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
+ }