@blamejs/blamejs-shop 0.4.31 → 0.4.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -1
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/vendor/MANIFEST.json +400 -282
  5. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  6. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  7. package/lib/vendor/blamejs/.gitignore +6 -0
  8. package/lib/vendor/blamejs/CHANGELOG.md +28 -0
  9. package/lib/vendor/blamejs/MIGRATING.md +55 -0
  10. package/lib/vendor/blamejs/README.md +8 -6
  11. package/lib/vendor/blamejs/SECURITY.md +19 -3
  12. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  13. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  14. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  15. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  16. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  17. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  18. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  19. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  20. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  21. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  22. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  23. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  24. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  25. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  26. package/lib/vendor/blamejs/index.js +4 -0
  27. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  28. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  29. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  30. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  31. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  32. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  33. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  34. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  35. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  36. package/lib/vendor/blamejs/lib/audit.js +259 -123
  37. package/lib/vendor/blamejs/lib/auth/elevation-grant.js +6 -2
  38. package/lib/vendor/blamejs/lib/auth/oauth.js +66 -9
  39. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  40. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  41. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +36 -7
  42. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  43. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  44. package/lib/vendor/blamejs/lib/cache.js +174 -105
  45. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  46. package/lib/vendor/blamejs/lib/cli.js +19 -14
  47. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  48. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  49. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  50. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  51. package/lib/vendor/blamejs/lib/compliance.js +210 -4
  52. package/lib/vendor/blamejs/lib/consent.js +82 -29
  53. package/lib/vendor/blamejs/lib/constants.js +27 -11
  54. package/lib/vendor/blamejs/lib/credential-hash.js +9 -0
  55. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  56. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  57. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  58. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  59. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  60. package/lib/vendor/blamejs/lib/db.js +249 -99
  61. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  62. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  63. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  64. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  65. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  66. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  67. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  68. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  69. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  70. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  71. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  72. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  73. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  74. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  75. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  76. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  77. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  78. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  79. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  80. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  81. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  82. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  83. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  84. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  85. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  86. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  87. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  88. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  89. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  90. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  91. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  92. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  93. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  94. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  95. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  96. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  97. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  98. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  99. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  100. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  101. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  102. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  103. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  104. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  105. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  106. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  107. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  108. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  109. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  110. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  111. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  112. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  113. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  114. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  115. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  116. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  117. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  118. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  119. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  120. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  121. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  122. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  123. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  124. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  125. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  126. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  127. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  128. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  129. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  131. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  132. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  133. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  134. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  135. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  136. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  137. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  138. package/lib/vendor/blamejs/lib/mail.js +8 -4
  139. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  140. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  141. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  142. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  143. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  144. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  145. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  146. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  147. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  148. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  149. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  150. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  151. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  152. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  153. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  154. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  155. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  156. package/lib/vendor/blamejs/lib/observability.js +124 -0
  157. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  158. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  159. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  160. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  161. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  162. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  163. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  164. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  165. package/lib/vendor/blamejs/lib/queue.js +7 -0
  166. package/lib/vendor/blamejs/lib/redact.js +68 -11
  167. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  168. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  169. package/lib/vendor/blamejs/lib/retention.js +117 -42
  170. package/lib/vendor/blamejs/lib/router.js +212 -5
  171. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  172. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  173. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  174. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  175. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  176. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  177. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  178. package/lib/vendor/blamejs/lib/scheduler.js +47 -12
  179. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  180. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  181. package/lib/vendor/blamejs/lib/session.js +175 -77
  182. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  183. package/lib/vendor/blamejs/lib/sse.js +26 -0
  184. package/lib/vendor/blamejs/lib/ssrf-guard.js +169 -4
  185. package/lib/vendor/blamejs/lib/static.js +177 -34
  186. package/lib/vendor/blamejs/lib/subject.js +96 -49
  187. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  188. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  189. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  190. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  191. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  192. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  193. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  194. package/lib/vendor/blamejs/package.json +2 -2
  195. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  200. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  201. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  202. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  203. package/lib/vendor/blamejs/release-notes/v0.15.7.json +43 -0
  204. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  205. package/lib/vendor/blamejs/scripts/gen-migrating.js +67 -0
  206. package/lib/vendor/blamejs/scripts/release.js +398 -38
  207. package/lib/vendor/blamejs/test/00-primitives.js +168 -0
  208. package/lib/vendor/blamejs/test/10-state.js +140 -14
  209. package/lib/vendor/blamejs/test/20-db.js +65 -2
  210. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  211. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  212. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  213. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  214. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  215. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  216. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  217. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  220. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  221. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  222. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  223. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  224. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  225. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  226. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  227. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  228. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  229. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  230. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  231. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  232. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  233. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  234. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  235. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  236. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  237. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  238. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  239. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  240. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  242. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  248. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  250. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  251. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  252. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1196 -14
  253. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +18 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  256. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  259. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  267. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  271. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  272. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  273. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  275. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  276. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  277. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  278. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  282. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  283. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  285. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  286. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  287. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  288. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  296. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/retention-floor.test.js +59 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +362 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-watchdog-stale-settle.test.js +71 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  308. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  309. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  310. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  312. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  313. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  314. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  315. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  316. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  317. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  318. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  319. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  320. package/lib/vendor/blamejs/test/smoke.js +79 -21
  321. package/package.json +2 -2
  322. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  323. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  324. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  325. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  326. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  327. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  328. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  329. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  330. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  331. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  332. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  333. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  334. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  335. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  336. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  337. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  338. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  339. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  340. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  341. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  342. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  343. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -12,6 +12,13 @@ var helpers = require("../helpers");
12
12
  var b = helpers.b;
13
13
  var check = helpers.check;
14
14
  var C = b.constants;
15
+ var fs = helpers.fs;
16
+ var os = helpers.os;
17
+ var path = helpers.path;
18
+ var setupTestDb = helpers.setupTestDb;
19
+ var teardownTestDb = helpers.teardownTestDb;
20
+
21
+ function _tmp() { return fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-dsr-")); }
15
22
 
16
23
  function _makeDsr(extraOpts) {
17
24
  var sources = [
@@ -759,6 +766,209 @@ async function testReceiptSignerError() {
759
766
  receipt.signature === undefined);
760
767
  }
761
768
 
769
+ // ---- dbTicketStore: at-rest sealing + erasure purge + upgrade path ----
770
+
771
+ function _dbDsr(extraOpts) {
772
+ var store = b.dsr.dbTicketStore({ db: b.db });
773
+ return {
774
+ store: store,
775
+ dsr: b.dsr.create(Object.assign({
776
+ ticketStore: store,
777
+ posture: "gdpr",
778
+ identityResolver: async function (input) {
779
+ if (input.email === "alice@example.com") {
780
+ return { subjectId: "u-1", email: "alice@example.com", phone: "+15550001111" };
781
+ }
782
+ return null;
783
+ },
784
+ sources: [{
785
+ name: "users",
786
+ query: async function () { return [{ id: 1 }]; },
787
+ erase: async function () { return { deletedIds: [1] }; },
788
+ }],
789
+ }, extraOpts || {})),
790
+ };
791
+ }
792
+
793
+ async function testDbStoreSealsAtRest() {
794
+ var tmpDir = _tmp();
795
+ await setupTestDb(tmpDir);
796
+ try {
797
+ var h = _dbDsr();
798
+ var ticket = await h.dsr.submit({
799
+ type: "access",
800
+ subject: { email: "alice@example.com" },
801
+ reason: "sealing-at-rest verification",
802
+ });
803
+ // Read the RAW row directly (bypassing the store's unseal) and assert
804
+ // the PII columns + payload are NOT stored in plaintext.
805
+ var raw = b.db.prepare(
806
+ "SELECT subject_email, subject_phone, subject_id, subject_email_hash, payload " +
807
+ "FROM dsr_tickets WHERE id = $id").all({ $id: ticket.id })[0];
808
+ check("dbStore: raw row exists", !!raw);
809
+ check("dbStore: subject_email sealed at rest (not plaintext)",
810
+ typeof raw.subject_email === "string" &&
811
+ raw.subject_email.indexOf("alice@example.com") === -1);
812
+ check("dbStore: subject_phone sealed at rest (not plaintext)",
813
+ typeof raw.subject_phone === "string" &&
814
+ raw.subject_phone.indexOf("+15550001111") === -1);
815
+ check("dbStore: payload sealed at rest (no plaintext email leak)",
816
+ typeof raw.payload === "string" &&
817
+ raw.payload.indexOf("alice@example.com") === -1);
818
+ check("dbStore: derived email hash populated for lookup",
819
+ typeof raw.subject_email_hash === "string" && raw.subject_email_hash.length > 0);
820
+
821
+ // The store still round-trips the cleartext ticket via get().
822
+ var got = await h.store.get(ticket.id);
823
+ check("dbStore: get() unseals payload back to cleartext",
824
+ got && got.subject.email === "alice@example.com");
825
+
826
+ // list-by-subject matches via the derived hash (sealed columns can't
827
+ // be matched on plaintext).
828
+ var listed = await h.store.list({ subject: { email: "alice@example.com" } });
829
+ check("dbStore: list-by-subject matches via derived hash",
830
+ listed.length === 1 && listed[0].id === ticket.id);
831
+ } finally {
832
+ await teardownTestDb(tmpDir);
833
+ }
834
+ }
835
+
836
+ async function testDbStoreErasurePurgesPriorTickets() {
837
+ var tmpDir = _tmp();
838
+ await setupTestDb(tmpDir);
839
+ try {
840
+ var h = _dbDsr();
841
+ // Two prior access tickets for alice + a final erasure.
842
+ var t1 = await h.dsr.submit({ type: "access", subject: { email: "alice@example.com" }, reason: "prior access one" });
843
+ var t2 = await h.dsr.submit({ type: "access", subject: { email: "alice@example.com" }, reason: "prior access two" });
844
+ var erasure = await h.dsr.submit({
845
+ type: "erasure", subject: { email: "alice@example.com" },
846
+ reason: "right to erasure", verificationLevel: "secondary",
847
+ });
848
+ var before = await h.store.list({ subject: { email: "alice@example.com" } });
849
+ check("dbStore erasure: 3 tickets before completion", before.length === 3);
850
+
851
+ var processed = await h.dsr.process(erasure.id, { actor: "compliance@", verificationLevel: "secondary" });
852
+ check("dbStore erasure: erasure completed", processed.status === "completed");
853
+
854
+ // After completion, the subject's prior tickets are purged; the
855
+ // erasure ticket itself survives (audit/receipt trail).
856
+ var after = await h.store.list({ subject: { email: "alice@example.com" } });
857
+ check("dbStore erasure: only the erasure ticket remains", after.length === 1 && after[0].id === erasure.id);
858
+ check("dbStore erasure: prior ticket t1 gone", (await h.store.get(t1.id)) === null);
859
+ check("dbStore erasure: prior ticket t2 gone", (await h.store.get(t2.id)) === null);
860
+ } finally {
861
+ await teardownTestDb(tmpDir);
862
+ }
863
+ }
864
+
865
+ async function testDbStoreUpgradePath() {
866
+ // Build an OLD-shape (v0.8.0) dsr_tickets table by hand — no
867
+ // subject_*_hash columns — then construct the store. ensureSchema must
868
+ // ALTER TABLE ADD COLUMN the missing columns so the first insert
869
+ // succeeds rather than throwing "no such column".
870
+ var tmpDir = _tmp();
871
+ await setupTestDb(tmpDir);
872
+ try {
873
+ b.db.runSql("CREATE TABLE dsr_tickets (" +
874
+ "id TEXT PRIMARY KEY, type TEXT NOT NULL, status TEXT NOT NULL, " +
875
+ "subject_id TEXT, subject_email TEXT, subject_phone TEXT, " +
876
+ "submitted_at INTEGER NOT NULL, deadline_at INTEGER NOT NULL, " +
877
+ "processed_at INTEGER, verification_level TEXT, posture TEXT, " +
878
+ "payload TEXT NOT NULL)");
879
+ // Seed a legacy plaintext row to prove the ALTER survives existing data.
880
+ b.db.prepare("INSERT INTO dsr_tickets (id, type, status, subject_email, submitted_at, deadline_at, payload) " +
881
+ "VALUES ($id, $type, $status, $email, $sa, $da, $p)").run({
882
+ $id: "DSR-LEGACY-1", $type: "access", $status: "pending",
883
+ $email: "legacy@example.com", $sa: Date.now(), $da: Date.now() + 1000,
884
+ $p: JSON.stringify({ id: "DSR-LEGACY-1", status: "pending", subject: { email: "legacy@example.com" } }),
885
+ });
886
+
887
+ // Constructing the store runs ensureSchema → reconciles the columns.
888
+ var h = _dbDsr();
889
+ var cols = b.db.prepare("PRAGMA table_info(dsr_tickets)").all({});
890
+ var names = cols.map(function (c) { return c.name; });
891
+ check("dbStore upgrade: subject_email_hash column added",
892
+ names.indexOf("subject_email_hash") !== -1);
893
+ check("dbStore upgrade: subject_id_hash column added",
894
+ names.indexOf("subject_id_hash") !== -1);
895
+
896
+ // A fresh insert against the upgraded table succeeds (the bug under
897
+ // test threw "no such column: subject_email_hash" here).
898
+ var ticket = await h.dsr.submit({
899
+ type: "access", subject: { email: "alice@example.com" }, reason: "post-upgrade insert",
900
+ });
901
+ check("dbStore upgrade: insert succeeds after schema reconcile",
902
+ typeof ticket.id === "string");
903
+ var got = await h.store.get(ticket.id);
904
+ check("dbStore upgrade: round-trips the new ticket", got && got.subject.email === "alice@example.com");
905
+
906
+ // The legacy row was seeded with a plaintext subject and NULL hash. Once a
907
+ // vault is present, list({ subject }) matches on the hash column, so the
908
+ // upgrade MUST have backfilled the legacy row's hash — otherwise it is
909
+ // invisible to a subject lookup and the erasure-completion purge skips it.
910
+ var legacyFound = await h.store.list({ subject: { email: "legacy@example.com" } });
911
+ check("dbStore upgrade: legacy plaintext row backfilled + found by subject",
912
+ legacyFound.some(function (t) { return t.id === "DSR-LEGACY-1"; }));
913
+ var rawLegacy = b.db.prepare(
914
+ "SELECT subject_email, subject_email_hash FROM dsr_tickets WHERE id = $id")
915
+ .all({ $id: "DSR-LEGACY-1" })[0];
916
+ check("dbStore upgrade: legacy subject_email_hash populated by backfill",
917
+ rawLegacy && typeof rawLegacy.subject_email_hash === "string" &&
918
+ rawLegacy.subject_email_hash.length > 0);
919
+ check("dbStore upgrade: legacy subject_email sealed at rest by backfill (now erasable)",
920
+ rawLegacy && rawLegacy.subject_email !== "legacy@example.com");
921
+ } finally {
922
+ await teardownTestDb(tmpDir);
923
+ }
924
+ }
925
+
926
+ // ---- AAD_ROTATION descriptor + reseal ----
927
+
928
+ function testAadRotationDescriptor() {
929
+ var d = b.dsr.AAD_ROTATION;
930
+ check("AAD_ROTATION: exported", d && typeof d === "object");
931
+ check("AAD_ROTATION: table is dsr_tickets", d.table === "dsr_tickets");
932
+ check("AAD_ROTATION: rowIdField is id", d.rowIdField === "id");
933
+ check("AAD_ROTATION: backend external", d.backend === "external");
934
+ check("AAD_ROTATION: reseal is the fn", d.reseal === b.dsr.reseal && typeof d.reseal === "function");
935
+ }
936
+
937
+ async function testResealValidationAndStore() {
938
+ var tmpDir = _tmp();
939
+ await setupTestDb(tmpDir);
940
+ try {
941
+ // Missing roots are rejected at entry.
942
+ var threw = null;
943
+ try { await b.dsr.reseal({ store: { listAll: function () { return []; }, putResealed: function () {} } }); }
944
+ catch (e) { threw = e; }
945
+ check("reseal: missing root snapshots refused",
946
+ threw && /dsr\/bad-root/.test(threw.code));
947
+
948
+ // Bad store shape is rejected.
949
+ var threw2 = null;
950
+ try { await b.dsr.reseal({ oldRootJson: "{}", newRootJson: "{}", store: {} }); }
951
+ catch (e) { threw2 = e; }
952
+ check("reseal: store missing listAll/putResealed refused",
953
+ threw2 && /dsr\/bad-reseal-store/.test(threw2.code));
954
+
955
+ // A store whose rows carry no AAD-sealed cells re-seals nothing
956
+ // (plaintext rows pass through) — exercises the row-walk + putResealed
957
+ // contract without needing a full keypair rotation.
958
+ var keys = b.vault.getKeysJson();
959
+ var puts = [];
960
+ var plainStore = {
961
+ listAll: function () { return [{ id: "T1", payload: "plain-json" }]; },
962
+ putResealed: function (row) { puts.push(row.id); },
963
+ };
964
+ var rv = await b.dsr.reseal({ oldRootJson: keys, newRootJson: keys, store: plainStore });
965
+ check("reseal: returns table + resealed count", rv.table === "dsr_tickets" && rv.resealed === 0);
966
+ check("reseal: plaintext rows not re-persisted", puts.length === 0);
967
+ } finally {
968
+ await teardownTestDb(tmpDir);
969
+ }
970
+ }
971
+
762
972
  // ---- Run all ----
763
973
 
764
974
  (async function run() {
@@ -812,4 +1022,12 @@ async function testReceiptSignerError() {
812
1022
  await testReceiptNotTerminal();
813
1023
  await testReceiptWithSigner();
814
1024
  await testReceiptSignerError();
1025
+
1026
+ // dbTicketStore at-rest sealing + erasure purge + upgrade path
1027
+ await testDbStoreSealsAtRest();
1028
+ await testDbStoreErasurePurgesPriorTickets();
1029
+ await testDbStoreUpgradePath();
1030
+ // AAD_ROTATION descriptor + reseal
1031
+ testAadRotationDescriptor();
1032
+ await testResealValidationAndStore();
815
1033
  })().catch(function (e) { console.error(e); process.exit(1); });
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ /**
3
+ * b.cryptoField.eraseRow posture-driven VACUUM cascade.
4
+ *
5
+ * The advertised guarantee (crypto-field.js eraseRow docstring +
6
+ * compliance.js POSTURE_DEFAULTS): under a regulatory posture whose
7
+ * requireVacuumAfterErase floor is true, calling eraseRow on a sealed-
8
+ * column table automatically runs b.db.vacuumAfterErase({ mode:"full" })
9
+ * so SQLite's freed B-tree pages don't keep sealed-column ciphertext
10
+ * recoverable from a forensic disk image — the residual that would
11
+ * defeat the right-to-erasure the regime guarantees.
12
+ *
13
+ * This drives that guarantee end-to-end on a REAL encrypted-at-rest DB
14
+ * (wrapped vault, sealed db.enc, signed audit chain — the production
15
+ * path via setupTestDb). The vacuum entry point is wrapped with a
16
+ * call-through spy: the real VACUUM still executes against the real
17
+ * tmpfs SQLite, AND the framework's own `db.vacuum_after_erase` row is
18
+ * read back out of the signed audit chain as the observable side
19
+ * effect.
20
+ *
21
+ * gdpr / hipaa MUST trigger the vacuum (positive control).
22
+ *
23
+ * uk-gdpr / appi-jp / pdpa-sg are all in
24
+ * b.compliance.CROSS_BORDER_REGULATED_POSTURES — the same cross-border
25
+ * privacy regimes whose Art. 17-equivalent right-to-erasure applies
26
+ * identically (uk-gdpr is literally "retained EU GDPR"), so they MUST
27
+ * trigger the same mandatory residue VACUUM. An invariant below asserts
28
+ * that EVERY cross-border regulated posture carries the
29
+ * requireVacuumAfterErase floor, so a newly-added regime can never
30
+ * silently drop the residue cleanup the right-to-erasure depends on.
31
+ */
32
+
33
+ var fs = require("node:fs");
34
+ var os = require("node:os");
35
+ var path = require("node:path");
36
+
37
+ var helpers = require("../helpers");
38
+ var b = helpers.b;
39
+ var check = helpers.check;
40
+ var setupTestDb = require("../helpers/db").setupTestDb;
41
+ var teardownTestDb = require("../helpers/db").teardownTestDb;
42
+
43
+ var SEALED_SCHEMA = [
44
+ {
45
+ name: "patients",
46
+ columns: {
47
+ _id: "TEXT PRIMARY KEY",
48
+ ssn: "TEXT",
49
+ ssnHash: "TEXT",
50
+ name: "TEXT",
51
+ },
52
+ indexes: ["ssnHash"],
53
+ sealedFields: ["ssn", "name"],
54
+ derivedHashes: { ssnHash: { from: "ssn" } },
55
+ // AAD-bound envelope so the table satisfies the HIPAA
56
+ // sealEnvelopeFloor:"aad" gate as well as the no-floor postures.
57
+ aad: true,
58
+ rowIdField: "_id",
59
+ },
60
+ ];
61
+
62
+ // Drive the full erase under one posture against a real encrypted DB
63
+ // and report whether the posture-cascade actually fired the VACUUM.
64
+ // Returns { vacuumCalled, modeFull, auditRowSeen }.
65
+ async function eraseUnderPosture(posture) {
66
+ var dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-erasevac-"));
67
+ await setupTestDb(dataDir, SEALED_SCHEMA);
68
+
69
+ // Pin the posture. The cascade calls cryptoField.applyPosture(posture)
70
+ // (records _activePosture) + db.applyPosture(posture). This is the
71
+ // exact boot wiring an operator running under `posture` gets.
72
+ b.compliance._resetForTest();
73
+ b.compliance.set(posture);
74
+ check("compliance.current cascaded -> " + posture,
75
+ b.compliance.current() === posture);
76
+ check("cryptoField active posture cascaded -> " + posture,
77
+ b.cryptoField.getActivePosture() === posture);
78
+
79
+ // The sealed table is already in the field-crypto registry —
80
+ // setupTestDb's db.init registered it from the schema (sealedFields /
81
+ // derivedHashes / aad) — so eraseRow has a schema to tombstone against.
82
+
83
+ // Persist a real row through the real field-crypto + db path
84
+ // (insertOne seals the sealed columns itself), then delete it so the
85
+ // upcoming erase + vacuum has freed B-tree pages to reclaim. The
86
+ // residue the VACUUM clears is exactly those freed pages — they still
87
+ // hold the sealed-column ciphertext until a full rewrite.
88
+ var inserted = b.db.from("patients").insertOne({
89
+ ssn: "123-45-6789",
90
+ name: "Alice Patient",
91
+ });
92
+ b.db.from("patients").where({ _id: inserted._id }).deleteOne();
93
+
94
+ // A sealed-shaped in-memory row for eraseRow to tombstone (eraseRow
95
+ // operates on a row object, NULLing its sealed + derived columns).
96
+ var sealed = b.cryptoField.sealRow("patients", {
97
+ _id: inserted._id,
98
+ ssn: "123-45-6789",
99
+ name: "Alice Patient",
100
+ });
101
+
102
+ // Wrap the real vacuum entry point with a call-through spy. eraseRow
103
+ // reaches it via require("./db").vacuumAfterErase, which is the same
104
+ // object as b.db — so the wrapper is what eraseRow invokes, and the
105
+ // real VACUUM still runs against the encrypted tmpfs SQLite.
106
+ var realVacuum = b.db.vacuumAfterErase;
107
+ var spy = { called: false, mode: null, threw: null };
108
+ b.db.vacuumAfterErase = function (opts) {
109
+ spy.called = true;
110
+ spy.mode = opts && opts.mode;
111
+ try {
112
+ return realVacuum.call(b.db, opts); // real VACUUM executes
113
+ } catch (e) {
114
+ spy.threw = e;
115
+ throw e;
116
+ }
117
+ };
118
+
119
+ var auditRowSeen = false;
120
+ try {
121
+ var sinceMs = Date.now();
122
+ // The advertised cascade: erasing a sealed row under a vacuum-floor
123
+ // posture auto-runs b.db.vacuumAfterErase({ mode:"full" }).
124
+ var erased = b.cryptoField.eraseRow("patients", sealed);
125
+ check("eraseRow NULLed the sealed ssn (" + posture + ")", erased.ssn === null);
126
+ check("eraseRow NULLed the derived ssnHash (" + posture + ")", erased.ssnHash === null);
127
+
128
+ // Real side effect: the framework's own audit row for the vacuum
129
+ // must be present in the signed chain when (and only when) the
130
+ // posture actually triggered it.
131
+ await b.audit.flush();
132
+ var rows = await b.audit.query({
133
+ action: "db.vacuum_after_erase",
134
+ from: sinceMs - 1000,
135
+ limit: 50,
136
+ });
137
+ // The audit chain stores metadata as a JSON string column; parse
138
+ // it back to confirm the vacuum row carried mode:"full".
139
+ auditRowSeen = Array.isArray(rows) && rows.some(function (r) {
140
+ if (!r || r.metadata == null) return false;
141
+ var md = r.metadata;
142
+ if (typeof md === "string") { try { md = JSON.parse(md); } catch (_e) { return false; } }
143
+ return md && md.mode === "full";
144
+ });
145
+ } finally {
146
+ b.db.vacuumAfterErase = realVacuum;
147
+ await teardownTestDb(dataDir);
148
+ b.compliance._resetForTest();
149
+ }
150
+
151
+ return {
152
+ vacuumCalled: spy.called,
153
+ modeFull: spy.mode === "full",
154
+ vacuumThrew: spy.threw,
155
+ auditRowSeen: auditRowSeen,
156
+ };
157
+ }
158
+
159
+ async function run() {
160
+ // ---- Positive control: gdpr + hipaa MUST trigger the full VACUUM,
161
+ // the real VACUUM must execute, and the audit row must land. This is
162
+ // the genuine end-to-end proof of the advertised guarantee.
163
+ var gdpr = await eraseUnderPosture("gdpr");
164
+ check("gdpr eraseRow TRIGGERED vacuumAfterErase", gdpr.vacuumCalled === true);
165
+ check("gdpr vacuum ran with mode:'full'", gdpr.modeFull === true);
166
+ check("gdpr real VACUUM executed without throwing", gdpr.vacuumThrew === null);
167
+ check("gdpr db.vacuum_after_erase landed in the signed audit chain",
168
+ gdpr.auditRowSeen === true);
169
+
170
+ var hipaa = await eraseUnderPosture("hipaa");
171
+ check("hipaa eraseRow TRIGGERED vacuumAfterErase", hipaa.vacuumCalled === true);
172
+ check("hipaa vacuum ran with mode:'full'", hipaa.modeFull === true);
173
+ check("hipaa real VACUUM executed without throwing", hipaa.vacuumThrew === null);
174
+ check("hipaa db.vacuum_after_erase landed in the signed audit chain",
175
+ hipaa.auditRowSeen === true);
176
+
177
+ // ---- Invariant (recurrence guard): EVERY cross-border regulated posture
178
+ // must carry the requireVacuumAfterErase floor. A regime added to
179
+ // CROSS_BORDER_REGULATED_POSTURES without a POSTURE_DEFAULTS entry would
180
+ // silently skip the mandatory residue cleanup — this catches that drift.
181
+ var crossBorder = b.compliance.CROSS_BORDER_REGULATED_POSTURES.slice();
182
+ check("there is at least one cross-border regulated posture to check",
183
+ crossBorder.length > 0);
184
+ for (var k = 0; k < crossBorder.length; k += 1) {
185
+ check(crossBorder[k] + " carries the requireVacuumAfterErase floor",
186
+ b.compliance.postureDefault(crossBorder[k], "requireVacuumAfterErase") === true);
187
+ }
188
+
189
+ // ---- End-to-end: erasing under each cross-border posture triggers the
190
+ // same mandatory residue VACUUM the positive controls (gdpr/hipaa) do.
191
+ var sample = ["uk-gdpr", "appi-jp", "pdpa-sg"];
192
+ for (var i = 0; i < sample.length; i += 1) {
193
+ var p = sample[i];
194
+ var r = await eraseUnderPosture(p);
195
+ check(p + " eraseRow TRIGGERED the mandatory residue VACUUM",
196
+ r.vacuumCalled === true);
197
+ check(p + " vacuum ran with mode:'full'", r.modeFull === true);
198
+ check(p + " db.vacuum_after_erase landed in the signed audit chain",
199
+ r.auditRowSeen === true);
200
+ }
201
+ }
202
+
203
+ module.exports = { run: run };
204
+
205
+ if (require.main === module) {
206
+ run().then(
207
+ function () { console.log("[erase-posture-vacuum] OK"); },
208
+ function (e) { console.error(e.stack || e); process.exit(1); }
209
+ );
210
+ }
@@ -26,7 +26,10 @@ function _instrumentingDriver(opts) {
26
26
  opts.failOnce = null;
27
27
  throw e;
28
28
  }
29
- if (/^SELECT rolname FROM pg_roles/i.test(sql)) {
29
+ // assertRoleHardening composes the pg_roles scan through b.sql, which
30
+ // quotes the projected identifier (`SELECT "rolname" FROM pg_roles
31
+ // ORDER BY "rolname" ASC`). Match the quoted-or-bare form.
32
+ if (/^SELECT\s+"?rolname"?\s+FROM\s+pg_roles\b/i.test(sql)) {
30
33
  return { rows: rolesRow.map(function (n) { return { rolname: n }; }), rowCount: rolesRow.length };
31
34
  }
32
35
  if (/^SELECT 1$/i.test(sql)) return { rows: [{ n: 1 }], rowCount: 1 };
@@ -172,12 +172,58 @@ async function run() {
172
172
  async function () { await migrate.up(); },
173
173
  /externaldb-migrate\/missing-up/);
174
174
 
175
- // ---------- Cleanup ----------
176
-
177
175
  driver._close();
178
176
  b.externalDb._resetForTest();
179
177
  fs.rmSync(dataDir, { recursive: true, force: true });
180
178
  fs.rmSync(migDir, { recursive: true, force: true });
179
+
180
+ // ---------- Residency: framework tracking writes survive a
181
+ // residency-tagged backend under a cross-border regulated posture ----
182
+ // The migrate runner's own tracking / history / lock INSERTs are
183
+ // region-neutral metadata and carry the "unrestricted" tag; without
184
+ // that exemption the per-row residency write gate would refuse every
185
+ // migration with RESIDENCY_GATE_REQUIRED on an eu-tagged backend
186
+ // under gdpr.
187
+ var resDataDir = _tempDir("blamejs-xmig-res-data");
188
+ var resMigDir = _tempDir("blamejs-xmig-res-mig");
189
+ var resDriver = _makeSqliteDriver(path.join(resDataDir, "fake-pg.db"));
190
+ b.externalDb._resetForTest();
191
+ b.externalDb.init({
192
+ backends: {
193
+ main: {
194
+ connect: resDriver.connect, query: resDriver.query,
195
+ close: resDriver.close, dialect: "postgres", residencyTag: "eu",
196
+ },
197
+ },
198
+ });
199
+ // DDL-only migration — the only DML in the transaction is the
200
+ // framework's tracking + history INSERTs (operator DML would itself
201
+ // be gated, which is correct, so the migration body stays DDL).
202
+ _writeMigration(resMigDir, "0001-ddl-only.js",
203
+ 'module.exports = {\n' +
204
+ ' description: "create widget",\n' +
205
+ ' up: async function (xdb) { await xdb.query("CREATE TABLE widget (id INTEGER PRIMARY KEY)", []); },\n' +
206
+ ' down: async function (xdb) { await xdb.query("DROP TABLE widget", []); },\n' +
207
+ '};\n');
208
+ var resMigrate = b.externalDb.migrate.create({ dir: resMigDir });
209
+ b.compliance.clear();
210
+ b.compliance.set("gdpr");
211
+ try {
212
+ var resR = await resMigrate.up();
213
+ check("migrate up() succeeds on eu backend under gdpr (framework writes exempt)",
214
+ resR.applied.length === 1 && resR.applied[0] === "0001-ddl-only.js");
215
+ // The tracking row landed — re-running is idempotent (proves the
216
+ // framework INSERT into the tracking table actually committed).
217
+ var resR2 = await resMigrate.up();
218
+ check("migrate is idempotent after the tracked apply (tracking INSERT committed)",
219
+ resR2.applied.length === 0 && resR2.skipped.length === 1);
220
+ } finally {
221
+ b.compliance.clear();
222
+ resDriver._close();
223
+ b.externalDb._resetForTest();
224
+ fs.rmSync(resDataDir, { recursive: true, force: true });
225
+ fs.rmSync(resMigDir, { recursive: true, force: true });
226
+ }
181
227
  }
182
228
 
183
229
  module.exports = { run: run };