@blamejs/blamejs-shop 0.4.30 → 0.4.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/checkout.js +8 -0
  4. package/lib/order.js +71 -11
  5. package/lib/vendor/MANIFEST.json +392 -278
  6. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  7. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  8. package/lib/vendor/blamejs/.gitignore +6 -0
  9. package/lib/vendor/blamejs/CHANGELOG.md +26 -0
  10. package/lib/vendor/blamejs/MIGRATING.md +43 -0
  11. package/lib/vendor/blamejs/README.md +8 -6
  12. package/lib/vendor/blamejs/SECURITY.md +19 -3
  13. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  14. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  15. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  16. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  17. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  18. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  19. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  20. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  21. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  22. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  23. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  24. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  25. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  26. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  27. package/lib/vendor/blamejs/index.js +4 -0
  28. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  29. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  30. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  31. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  32. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  33. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  34. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  35. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  36. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  37. package/lib/vendor/blamejs/lib/audit.js +259 -123
  38. package/lib/vendor/blamejs/lib/auth/oauth.js +53 -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 +31 -5
  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 +206 -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/crypto-field.js +916 -156
  55. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  56. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  57. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  58. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  59. package/lib/vendor/blamejs/lib/db.js +249 -99
  60. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  61. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  62. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  63. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  64. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  65. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  66. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  67. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  68. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  69. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  70. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  71. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  72. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  73. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  74. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  75. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  76. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  77. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  78. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  79. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  80. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  81. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  82. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  83. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  84. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  85. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  86. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  87. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  88. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  89. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  90. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  91. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  92. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  93. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  94. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  95. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  96. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  97. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  98. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  99. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  100. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  101. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  102. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  103. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  104. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  105. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  106. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  107. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  108. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  109. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  110. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  111. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  112. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  113. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  114. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  115. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  116. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  117. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  118. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  119. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  120. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  121. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  122. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  123. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  124. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  125. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  126. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  127. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  128. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  129. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  131. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  132. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  133. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  134. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  135. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  136. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  137. package/lib/vendor/blamejs/lib/mail.js +8 -4
  138. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  139. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  140. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  141. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  142. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  143. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  144. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  145. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  146. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  147. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  148. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  149. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  150. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  151. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  152. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  153. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  154. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  155. package/lib/vendor/blamejs/lib/observability.js +124 -0
  156. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  157. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  158. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  159. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  160. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  161. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  162. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  163. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  164. package/lib/vendor/blamejs/lib/queue.js +7 -0
  165. package/lib/vendor/blamejs/lib/redact.js +68 -11
  166. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  167. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  168. package/lib/vendor/blamejs/lib/retention.js +101 -40
  169. package/lib/vendor/blamejs/lib/router.js +212 -5
  170. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  171. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  172. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  173. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  174. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  175. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  176. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  177. package/lib/vendor/blamejs/lib/scheduler.js +35 -12
  178. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  179. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  180. package/lib/vendor/blamejs/lib/session.js +175 -77
  181. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  182. package/lib/vendor/blamejs/lib/sse.js +26 -0
  183. package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
  184. package/lib/vendor/blamejs/lib/static.js +177 -34
  185. package/lib/vendor/blamejs/lib/subject.js +96 -49
  186. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  187. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  188. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  189. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  190. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  191. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  192. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  193. package/lib/vendor/blamejs/package.json +2 -2
  194. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  195. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  200. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  201. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  202. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  203. package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
  204. package/lib/vendor/blamejs/scripts/release.js +398 -38
  205. package/lib/vendor/blamejs/test/00-primitives.js +117 -0
  206. package/lib/vendor/blamejs/test/10-state.js +140 -14
  207. package/lib/vendor/blamejs/test/20-db.js +65 -2
  208. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  209. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  210. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  211. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  212. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  213. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  214. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  215. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  216. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  217. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  220. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  221. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  222. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  223. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  224. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  225. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  226. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  227. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  228. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  229. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  230. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  231. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  232. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  233. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  234. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  235. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  236. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  237. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  238. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  240. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  248. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  250. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
  251. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  253. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  256. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  259. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  260. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  261. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  271. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  272. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  273. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  274. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  275. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  277. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  278. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  279. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  280. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  282. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  283. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  284. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  285. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  293. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  304. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  308. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  310. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  312. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  313. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  314. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  315. package/lib/vendor/blamejs/test/smoke.js +79 -21
  316. package/package.json +1 -1
  317. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  318. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  319. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  320. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  321. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  322. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  323. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  324. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  325. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  326. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  327. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  328. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  329. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  330. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  331. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  332. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  333. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  334. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  335. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  336. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  337. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  338. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -0,0 +1,350 @@
1
+ "use strict";
2
+ /**
3
+ * b.cryptoField per-row key (K_row) crypto-shred substrate.
4
+ *
5
+ * declarePerRowKey opts a table into per-row keying. On INSERT the write
6
+ * boundary materializes a fresh CSPRNG row-secret, derives K_row from it,
7
+ * stores the SECRET (never K_row) AAD-sealed in _blamejs_per_row_keys, and
8
+ * seals the row's sealed columns under K_row as vault.row: cells. Reads
9
+ * fetch + unwrap the secret, re-derive K_row, and decrypt. Destroying the
10
+ * wrapped secret (b.subject.eraseHard / b.retention) leaves WAL / replica
11
+ * residual ciphertext mathematically undecryptable — the crypto-shred.
12
+ *
13
+ * Regression for the v0.14.25 critical class:
14
+ * - pre-fix K_row derived from the PLAINTEXT-on-disk derivedHash salt,
15
+ * so an attacker with disk access re-derived it and deleting the wrap
16
+ * shred NOTHING. The row-secret is now random.
17
+ * - pre-fix the wrap was sealed WITHOUT AAD despite a copy-protection
18
+ * claim. The wrap + the cells are now AAD-bound to (table, rowId,
19
+ * column, schemaVersion).
20
+ * - pre-fix materializePerRowKey was NEVER called on INSERT (dead
21
+ * surface). It is now wired at the db-query write boundary.
22
+ *
23
+ * Pins: materialize-rowId == destroy-rowId == _id; the residency-tag
24
+ * column is NEVER K_row-sealed; a copied cell fails Poly1305; a vault
25
+ * keypair rotation reseals the wrapped secret old-root -> new-root.
26
+ */
27
+
28
+ var helpers = require("../helpers");
29
+ var b = helpers.b;
30
+ var fs = require("fs");
31
+ var os = require("os");
32
+ var path = require("path");
33
+ var check = helpers.check;
34
+ var { setupTestDb, teardownTestDb } = require("../helpers/db");
35
+
36
+ var ROW_PREFIX = require("../../lib/constants").ROW_PREFIX;
37
+
38
+ // App table opted into per-row keying. `subjectId` is the plaintext
39
+ // subject column eraseHard matches on; `dataRegion` is the plaintext
40
+ // residency tag (declarePerRowResidency) that must NEVER be K_row-sealed;
41
+ // `ssn` / `note` are the sealed columns that become vault.row: cells.
42
+ var KEYED_SCHEMA = [{
43
+ name: "pr_keyed",
44
+ columns: {
45
+ _id: "TEXT PRIMARY KEY",
46
+ subjectId: "TEXT",
47
+ dataRegion: "TEXT",
48
+ ssn: "TEXT",
49
+ note: "TEXT",
50
+ },
51
+ indexes: ["subjectId"],
52
+ sealedFields: ["ssn", "note"],
53
+ subjectField: "subjectId",
54
+ }];
55
+
56
+ function _perRowKeyCount(rowId) {
57
+ var row = b.db.prepare(
58
+ 'SELECT COUNT(*) AS n FROM "_blamejs_per_row_keys" WHERE tableName = ? AND rowId = ?'
59
+ ).get("pr_keyed", rowId);
60
+ return row ? row.n : 0;
61
+ }
62
+
63
+ function _rawCell(rowId, col) {
64
+ var row = b.db.prepare('SELECT "' + col + '" AS v FROM "pr_keyed" WHERE _id = ?').get(rowId);
65
+ return row ? row.v : null;
66
+ }
67
+
68
+ async function run() {
69
+ var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cf-prk-"));
70
+ try {
71
+ await setupTestDb(dir, KEYED_SCHEMA);
72
+ b.cryptoField.clearResidencyForTest();
73
+ b.cryptoField.declarePerRowKey("pr_keyed", { keySize: 32 });
74
+ b.cryptoField.declarePerRowResidency("pr_keyed", {
75
+ residencyColumn: "dataRegion",
76
+ allowedTags: ["eu", "us", "global"],
77
+ });
78
+ check("hasPerRowKey true after declare", b.cryptoField.hasPerRowKey("pr_keyed") === true);
79
+
80
+ // ---- INSERT: materialize + K_row-seal at the write boundary ----
81
+ var inserted = b.db.from("pr_keyed").insertOne({
82
+ _id: "row-1", subjectId: "subj-A", dataRegion: "eu", ssn: "123-45-6789", note: "patient note one",
83
+ });
84
+ check("insertOne returns plaintext _id", inserted._id === "row-1");
85
+
86
+ // A _blamejs_per_row_keys entry appears for this row's _id.
87
+ check("per-row-keys registry row created for _id", _perRowKeyCount("row-1") === 1);
88
+
89
+ // The stored wrapped secret is AAD-sealed (vault.aad: prefix).
90
+ var wrapRow = b.db.prepare(
91
+ 'SELECT wrappedKey FROM "_blamejs_per_row_keys" WHERE tableName = ? AND rowId = ?'
92
+ ).get("pr_keyed", "row-1");
93
+ check("wrapped secret stored AAD-sealed (vault.aad:)",
94
+ typeof wrapRow.wrappedKey === "string" && wrapRow.wrappedKey.indexOf("vault.aad:") === 0);
95
+
96
+ // Sealed columns on disk carry the vault.row: prefix (K_row cells) —
97
+ // asserted via the public b.cryptoField.isRowSealed primitive.
98
+ check("ssn column is a vault.row: cell", b.cryptoField.isRowSealed(_rawCell("row-1", "ssn")));
99
+ check("note column is a vault.row: cell", b.cryptoField.isRowSealed(_rawCell("row-1", "note")));
100
+ check("ROW_PREFIX is the vault.row: envelope isRowSealed detects",
101
+ String(_rawCell("row-1", "ssn")).indexOf(ROW_PREFIX) === 0);
102
+
103
+ // PIN: the residency-tag column stays plaintext (never K_row-sealed);
104
+ // isRowSealed is false for a plaintext cell.
105
+ check("residency tag column stays plaintext", _rawCell("row-1", "dataRegion") === "eu");
106
+ check("isRowSealed false for the plaintext residency tag",
107
+ b.cryptoField.isRowSealed(_rawCell("row-1", "dataRegion")) === false);
108
+
109
+ // ---- READ: round-trips to plaintext ----
110
+ var got = b.db.from("pr_keyed").where({ _id: "row-1" }).first();
111
+ check("read round-trips ssn", got.ssn === "123-45-6789");
112
+ check("read round-trips note", got.note === "patient note one");
113
+ check("read surfaces residency tag verbatim", got.dataRegion === "eu");
114
+
115
+ // ---- Codex P1 regression: unsealRow with NO dbHandle (the
116
+ // break-glass / clusterStorage read path) still decrypts a keyed row.
117
+ // crypto-field resolves the local db itself for the wrapped-secret
118
+ // lookup, so a direct caller doesn't null every K_row cell. ----
119
+ var rawRow = b.db.prepare('SELECT * FROM "pr_keyed" WHERE _id = ?').get("row-1");
120
+ check("raw row carries a vault.row: cell pre-unseal", b.cryptoField.isRowSealed(rawRow.ssn));
121
+ var noHandle = b.cryptoField.unsealRow("pr_keyed", rawRow); // 4th arg (dbHandle) omitted
122
+ check("unsealRow without a dbHandle still decrypts ssn", noHandle.ssn === "123-45-6789");
123
+ check("unsealRow without a dbHandle still decrypts note", noHandle.note === "patient note one");
124
+
125
+ // all() path round-trips too.
126
+ var all = b.db.from("pr_keyed").where({ subjectId: "subj-A" }).all();
127
+ check("all() round-trips one keyed row", all.length === 1 && all[0].ssn === "123-45-6789");
128
+
129
+ // ---- COPY-ROW ATTACK: paste another row's cell ----
130
+ b.db.from("pr_keyed").insertOne({
131
+ _id: "row-2", subjectId: "subj-B", dataRegion: "us", ssn: "999-88-7777", note: "patient note two",
132
+ });
133
+ var row2Ssn = _rawCell("row-2", "ssn"); // a valid vault.row: cell, but bound to row-2
134
+ // Overwrite row-1's ssn cell with row-2's ciphertext via raw SQL (a
135
+ // DB-write attacker). row-1's K_row + AAD differ, so Poly1305 fails.
136
+ b.db.prepare('UPDATE "pr_keyed" SET "ssn" = ? WHERE _id = ?').run(row2Ssn, "row-1");
137
+ var tampered = b.db.from("pr_keyed").where({ _id: "row-1" }).first();
138
+ check("copied cell from another row fails to decrypt (null)", tampered.ssn === null);
139
+ check("untouched cell on the same row still decrypts", tampered.note === "patient note one");
140
+ // Restore a valid row-1 ssn cell for the erase/rotation phases.
141
+ b.db.from("pr_keyed").where({ _id: "row-1" }).updateOne({ ssn: "123-45-6789" });
142
+ check("re-seal under K_row round-trips after update",
143
+ b.db.from("pr_keyed").where({ _id: "row-1" }).first().ssn === "123-45-6789");
144
+
145
+ // ---- ERASE-HARD: crypto-shred ----
146
+ var result = b.subject.eraseHard("subj-A", {
147
+ reason: "test-crypto-shred",
148
+ acknowledgements: ["no-litigation-hold", "no-statutory-retention-required"],
149
+ });
150
+ check("eraseHard destroyed 1 per-row key", result.perRowKeysDestroyed === 1);
151
+ check("eraseHard deleted the row", result.perTable.pr_keyed === 1);
152
+ check("per-row-keys registry row gone after shred", _perRowKeyCount("row-1") === 0);
153
+ // The row itself was DELETEd by eraseHard; assert the wrapped secret
154
+ // is gone (the residual-ciphertext shred). A post-shred re-insert of
155
+ // the SAME _id would mint a NEW random secret — the old WAL cell would
156
+ // never decrypt under it.
157
+ check("subj-B row still present + decrypts (shred is row-scoped)",
158
+ b.db.from("pr_keyed").where({ _id: "row-2" }).first().ssn === "999-88-7777");
159
+
160
+ // ---- POST-DESTROY read-absent: a vault.row: cell with no wrap ----
161
+ // Re-create a wrap-less keyed cell: insert, then destroy ONLY its
162
+ // wrapped secret (simulating WAL residue after shred). The read must
163
+ // null the field, not crash.
164
+ b.db.from("pr_keyed").insertOne({
165
+ _id: "row-3", subjectId: "subj-C", dataRegion: "global", ssn: "111-22-3333", note: "n3",
166
+ });
167
+ b.cryptoField.destroyPerRowKey("pr_keyed", "row-3", b.db);
168
+ check("destroyPerRowKey removed the wrap", _perRowKeyCount("row-3") === 0);
169
+ var shredded = b.db.from("pr_keyed").where({ _id: "row-3" }).first();
170
+ check("shredded cell reads as absent (null), no crash", shredded.ssn === null && shredded.note === null);
171
+
172
+ // ---- FORENSIC RESIDUAL PROOF (the crypto-shred guarantee) ----
173
+ // Advertised (crypto-field.js destroyPerRowKey): destroying the wrapped
174
+ // row-secret makes residual ciphertext (WAL / replica / backup)
175
+ // undecryptable EVEN WITH THE VAULT ROOT, because the random row-secret —
176
+ // the only seed for K_row — is gone everywhere it lived. A read returning
177
+ // null after shred proves only the read path; this proves the CIPHERTEXT
178
+ // itself is unrecoverable while the vault root + machinery stay fully
179
+ // functional (a sibling keyed row keeps decrypting throughout).
180
+ var FNEEDLE = "SECRET-SSN-FORENSIC-7Q2";
181
+ b.db.from("pr_keyed").insertOne({
182
+ _id: "row-F", subjectId: "subj-F", dataRegion: "eu", ssn: FNEEDLE, note: "forensic note",
183
+ });
184
+ var residualCipher = _rawCell("row-F", "ssn");
185
+ check("forensic: captured cell is a vault.row: residual envelope",
186
+ b.cryptoField.isRowSealed(residualCipher));
187
+ check("forensic: control — residual cell decrypts before shred",
188
+ b.db.from("pr_keyed").where({ _id: "row-F" }).first().ssn === FNEEDLE);
189
+
190
+ // Seal the working copy to durable db.enc, then scan every on-disk db
191
+ // file (durable sealed copy + tmpfs working copy + WAL): the plaintext must
192
+ // appear in NONE of them, while the sealed envelope DOES persist (a real
193
+ // residual an attacker could lift from WAL / a replica / a backup).
194
+ await b.db.flushToDisk();
195
+ var plainNeedle = Buffer.from(FNEEDLE, "utf8");
196
+ var envNeedle = Buffer.from(residualCipher, "utf8");
197
+ var scanned = 0, plaintextHits = 0, envelopeOnDisk = false;
198
+ [dir, path.join(dir, "tmpfs")].forEach(function (d) {
199
+ var entries;
200
+ try { entries = fs.readdirSync(d); } catch (_e) { return; }
201
+ entries.forEach(function (f) {
202
+ var p = path.join(d, f), st;
203
+ try { st = fs.statSync(p); } catch (_e) { return; }
204
+ if (!st.isFile()) return;
205
+ var buf;
206
+ try { buf = fs.readFileSync(p); } catch (_e) { return; }
207
+ scanned += 1;
208
+ if (buf.indexOf(plainNeedle) !== -1) plaintextHits += 1;
209
+ if (buf.indexOf(envNeedle) !== -1) envelopeOnDisk = true;
210
+ });
211
+ });
212
+ check("forensic: scanned on-disk db files (db.enc + working + wal)", scanned > 0);
213
+ check("forensic: plaintext ssn appears in NO on-disk file (sealed at rest)", plaintextHits === 0);
214
+ check("forensic: the sealed vault.row: envelope DOES persist on disk (real residual)", envelopeOnDisk);
215
+
216
+ // Shred the wrapped secret ONLY — the row + its residual cell stay put.
217
+ var fdestroy = b.cryptoField.destroyPerRowKey("pr_keyed", "row-F", b.db);
218
+ check("forensic: destroyPerRowKey removed exactly the row-F wrap", fdestroy.destroyed === 1);
219
+ check("forensic: wrapped secret gone from registry", _perRowKeyCount("row-F") === 0);
220
+ check("forensic: residual cipher still physically on the row post-shred",
221
+ _rawCell("row-F", "ssn") === residualCipher);
222
+ // The vault root + crypto-field machinery are STILL fully functional: a
223
+ // sibling keyed row (row-2, never shredded) decrypts normally.
224
+ check("forensic: vault root intact — sibling keyed row still decrypts",
225
+ b.db.from("pr_keyed").where({ _id: "row-2" }).first().ssn === "999-88-7777");
226
+ // Yet the residual cell yields NO plaintext: the random row-secret that
227
+ // seeded K_row is gone, and the vault root alone cannot reconstruct it.
228
+ check("forensic: residual cell undecryptable after shred (root intact)",
229
+ b.db.from("pr_keyed").where({ _id: "row-F" }).first().ssn === null);
230
+
231
+ // Re-materializing the SAME _id mints a NEW random secret -> a DIFFERENT
232
+ // K_row, so the captured residual (sealed under the destroyed K_row) is
233
+ // STILL undecryptable. The shred is irreversible, not a key-reuse window.
234
+ b.cryptoField.materializePerRowKey("pr_keyed", "row-F", b.db);
235
+ check("forensic: a fresh wrap exists again for row-F", _perRowKeyCount("row-F") === 1);
236
+ b.db.prepare('UPDATE "pr_keyed" SET "ssn" = ? WHERE _id = ?').run(residualCipher, "row-F");
237
+ check("forensic: residual STILL undecryptable under a freshly-minted K_row",
238
+ b.db.from("pr_keyed").where({ _id: "row-F" }).first().ssn === null);
239
+ b.db.prepare('DELETE FROM "pr_keyed" WHERE _id = ?').run("row-F");
240
+ b.cryptoField.destroyPerRowKey("pr_keyed", "row-F", b.db);
241
+
242
+ await teardownTestDb(dir);
243
+
244
+ // ---- ROTATION ROUND-TRIP ----
245
+ await _rotationRoundTrip();
246
+ } finally {
247
+ try { b.cryptoField.clearResidencyForTest(); } catch (_e) {}
248
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_e) {}
249
+ }
250
+ console.log("OK — crypto-field per-row-key tests");
251
+ }
252
+
253
+ // A vault keypair rotation must reseal the wrapped row-secret in
254
+ // _blamejs_per_row_keys old-root -> new-root (registerTable({aad:true})
255
+ // wires rotate._rotateColumn). After the swap, the wrap unwraps under the
256
+ // new root, K_row re-derives, and a vault.row: data cell still decrypts.
257
+ async function _rotationRoundTrip() {
258
+ var dirNew = fs.mkdtempSync(path.join(os.tmpdir(), "prk-vr-new-"));
259
+ var dirA = fs.mkdtempSync(path.join(os.tmpdir(), "prk-vr-a-"));
260
+ var staging = path.join(os.tmpdir(), "prk-vr-stg-" + process.pid + "-" + Date.now());
261
+
262
+ async function _reset() {
263
+ process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
264
+ b.cluster._resetForTest();
265
+ b.audit._resetForTest();
266
+ b.vault._resetForTest();
267
+ b.db._resetForTest();
268
+ }
269
+
270
+ try {
271
+ // Fresh keypair to rotate INTO.
272
+ await _reset();
273
+ await b.vault.init({ dataDir: dirNew, mode: "plaintext" });
274
+ var newKeys = JSON.parse(b.vault.getKeysJson());
275
+ b.vault._resetForTest();
276
+
277
+ // Live deployment with a per-row-key table + one keyed row.
278
+ await _reset();
279
+ await b.vault.init({ dataDir: dirA, mode: "plaintext" });
280
+ var oldKeys = JSON.parse(b.vault.getKeysJson());
281
+ check("rotation: old and new keypairs differ",
282
+ JSON.stringify(oldKeys) !== JSON.stringify(newKeys));
283
+ await b.db.init({
284
+ dataDir: dirA, tmpDir: path.join(dirA, "tmpfs"), atRest: "encrypted",
285
+ auditSigning: false, schema: KEYED_SCHEMA,
286
+ });
287
+ b.cryptoField.clearResidencyForTest();
288
+ b.cryptoField.declarePerRowKey("pr_keyed", { keySize: 32 });
289
+ b.cryptoField.declarePerRowResidency("pr_keyed", {
290
+ residencyColumn: "dataRegion", allowedTags: ["eu", "us", "global"],
291
+ });
292
+
293
+ b.db.from("pr_keyed").insertOne({
294
+ _id: "rot-1", subjectId: "s1", dataRegion: "eu", ssn: "555-44-3333", note: "rotate me",
295
+ });
296
+ check("rotation: keyed cell sealed as vault.row:",
297
+ String(_rawCell("rot-1", "ssn")).indexOf(ROW_PREFIX) === 0);
298
+ await b.db.flushToDisk();
299
+ await b.db.close();
300
+
301
+ // Rotate the keypair old -> new. externalAadResealed:true: this
302
+ // deployment uses none of the operator-supplied external AAD stores.
303
+ var rot = await b.vaultRotate.rotate({
304
+ dataDir: dirA, stagingDir: staging, oldKeys: oldKeys, newKeys: newKeys,
305
+ mode: "plaintext", externalAadResealed: true,
306
+ });
307
+ check("rotation: internal verify ok (AAD cells decrypt under new root)",
308
+ !!rot.verifyResult && rot.verifyResult.ok === true);
309
+ check("rotation: processed at least the wrapped-secret + the data cell",
310
+ rot.totalRowsProcessed >= 1);
311
+
312
+ // Swap staging -> dataDir, re-open under the NEW keypair.
313
+ ["db.enc", "db.key.enc", "vault.key"].forEach(function (f) {
314
+ var s = path.join(staging, f);
315
+ if (fs.existsSync(s)) fs.copyFileSync(s, path.join(dirA, f));
316
+ });
317
+ try { fs.rmSync(path.join(dirA, "tmpfs"), { recursive: true, force: true }); } catch (_e) {}
318
+
319
+ await _reset();
320
+ await b.vault.init({ dataDir: dirA, mode: "plaintext" });
321
+ check("rotation: vault now live under the NEW keypair",
322
+ JSON.stringify(JSON.parse(b.vault.getKeysJson())) === JSON.stringify(newKeys));
323
+ await b.db.init({
324
+ dataDir: dirA, tmpDir: path.join(dirA, "tmpfs"), atRest: "encrypted",
325
+ auditSigning: false, schema: KEYED_SCHEMA,
326
+ });
327
+ b.cryptoField.clearResidencyForTest();
328
+ b.cryptoField.declarePerRowKey("pr_keyed", { keySize: 32 });
329
+ b.cryptoField.declarePerRowResidency("pr_keyed", {
330
+ residencyColumn: "dataRegion", allowedTags: ["eu", "us", "global"],
331
+ });
332
+
333
+ var got = b.db.from("pr_keyed").where({ _id: "rot-1" }).first();
334
+ check("rotation: vault.row: cell decrypts after rotation under the new keypair",
335
+ !!got && got.ssn === "555-44-3333" && got.note === "rotate me");
336
+ await b.db.close();
337
+ } finally {
338
+ await _reset();
339
+ try { b.cryptoField.clearResidencyForTest(); } catch (_e) {}
340
+ [dirNew, dirA, staging].forEach(function (d) {
341
+ try { fs.rmSync(d, { recursive: true, force: true }); } catch (_e) {}
342
+ });
343
+ }
344
+ }
345
+
346
+ module.exports = { run: run };
347
+ if (require.main === module) {
348
+ run().then(function () { process.exit(0); })
349
+ .catch(function (err) { process.exitCode = 1; throw err; });
350
+ }
@@ -5,12 +5,14 @@
5
5
  * A DB-write attacker who can write `vault:<crafted>` / `vault.aad:<…>`
6
6
  * payloads to sealed columns can force KEM-decapsulation / AEAD-verify on
7
7
  * attacker-controlled bytes on every read. unsealRow already nulls the
8
- * field + emits system.crypto.unseal_failed, but absent a cap the oracle
9
- * can be hammered indefinitely. configureUnsealRateCap adds an opt-in
10
- * per-(actor, table, column) sliding-window failure cap: past `threshold`
8
+ * field + emits system.crypto.unseal_failed; the per-(actor, table,
9
+ * column) sliding-window failure cap bounds the oracle: past `threshold`
11
10
  * failures inside `windowMs`, further unseal attempts for that tuple are
12
11
  * refused for `cooldownMs` with a typed CryptoFieldRateError + a distinct
13
- * system.crypto.unseal_rate_exceeded audit.
12
+ * system.crypto.unseal_rate_exceeded audit. The cap is ON BY DEFAULT
13
+ * (v0.15.0) — armed at module load — so the oracle is bounded out of the
14
+ * box; configureUnsealRateCap tunes the thresholds, and
15
+ * configureUnsealRateCap(null) is the documented opt-out to audit-only.
14
16
  *
15
17
  * Every window / cooldown assertion drives an INJECTED clock — no real
16
18
  * sleeps — so the test is deterministic on contended runners.
@@ -57,14 +59,30 @@ async function run() {
57
59
  throws(/unknown|allowed|cryptoField.configureUnsealRateCap/i,
58
60
  function () { b.cryptoField.configureUnsealRateCap({ threshold: 3, bogus: 1 }); }));
59
61
 
60
- // ---- default OFF: unconfigured unsealRow is audit-only, never throws ----
61
- b.cryptoField.clearRateCapForTest();
62
- var offThrew = false;
62
+ // ---- default ON (v0.15.0): the cap is armed at module load, so a
63
+ // forged-ciphertext oracle is bounded with no operator action. The
64
+ // default threshold (10) trips well before 50 hammered unseals. ----
65
+ b.cryptoField.clearRateCapForTest(); // restores the secure DEFAULT cap
66
+ var defaultThrew = false;
63
67
  for (var k = 0; k < 50; k++) {
64
- try { b.cryptoField.unsealRow("cf_ratecap", { id: "r1", secret: FORGED }, "attacker"); }
68
+ try { b.cryptoField.unsealRow("cf_ratecap", { id: "r1", secret: FORGED }, "default-attacker"); }
69
+ catch (e) {
70
+ if (e.code === "crypto-field/unseal-rate-exceeded") defaultThrew = true;
71
+ }
72
+ }
73
+ check("cap default-ON: forged-unseal oracle is bounded out of the box",
74
+ defaultThrew === true);
75
+
76
+ // ---- documented opt-out: configureUnsealRateCap(null) restores the
77
+ // pre-v0.15.0 audit-only behaviour (no throw on a forged read). ----
78
+ b.cryptoField.configureUnsealRateCap(null);
79
+ var offThrew = false;
80
+ for (var k2 = 0; k2 < 50; k2++) {
81
+ try { b.cryptoField.unsealRow("cf_ratecap", { id: "r1", secret: FORGED }, "optout-attacker"); }
65
82
  catch (_e) { offThrew = true; }
66
83
  }
67
- check("cap default-off: 50 forged unseals never throw (back-compat)", offThrew === false);
84
+ check("opt-out (configureUnsealRateCap(null)): 50 forged unseals never throw",
85
+ offThrew === false);
68
86
 
69
87
  // ---- injected clock + audit sink ----
70
88
  var nowMs = 1000000;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ // #116: crypto-field's upgrade-on-read durable rewrite (re-hash a legacy
3
+ // salted-sha3 derived-hash column to the keyed MAC) hardcoded
4
+ // dialect: "sqlite" when building the UPDATE, ignoring the actual handle's
5
+ // dialect. The local b.db is sqlite, so it works there — but unsealRow accepts
6
+ // a caller-supplied dbHandle (db-query threads an external Postgres / MySQL
7
+ // handle through the read path). On a MySQL handle the sqlite-dialected UPDATE
8
+ // emits double-quoted identifiers ("users"), which MySQL parses as a string
9
+ // literal and rejects, so the durable re-hash is swallowed (try/catch) and the
10
+ // legacy digest stays on disk forever — the advertised keyed-MAC migration
11
+ // silently never happens off sqlite.
12
+ //
13
+ // RED on the buggy tree: the rewrite SQL is sqlite-quoted ("users") even when
14
+ // the handle declares dialect "mysql". GREEN after the fix: it is backtick-
15
+ // quoted (`users`) — the handle's dialect drives the builder.
16
+
17
+ var helpers = require("../helpers");
18
+ var b = helpers.b;
19
+ var check = helpers.check;
20
+ var fs = require("fs");
21
+ var os = require("os");
22
+ var path = require("path");
23
+ var { setupTestDb, teardownTestDb } = require("../helpers/db");
24
+
25
+ async function run() {
26
+ var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-cf-dialect-"));
27
+ try {
28
+ await setupTestDb(dir);
29
+
30
+ var email = "Alice@Example.com";
31
+ var keyed = b.cryptoField.lookupHash("users", "email", email);
32
+ var legacyHash = keyed.legacyValue;
33
+
34
+ // Forge a legacy-indexed row (the derived-hash column holds the salted
35
+ // digest a pre-v0.15.0 row carries), mirroring crypto-field-dual-read.
36
+ var sealed = b.cryptoField.sealRow("users", { _id: "u-mysql", email: email, name: "Alice" });
37
+ sealed.emailHash = legacyHash;
38
+ b.db.prepare(
39
+ 'INSERT INTO "users" ("_id","email","emailHash","name") VALUES (?,?,?,?)'
40
+ ).run(sealed._id, sealed.email, sealed.emailHash, sealed.name);
41
+
42
+ var rawRow = b.db.prepare('SELECT * FROM "users" WHERE _id = ?').get("u-mysql");
43
+
44
+ // A caller-supplied handle that DECLARES the MySQL dialect (as an external
45
+ // Postgres/MySQL handle does) and captures the durable-rewrite SQL. The
46
+ // statement is a no-op runner — we only assert the dialect of the emitted
47
+ // identifiers, not execute against a real MySQL.
48
+ var capturedSql = null;
49
+ var mysqlHandle = {
50
+ dialect: "mysql",
51
+ prepare: function (sqlText) {
52
+ capturedSql = sqlText;
53
+ return { run: function () { return { changes: 1 }; } };
54
+ },
55
+ };
56
+
57
+ var unsealed = b.cryptoField.unsealRow("users", rawRow, "actor-1", mysqlHandle);
58
+ check("unsealRow still decrypts the sealed email (handle independent of crypto)",
59
+ unsealed.email === email);
60
+ check("upgrade-on-read fired: a durable rewrite UPDATE was built", capturedSql !== null);
61
+ check("#116 the rewrite UPDATE honors the handle's MySQL dialect (backtick-quoted)",
62
+ capturedSql && capturedSql.indexOf("`users`") !== -1);
63
+ check("#116 the rewrite UPDATE does NOT emit sqlite double-quoted identifiers on a MySQL handle",
64
+ capturedSql && capturedSql.indexOf("\"users\"") === -1);
65
+
66
+ console.log("OK — crypto-field upgrade-on-read dialect tests");
67
+ } finally {
68
+ await teardownTestDb(dir);
69
+ }
70
+ }
71
+
72
+ module.exports = { run: run };
73
+ if (require.main === module) {
74
+ run().then(function () { process.exit(0); })
75
+ .catch(function (err) { process.exitCode = 1; throw err; });
76
+ }