@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
@@ -29,9 +29,20 @@ var SCHEMA = [{
29
29
  status: "TEXT DEFAULT 'active'",
30
30
  },
31
31
  indexes: ["status"],
32
+ }, {
33
+ // Residency-gate fixture table — `dataRegion` carries the per-row
34
+ // residency tag (plaintext, inspected before sealRow); `addr` is a
35
+ // region-bound column for the per-column gate.
36
+ name: "residents",
37
+ columns: {
38
+ _id: "TEXT PRIMARY KEY",
39
+ name: "TEXT",
40
+ dataRegion: "TEXT",
41
+ addr: "TEXT",
42
+ },
32
43
  }];
33
44
 
34
- async function initDb(tmpDir, columnGate) {
45
+ async function initDb(tmpDir, columnGate, dataResidency) {
35
46
  process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
36
47
  helpers.setTestPassphraseEnv();
37
48
  b.cluster._resetForTest();
@@ -41,6 +52,7 @@ async function initDb(tmpDir, columnGate) {
41
52
  await b.vault.init({ dataDir: tmpDir });
42
53
  var opts = { dataDir: tmpDir, tmpDir: path.join(tmpDir, "tmpfs"), schema: SCHEMA };
43
54
  if (columnGate !== undefined) opts.columnGate = columnGate;
55
+ if (dataResidency !== undefined) opts.dataResidency = dataResidency;
44
56
  await b.db.init(opts);
45
57
  }
46
58
 
@@ -136,9 +148,176 @@ async function run() {
136
148
  b.vault._resetForTest();
137
149
  b.cluster._resetForTest();
138
150
 
151
+ // ---- per-row + per-column residency write gates via db.from(...) ----
152
+ await runRowResidencyGate();
153
+ await runColumnResidencyGate();
154
+ await runRowResidencyRegionGate();
155
+
139
156
  console.log("OK — db column-gate tests");
140
157
  }
141
158
 
159
+ // Local per-row residency gate driven through db.from(table).insertOne /
160
+ // .updateOne. The fixture's db has NO dataResidency region configured,
161
+ // so the local-mismatch refusal can't fire here (the gate passes when no
162
+ // region is set) — this covers the missing-tag / invalid-tag refusals,
163
+ // the valid-tag success path (row written + readable back), and the
164
+ // update matrix. Region-mismatch lives in runRowResidencyRegionGate.
165
+ async function runRowResidencyGate() {
166
+ var tmp = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-rowresid-"));
167
+ await initDb(tmp);
168
+ b.cryptoField.clearResidencyForTest();
169
+ try {
170
+ b.cryptoField.declarePerRowResidency("residents", {
171
+ residencyColumn: "dataRegion",
172
+ allowedTags: ["eu-west-1", "us-east-1", "global"],
173
+ });
174
+
175
+ // INSERT without the tag column → refused.
176
+ check("insert without residency tag refused",
177
+ insertCode("residents", { _id: "r-miss", name: "x" }) ===
178
+ "db-query/row-residency-tag-missing");
179
+
180
+ // INSERT with a tag outside allowedTags → refused.
181
+ check("insert with tag not in allowedTags refused",
182
+ insertCode("residents", { _id: "r-bad", name: "x", dataRegion: "ap-south-1" }) ===
183
+ "db-query/row-residency-tag-invalid");
184
+
185
+ // INSERT with a valid tag → row written AND readable back.
186
+ var ins = b.db.from("residents").insertOne({
187
+ _id: "r-ok", name: "alice", dataRegion: "eu-west-1",
188
+ });
189
+ check("insert with valid residency tag returns the row",
190
+ ins && ins._id === "r-ok" && ins.dataRegion === "eu-west-1");
191
+ var readBack = b.db.from("residents").where({ _id: "r-ok" }).first();
192
+ check("valid-tag row is readable back after insert",
193
+ readBack && readBack.name === "alice" && readBack.dataRegion === "eu-west-1");
194
+
195
+ // UPDATE that does NOT touch the residency column on a declared
196
+ // table → passes (not a transfer).
197
+ var updNonResidency = b.db.from("residents").where({ _id: "r-ok" }).updateOne({ name: "alice-2" });
198
+ check("update not touching residency column passes", updNonResidency === true);
199
+ check("non-residency update persisted",
200
+ b.db.from("residents").where({ _id: "r-ok" }).first().name === "alice-2");
201
+
202
+ // UPDATE that sets an out-of-allowlist tag → refused.
203
+ check("update to out-of-allowlist residency tag refused",
204
+ updateCode("residents", { _id: "r-ok" }, { dataRegion: "ap-south-1" }) ===
205
+ "db-query/row-residency-tag-invalid");
206
+
207
+ // UPDATE that explicitly NULLs the residency column → refused (the
208
+ // row must not be nullable into an untagged state — INSERT requires
209
+ // a tag, UPDATE must not clear it).
210
+ check("update clearing residency column to null refused",
211
+ updateCode("residents", { _id: "r-ok" }, { dataRegion: null }) ===
212
+ "db-query/row-residency-tag-missing");
213
+ check("residency tag intact after the refused null-update",
214
+ b.db.from("residents").where({ _id: "r-ok" }).first().dataRegion === "eu-west-1");
215
+ } finally {
216
+ b.cryptoField.clearResidencyForTest();
217
+ b.compliance.clear();
218
+ b.db.close();
219
+ fs.rmSync(tmp, { recursive: true, force: true });
220
+ b.audit._resetForTest();
221
+ b.db._resetForTest();
222
+ b.vault._resetForTest();
223
+ b.cluster._resetForTest();
224
+ }
225
+ }
226
+
227
+ // Per-column residency gate wiring through db.from(...).insertOne. The
228
+ // fixture's db has a dataResidency region ("eu-west-1"); a column bound
229
+ // to a different region ("us-east-1") refuses under a cross-border
230
+ // regulated posture (gdpr) and passes (advisory) without a posture.
231
+ async function runColumnResidencyGate() {
232
+ var tmp = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-colresid-"));
233
+ await initDb(tmp, undefined, { region: "eu-west-1" });
234
+ b.cryptoField.clearResidencyForTest();
235
+ try {
236
+ b.cryptoField.declareColumnResidency("residents", {
237
+ columnResidency: { addr: "us-east-1" },
238
+ });
239
+
240
+ // Without a posture → write passes (advisory only).
241
+ var insAdvisory = b.db.from("residents").insertOne({
242
+ _id: "c-advisory", name: "noposture", addr: "10 Main St",
243
+ });
244
+ check("column-residency mismatch without posture passes (advisory)",
245
+ insAdvisory && insAdvisory._id === "c-advisory");
246
+
247
+ // Under gdpr → mismatched column refused.
248
+ b.compliance.set("gdpr");
249
+ check("column-residency mismatch under gdpr refused",
250
+ insertCode("residents", { _id: "c-gdpr", name: "y", addr: "20 Main St" }) ===
251
+ "db-query/column-residency-mismatch");
252
+ } finally {
253
+ b.compliance.clear();
254
+ b.cryptoField.clearResidencyForTest();
255
+ b.db.close();
256
+ fs.rmSync(tmp, { recursive: true, force: true });
257
+ b.audit._resetForTest();
258
+ b.db._resetForTest();
259
+ b.vault._resetForTest();
260
+ b.cluster._resetForTest();
261
+ }
262
+ }
263
+
264
+ // Per-row residency region gate — the fixture supports a custom
265
+ // dataResidency region, so this drives the cross-border local-mismatch
266
+ // refusal: under gdpr a tag in allowedTags but OUTSIDE the deployment's
267
+ // region set is refused; a tag inside the region set or "global" passes.
268
+ async function runRowResidencyRegionGate() {
269
+ var tmp = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-rowresid-region-"));
270
+ await initDb(tmp, undefined, { region: "eu-west-1" });
271
+ b.cryptoField.clearResidencyForTest();
272
+ try {
273
+ b.cryptoField.declarePerRowResidency("residents", {
274
+ residencyColumn: "dataRegion",
275
+ allowedTags: ["eu-west-1", "us-east-1", "global"],
276
+ });
277
+ b.compliance.set("gdpr");
278
+
279
+ // Tag in allowedTags but outside the deployment region → refused.
280
+ check("row tag outside deployment region under gdpr refused",
281
+ insertCode("residents", { _id: "rr-us", name: "u", dataRegion: "us-east-1" }) ===
282
+ "db-query/row-residency-local-mismatch");
283
+
284
+ // Tag matching the deployment region → passes.
285
+ var insEu = b.db.from("residents").insertOne({
286
+ _id: "rr-eu", name: "e", dataRegion: "eu-west-1",
287
+ });
288
+ check("row tag matching deployment region passes",
289
+ insEu && insEu._id === "rr-eu");
290
+
291
+ // "global" tag → passes any region.
292
+ var insGlobal = b.db.from("residents").insertOne({
293
+ _id: "rr-global", name: "g", dataRegion: "global",
294
+ });
295
+ check("row tag 'global' passes any region", insGlobal && insGlobal._id === "rr-global");
296
+ } finally {
297
+ b.compliance.clear();
298
+ b.cryptoField.clearResidencyForTest();
299
+ b.db.close();
300
+ fs.rmSync(tmp, { recursive: true, force: true });
301
+ b.audit._resetForTest();
302
+ b.db._resetForTest();
303
+ b.vault._resetForTest();
304
+ b.cluster._resetForTest();
305
+ }
306
+ }
307
+
308
+ // insertCode / updateCode — return the thrown error's .code when the
309
+ // write refuses, else null. Compose the same chainable surface the gate
310
+ // runs under, so the refusal is exercised end-to-end (not a unit call).
311
+ function insertCode(table, row) {
312
+ try { b.db.from(table).insertOne(row); } catch (e) { return e && e.code; }
313
+ return null;
314
+ }
315
+
316
+ function updateCode(table, whereObj, changes) {
317
+ try { b.db.from(table).where(whereObj).updateOne(changes); } catch (e) { return e && e.code; }
318
+ return null;
319
+ }
320
+
142
321
  module.exports = { run: run };
143
322
  if (require.main === module) {
144
323
  // Rethrow on failure so Node surfaces the error and exits non-zero,
@@ -87,7 +87,8 @@ async function testSchemaQualifiedSelectShape() {
87
87
  new dbQuery.Query(db, "audit.events").where({ recordedAt: 1 }).count();
88
88
  check("count() emits COUNT(*) FROM \"audit\".\"events\"",
89
89
  db.prepared.some(function (s) {
90
- return /SELECT COUNT\(\*\) AS n FROM "audit"\."events"/.test(s);
90
+ // b.sql quotes the aggregate alias by construction: AS "n".
91
+ return /SELECT COUNT\(\*\) AS "n" FROM "audit"\."events"/.test(s);
91
92
  }));
92
93
  }
93
94
 
@@ -109,7 +110,9 @@ async function testSchemaQualifiedInsertUpdateDelete() {
109
110
  }));
110
111
  check("updateOne sub-select uses qualified name",
111
112
  db.prepared.some(function (s) {
112
- return /SELECT rowid FROM "audit"\."events"/.test(s);
113
+ // b.sql quotes the rowid pseudo-column by construction; the
114
+ // single-row idiom is "rowid" = (SELECT "rowid" FROM ...).
115
+ return /SELECT "rowid" FROM "audit"\."events"/.test(s);
113
116
  }));
114
117
 
115
118
  db.prepared.length = 0;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ // #141: a membership query on a SEALED column must work. db-query's sealed
3
+ // field → derived-hash rewrite handled "=" (single value → keyed hash, with a
4
+ // dual-read of the legacy digest across the v0.15.0 keyed-MAC flip) and "!=",
5
+ // but for "IN" it passed the WHOLE candidate array to cryptoField.lookupHash
6
+ // as if it were one plaintext — producing a single bogus hash, which the
7
+ // later array-shape check then rejected with "where IN requires a non-empty
8
+ // array". So b.db.from().whereIn("email", [...]) and
9
+ // b.db.collection().find({ email: { $in: [...] } }) on a sealed column were
10
+ // unusable: the documented derived-hash query path supported equality but not
11
+ // membership. The fix maps EACH array element through lookupHash and builds
12
+ // the combined IN-list (including each element's legacy digest for dual-read).
13
+ //
14
+ // RED on the buggy tree: whereIn / $in on a sealed field throws. GREEN after
15
+ // the fix: both consumer paths return the matching rows, including legacy-
16
+ // digested (un-migrated) rows.
17
+
18
+ var helpers = require("../helpers");
19
+ var b = helpers.b;
20
+ var check = helpers.check;
21
+ var fs = require("fs");
22
+ var os = require("os");
23
+ var path = require("path");
24
+ var { setupTestDb, teardownTestDb } = require("../helpers/db");
25
+
26
+ function _insertSealed(id, email, name) {
27
+ var sealed = b.cryptoField.sealRow("users", { _id: id, email: email, name: name });
28
+ b.db.prepare(
29
+ 'INSERT INTO "users" ("_id","email","emailHash","name") VALUES (?,?,?,?)'
30
+ ).run(sealed._id, sealed.email, sealed.emailHash, sealed.name);
31
+ return sealed;
32
+ }
33
+
34
+ async function run() {
35
+ var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-sealed-in-"));
36
+ try {
37
+ await setupTestDb(dir);
38
+
39
+ var alice = "alice@example.com";
40
+ var bob = "bob@example.com";
41
+ var carol = "carol@example.com";
42
+ _insertSealed("u-alice", alice, "Alice");
43
+ _insertSealed("u-bob", bob, "Bob");
44
+ _insertSealed("u-carol", carol, "Carol");
45
+
46
+ // ---- equality still works (the path that was never broken) ----
47
+ var eqRows = await b.db.from("users").where("email", alice).all();
48
+ check("sealed equality query still finds the row",
49
+ eqRows.length === 1 && eqRows[0]._id === "u-alice");
50
+
51
+ // ---- #141: whereIn on a sealed column (the real consumer path) ----
52
+ var inRows = await b.db.from("users").whereIn("email", [alice, bob]).all();
53
+ var inIds = inRows.map(function (r) { return r._id; }).sort();
54
+ check("#141 whereIn on a sealed column returns every matching row",
55
+ inIds.length === 2 && inIds[0] === "u-alice" && inIds[1] === "u-bob");
56
+ check("#141 whereIn on a sealed column excludes the non-listed row",
57
+ inIds.indexOf("u-carol") === -1);
58
+
59
+ // ---- #141: the collection $in consumer path (same root) ----
60
+ var coll = b.db.collection("users");
61
+ var collRows = coll.find({ email: { $in: [alice, carol] } });
62
+ var collIds = collRows.map(function (r) { return r._id; }).sort();
63
+ check("#141 collection.find $in on a sealed column returns every match",
64
+ collIds.length === 2 && collIds[0] === "u-alice" && collIds[1] === "u-carol");
65
+
66
+ // ---- #141: dual-read — a legacy-digested (un-migrated) row is found ----
67
+ // Forge a row carrying the pre-v0.15.0 salted digest, like the dual-read
68
+ // migrate test. whereIn must include the legacy digest per element.
69
+ var dave = "dave@example.com";
70
+ var lk = b.cryptoField.lookupHash("users", "email", dave);
71
+ var sealedDave = b.cryptoField.sealRow("users", { _id: "u-dave", email: dave, name: "Dave" });
72
+ sealedDave.emailHash = lk.legacyValue; // pin the LEGACY digest
73
+ b.db.prepare(
74
+ 'INSERT INTO "users" ("_id","email","emailHash","name") VALUES (?,?,?,?)'
75
+ ).run(sealedDave._id, sealedDave.email, sealedDave.emailHash, sealedDave.name);
76
+ var dualRows = await b.db.from("users").whereIn("email", [dave, bob]).all();
77
+ var dualIds = dualRows.map(function (r) { return r._id; }).sort();
78
+ check("#141 whereIn dual-reads the legacy digest (finds an un-migrated row)",
79
+ dualIds.indexOf("u-dave") !== -1 && dualIds.indexOf("u-bob") !== -1);
80
+
81
+ // ---- single-element membership ----
82
+ var oneRows = await b.db.from("users").whereIn("email", [carol]).all();
83
+ check("#141 single-element whereIn on a sealed column works",
84
+ oneRows.length === 1 && oneRows[0]._id === "u-carol");
85
+
86
+ // ---- regression: whereIn on a NON-sealed column is unchanged ----
87
+ var idRows = await b.db.from("users").whereIn("_id", ["u-alice", "u-bob"]).all();
88
+ check("non-sealed whereIn is unaffected",
89
+ idRows.map(function (r) { return r._id; }).sort().join(",") === "u-alice,u-bob");
90
+
91
+ console.log("OK — db-query sealed-field IN / $in tests");
92
+ } finally {
93
+ await teardownTestDb(dir);
94
+ }
95
+ }
96
+
97
+ module.exports = { run: run };
98
+ if (require.main === module) {
99
+ run().then(function () { process.exit(0); })
100
+ .catch(function (err) { process.stderr.write(String(err && err.stack || err) + "\n"); process.exit(1); });
101
+ }
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ /**
3
+ * b.db.runSql / b.db.prepare(...).run(...) — local per-row residency gate on
4
+ * the RAW execution path. _assertLocalResidency is wired only at the
5
+ * structured builder boundary (insertOne/updateOne). The raw paths
6
+ * b.db.runSql (execRaw) and b.db.prepare(sql).run(...) write a cross-border
7
+ * row straight to disk with NO residency check under a regulated posture.
8
+ * RED today: builder refuses (control), raw paths do not.
9
+ */
10
+
11
+ var helpers = require("../helpers");
12
+ var b = helpers.b;
13
+ var check = helpers.check;
14
+ var fs = helpers.fs;
15
+ var os = helpers.os;
16
+ var path = helpers.path;
17
+
18
+ var SCHEMA = [{
19
+ name: "residents",
20
+ columns: { _id: "TEXT PRIMARY KEY", name: "TEXT", dataRegion: "TEXT" },
21
+ }];
22
+
23
+ async function initDb(tmpDir) {
24
+ process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
25
+ helpers.setTestPassphraseEnv();
26
+ b.cluster._resetForTest();
27
+ b.audit._resetForTest();
28
+ b.vault._resetForTest();
29
+ b.db._resetForTest();
30
+ await b.vault.init({ dataDir: tmpDir });
31
+ await b.db.init({
32
+ dataDir: tmpDir,
33
+ tmpDir: path.join(tmpDir, "tmpfs"),
34
+ schema: SCHEMA,
35
+ dataResidency: { region: "eu-west-1" },
36
+ });
37
+ }
38
+
39
+ function codeOf(fn) {
40
+ try { fn(); } catch (e) { return (e && e.code) || (e && e.message) || "threw"; }
41
+ return null;
42
+ }
43
+
44
+ async function run() {
45
+ var tmp = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-raw-resid-"));
46
+ await initDb(tmp);
47
+ b.cryptoField.clearResidencyForTest();
48
+ try {
49
+ b.cryptoField.declarePerRowResidency("residents", {
50
+ residencyColumn: "dataRegion",
51
+ allowedTags: ["eu-west-1", "us-east-1", "global"],
52
+ });
53
+ b.compliance.set("gdpr");
54
+
55
+ // CONTROL: the structured builder path refuses the cross-border tag.
56
+ check("control: db.from() builder refuses the cross-border tag",
57
+ codeOf(function () {
58
+ b.db.from("residents").insertOne({ _id: "ctl", name: "x", dataRegion: "us-east-1" });
59
+ }) === "db-query/row-residency-local-mismatch");
60
+
61
+ // RAW PATH 1: b.db.runSql (execRaw). RED today — no refusal.
62
+ var runSqlRefusedCode = codeOf(function () {
63
+ b.db.runSql(
64
+ "INSERT INTO \"residents\" (_id, name, dataRegion) VALUES ('raw-1', 'x', 'us-east-1')");
65
+ });
66
+ check("b.db.runSql cross-border write is refused (residency gate fires on the raw path)",
67
+ runSqlRefusedCode === "db-query/row-residency-local-mismatch");
68
+ check("b.db.runSql cross-border row did not persist (no us-east-1 row landed)",
69
+ b.db.from("residents").where({ _id: "raw-1" }).first() === null);
70
+
71
+ // RAW PATH 2: b.db.prepare(sql).run(...) — the seeders' consumer path.
72
+ var prepareRefusedCode = codeOf(function () {
73
+ b.db.prepare(
74
+ "INSERT INTO \"residents\" (_id, name, dataRegion) VALUES (?, ?, ?)")
75
+ .run("raw-2", "x", "us-east-1");
76
+ });
77
+ check("b.db.prepare().run() cross-border write is refused (residency gate fires)",
78
+ prepareRefusedCode === "db-query/row-residency-local-mismatch");
79
+ check("b.db.prepare().run() cross-border row did not persist",
80
+ b.db.from("residents").where({ _id: "raw-2" }).first() === null);
81
+
82
+ // An in-region raw write still succeeds (no over-rejection).
83
+ b.db.runSql(
84
+ "INSERT INTO \"residents\" (_id, name, dataRegion) VALUES ('raw-eu', 'y', 'eu-west-1')");
85
+ check("an in-region raw write still persists (gate does not over-reject)",
86
+ (b.db.from("residents").where({ _id: "raw-eu" }).first() || {}).dataRegion === "eu-west-1");
87
+
88
+ // RAW PATH 3: a SCHEMA-QUALIFIED table name must resolve to the table and
89
+ // still gate. SQLite accepts INSERT INTO main.residents; capturing only the
90
+ // "main" qualifier would skip the gate for the real target table.
91
+ var qualifiedRefusedCode = codeOf(function () {
92
+ b.db.runSql(
93
+ "INSERT INTO main.residents (_id, name, dataRegion) VALUES ('raw-q', 'x', 'us-east-1')");
94
+ });
95
+ check("qualified-name raw write is refused (main.residents resolves to residents)",
96
+ qualifiedRefusedCode === "db-query/row-residency-local-mismatch");
97
+ check("qualified-name cross-border row did not persist",
98
+ b.db.from("residents").where({ _id: "raw-q" }).first() === null);
99
+
100
+ // RAW PATH 4: an UPDATE whose SET value contains the word WHERE inside a
101
+ // quoted string must be parsed quote-aware, so the residency-column
102
+ // assignment after it is still seen and gated.
103
+ var quotedWhereRefusedCode = codeOf(function () {
104
+ b.db.runSql(
105
+ "UPDATE residents SET name='x WHERE y', dataRegion='us-east-1' WHERE _id='raw-eu'");
106
+ });
107
+ check("update with a quoted WHERE in a SET value is parsed quote-aware + refused",
108
+ quotedWhereRefusedCode === "db-query/row-residency-local-mismatch");
109
+ check("the quoted-WHERE update did not move the row cross-border",
110
+ (b.db.from("residents").where({ _id: "raw-eu" }).first() || {}).dataRegion === "eu-west-1");
111
+ } finally {
112
+ b.compliance.clear();
113
+ b.cryptoField.clearResidencyForTest();
114
+ b.db.close();
115
+ fs.rmSync(tmp, { recursive: true, force: true });
116
+ b.audit._resetForTest();
117
+ b.db._resetForTest();
118
+ b.vault._resetForTest();
119
+ b.cluster._resetForTest();
120
+ }
121
+ console.log("OK — db raw-path residency gate tests");
122
+ }
123
+
124
+ module.exports = { run: run };
125
+ if (require.main === module) {
126
+ run().then(function () { process.exit(0); })
127
+ .catch(function (err) { process.exitCode = 1; throw err; });
128
+ }
@@ -1,13 +1,16 @@
1
1
  "use strict";
2
2
  /**
3
- * dbSchema.reconcile / reconcileTable — opt-in schema-drift detection.
3
+ * dbSchema.reconcile / reconcileTable — schema-drift detection.
4
4
  *
5
5
  * reconcile is additive-only (CREATE IF NOT EXISTS + ALTER ADD COLUMN);
6
6
  * it never drops columns. The onDrift opt adds detection of config-vs-
7
7
  * live divergence without changing that non-destructive contract:
8
8
  *
9
- * - default (onDrift unset / "ignore") = pre-detection behavior; a
10
- * live table with an extra column is tolerated silently.
9
+ * - onDrift unset = posture-driven default (v0.15.0): "ignore" on an
10
+ * unpinned / non-regulated deployment (back-compat); "refuse" when a
11
+ * regulated compliance posture is globally pinned.
12
+ * - "ignore" = tolerate a live table with an extra column silently
13
+ * (the explicit opt-out under a regulated posture).
11
14
  * - "warn" = detect, never throw (report returned to the caller).
12
15
  * - "refuse" = throw at boot on the first drifted table (strict-schema
13
16
  * posture).
@@ -25,9 +28,10 @@ var os = helpers.os;
25
28
  var path = helpers.path;
26
29
  var sqlite = require("node:sqlite");
27
30
  var dbSchema = require("../../lib/db-schema");
31
+ var compliance = require("../../lib/compliance");
28
32
 
29
- function _openDb(tmpDir) {
30
- return new sqlite.DatabaseSync(path.join(tmpDir, "drift.db"));
33
+ function _openDb(tmpDir, name) {
34
+ return new sqlite.DatabaseSync(path.join(tmpDir, name || "drift.db"));
31
35
  }
32
36
 
33
37
  function _liveColumns(db, table) {
@@ -134,6 +138,35 @@ async function run() {
134
138
  /onDrift must be one of/));
135
139
  dbBad.close();
136
140
 
141
+ // ---- posture-driven default (v0.15.0): a regulated pinned posture
142
+ // flips the unset-onDrift default from "ignore" to "refuse"; an explicit
143
+ // onDrift always wins (including "ignore" to opt back out). ----
144
+ compliance._resetForTest();
145
+ try {
146
+ // Build a freshly-reconciled DB, then introduce out-of-band drift —
147
+ // all while UNPINNED so the clean setup never trips the default.
148
+ var dbPosture = _openDb(tmp, "drift-posture.db");
149
+ dbSchema.reconcile(dbPosture, SCHEMA);
150
+ dbSchema.runSql(dbPosture, 'ALTER TABLE "widgets" ADD COLUMN "posture_extra" TEXT');
151
+
152
+ // Unpinned: unset onDrift stays "ignore" — drift tolerated silently.
153
+ check("unpinned + unset onDrift tolerates drift (ignore default)",
154
+ threwMatching(function () { dbSchema.reconcile(dbPosture, SCHEMA); }, /.*/) === null);
155
+
156
+ // Pin a regulated posture: unset onDrift now refuses on the SAME drift.
157
+ compliance.set("gdpr");
158
+ check("regulated posture pinned + unset onDrift refuses drift (refuse default)",
159
+ !!threwMatching(function () { dbSchema.reconcile(dbPosture, SCHEMA); },
160
+ /schema drift on table 'widgets'/));
161
+ // Explicit opt-out: onDrift "ignore" tolerates drift even under the
162
+ // regulated posture (the documented escape hatch).
163
+ check("explicit onDrift:'ignore' opts out under a regulated posture",
164
+ threwMatching(function () { dbSchema.reconcile(dbPosture, SCHEMA, { onDrift: "ignore" }); }, /.*/) === null);
165
+ dbPosture.close();
166
+ } finally {
167
+ compliance._resetForTest();
168
+ }
169
+
137
170
  fs.rmSync(tmp, { recursive: true, force: true });
138
171
  console.log("OK — db schema-drift detection tests");
139
172
  }
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ /**
3
+ * dbSchema.reconcileTable verbatim column-TYPE string — statement-emittable
4
+ * gate parity with b.sql.createTable. The TYPE string is concatenated
5
+ * verbatim into CREATE TABLE (db-schema.js:264) + ALTER TABLE ADD COLUMN
6
+ * (db-schema.js:271); neither concat routes through the catalog emittable
7
+ * gate b.sql.createTable got in v0.15.3 (#105). RED today: reconcileTable
8
+ * does NOT throw and the stacked DROP destroys the sentinel table.
9
+ */
10
+
11
+ var helpers = require("../helpers");
12
+ var check = helpers.check;
13
+ var fs = helpers.fs;
14
+ var os = helpers.os;
15
+ var path = helpers.path;
16
+ var sqlite = require("node:sqlite");
17
+ var dbSchema = require("../../lib/db-schema");
18
+
19
+ // Real NUL built from a code point — never a literal NUL typed into source.
20
+ var NUL = String.fromCharCode(0);
21
+
22
+ function _openDb(tmpDir, name) {
23
+ return new sqlite.DatabaseSync(path.join(tmpDir, name || "emit.db"));
24
+ }
25
+
26
+ function threwMatching(fn, pattern) {
27
+ try { fn(); } catch (e) { return pattern.test(e.message || "") ? e : null; }
28
+ return null;
29
+ }
30
+
31
+ function _sentinelAlive(db) {
32
+ return db.prepare(
33
+ "SELECT count(*) AS n FROM sqlite_master WHERE type='table' AND name='secrets'"
34
+ ).get().n === 1;
35
+ }
36
+
37
+ function _seedSentinel(db) {
38
+ dbSchema.runSql(db, "DROP TABLE IF EXISTS secrets");
39
+ dbSchema.runSql(db, "CREATE TABLE secrets (k TEXT)");
40
+ dbSchema.runSql(db, "INSERT INTO secrets VALUES ('topsecret')");
41
+ }
42
+
43
+ async function run() {
44
+ var tmp = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-reconcile-emit-"));
45
+
46
+ // ---- CREATE TABLE path: hostile TYPE stacks a DROP ----
47
+ var dbCreate = _openDb(tmp, "create.db");
48
+ _seedSentinel(dbCreate);
49
+ check("setup: sentinel 'secrets' table exists before reconcile",
50
+ _sentinelAlive(dbCreate));
51
+
52
+ var createErr = threwMatching(function () {
53
+ dbSchema.reconcileTable(dbCreate, {
54
+ name: "t_create",
55
+ columns: { id: "TEXT PRIMARY KEY", evil: "TEXT); DROP TABLE secrets; --" },
56
+ }, { onDrift: "ignore" });
57
+ }, /.*/);
58
+ // RED today: createErr === null (no throw). GREEN: the catalog gate refuses.
59
+ check("reconcileTable refuses a CREATE-path verbatim type that stacks a statement",
60
+ !!createErr);
61
+ // Load-bearing RED proof: the stacked DROP must NOT have executed.
62
+ check("reconcileTable did not let the stacked DROP destroy the sentinel table (CREATE path)",
63
+ _sentinelAlive(dbCreate));
64
+ dbCreate.close();
65
+
66
+ // ---- ADD COLUMN path: reconcile a table, then re-reconcile with a
67
+ // hostile new-column TYPE so the ALTER TABLE ADD COLUMN concat fires. ----
68
+ var dbAlter = _openDb(tmp, "alter.db");
69
+ _seedSentinel(dbAlter);
70
+ dbSchema.reconcileTable(dbAlter, {
71
+ name: "t_alter", columns: { id: "TEXT PRIMARY KEY" },
72
+ }, { onDrift: "ignore" });
73
+
74
+ var alterErr = threwMatching(function () {
75
+ dbSchema.reconcileTable(dbAlter, {
76
+ name: "t_alter",
77
+ columns: { id: "TEXT PRIMARY KEY", evil2: "TEXT); DROP TABLE secrets; --" },
78
+ }, { onDrift: "ignore" });
79
+ }, /.*/);
80
+ check("reconcileTable refuses an ADD-COLUMN verbatim type that stacks a statement",
81
+ !!alterErr);
82
+ check("reconcileTable did not let the stacked DROP destroy the sentinel table (ADD COLUMN path)",
83
+ _sentinelAlive(dbAlter));
84
+ dbAlter.close();
85
+
86
+ // ---- unbalanced-quote TYPE ----
87
+ var dbQuote = _openDb(tmp, "quote.db");
88
+ _seedSentinel(dbQuote);
89
+ check("reconcileTable refuses a verbatim type with an unbalanced quote",
90
+ !!threwMatching(function () {
91
+ dbSchema.reconcileTable(dbQuote, {
92
+ name: "t_quote", columns: { id: "TEXT PRIMARY KEY", c: "TEXT'" },
93
+ }, { onDrift: "ignore" });
94
+ }, /.*/));
95
+ dbQuote.close();
96
+
97
+ // ---- NUL byte in the TYPE string (built via String.fromCharCode(0)) ----
98
+ var dbNul = _openDb(tmp, "nul.db");
99
+ _seedSentinel(dbNul);
100
+ check("reconcileTable refuses a verbatim type carrying a NUL byte",
101
+ !!threwMatching(function () {
102
+ dbSchema.reconcileTable(dbNul, {
103
+ name: "t_nul", columns: { id: "TEXT PRIMARY KEY", c: "TEXT" + NUL + " DEFAULT x" },
104
+ }, { onDrift: "ignore" });
105
+ }, /.*/));
106
+ dbNul.close();
107
+
108
+ // ---- a legitimate verbatim type still reconciles (no over-rejection) ----
109
+ var dbOk = _openDb(tmp, "ok.db");
110
+ check("reconcileTable still allows a legitimate multi-word verbatim type",
111
+ threwMatching(function () {
112
+ dbSchema.reconcileTable(dbOk, {
113
+ name: "t_ok",
114
+ columns: { id: "TEXT PRIMARY KEY", n: "INTEGER NOT NULL DEFAULT 0", v: "VARCHAR(255)" },
115
+ }, { onDrift: "ignore" });
116
+ }, /.*/) === null);
117
+ dbOk.close();
118
+
119
+ fs.rmSync(tmp, { recursive: true, force: true });
120
+ console.log("OK — db-schema reconcileTable emittable-gate tests");
121
+ }
122
+
123
+ module.exports = { run: run };
124
+ if (require.main === module) {
125
+ run().then(function () { process.exit(0); })
126
+ .catch(function (err) { process.exitCode = 1; throw err; });
127
+ }