@blamejs/blamejs-shop 0.4.30 → 0.4.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/checkout.js +8 -0
  4. package/lib/order.js +71 -11
  5. package/lib/vendor/MANIFEST.json +392 -278
  6. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  7. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  8. package/lib/vendor/blamejs/.gitignore +6 -0
  9. package/lib/vendor/blamejs/CHANGELOG.md +26 -0
  10. package/lib/vendor/blamejs/MIGRATING.md +43 -0
  11. package/lib/vendor/blamejs/README.md +8 -6
  12. package/lib/vendor/blamejs/SECURITY.md +19 -3
  13. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  14. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  15. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  16. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  17. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  18. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  19. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  20. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  21. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  22. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  23. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  24. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  25. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  26. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  27. package/lib/vendor/blamejs/index.js +4 -0
  28. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  29. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  30. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  31. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  32. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  33. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  34. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  35. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  36. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  37. package/lib/vendor/blamejs/lib/audit.js +259 -123
  38. package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
  39. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  40. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  41. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
  42. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  43. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  44. package/lib/vendor/blamejs/lib/cache.js +174 -105
  45. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  46. package/lib/vendor/blamejs/lib/cli.js +19 -14
  47. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  48. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  49. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  50. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  51. package/lib/vendor/blamejs/lib/compliance.js +206 -4
  52. package/lib/vendor/blamejs/lib/consent.js +82 -29
  53. package/lib/vendor/blamejs/lib/constants.js +27 -11
  54. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  55. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  56. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  57. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  58. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  59. package/lib/vendor/blamejs/lib/db.js +249 -99
  60. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  61. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  62. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  63. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  64. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  65. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  66. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  67. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  68. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  69. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  70. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  71. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  72. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  73. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  74. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  75. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  76. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  77. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  78. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  79. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  80. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  81. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  82. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  83. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  84. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  85. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  86. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  87. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  88. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  89. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  90. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  91. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  92. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  93. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  94. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  95. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  96. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  97. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  98. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  99. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  100. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  101. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  102. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  103. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  104. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  105. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  106. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  107. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  108. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  109. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  110. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  111. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  112. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  113. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  114. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  115. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  116. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  117. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  118. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  119. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  120. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  121. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  122. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  123. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  124. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  125. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  126. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  127. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  128. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  129. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  131. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  132. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  133. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  134. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  135. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  136. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  137. package/lib/vendor/blamejs/lib/mail.js +8 -4
  138. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  139. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  140. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  141. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  142. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  143. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  144. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  145. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  146. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  147. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  148. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  149. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  150. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  151. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  152. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  153. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  154. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  155. package/lib/vendor/blamejs/lib/observability.js +124 -0
  156. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  157. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  158. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  159. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  160. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  161. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  162. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  163. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  164. package/lib/vendor/blamejs/lib/queue.js +7 -0
  165. package/lib/vendor/blamejs/lib/redact.js +68 -11
  166. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  167. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  168. package/lib/vendor/blamejs/lib/retention.js +101 -40
  169. package/lib/vendor/blamejs/lib/router.js +212 -5
  170. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  171. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  172. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  173. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  174. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  175. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  176. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  177. package/lib/vendor/blamejs/lib/scheduler.js +35 -12
  178. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  179. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  180. package/lib/vendor/blamejs/lib/session.js +175 -77
  181. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  182. package/lib/vendor/blamejs/lib/sse.js +26 -0
  183. package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
  184. package/lib/vendor/blamejs/lib/static.js +177 -34
  185. package/lib/vendor/blamejs/lib/subject.js +96 -49
  186. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  187. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  188. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  189. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  190. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  191. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  192. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  193. package/lib/vendor/blamejs/package.json +2 -2
  194. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  195. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  200. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  201. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  202. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  203. package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
  204. package/lib/vendor/blamejs/scripts/release.js +398 -38
  205. package/lib/vendor/blamejs/test/00-primitives.js +117 -0
  206. package/lib/vendor/blamejs/test/10-state.js +140 -14
  207. package/lib/vendor/blamejs/test/20-db.js +65 -2
  208. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  209. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  210. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  211. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  212. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  213. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  214. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  215. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  216. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  217. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  220. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  221. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  222. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  223. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  224. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  225. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  226. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  227. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  228. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  229. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  230. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  231. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  232. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  233. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  234. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  235. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  236. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  237. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  238. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  240. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  248. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  250. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
  251. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  253. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  256. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  259. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  260. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  261. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  271. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  272. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  273. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  274. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  275. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  277. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  278. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  279. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  280. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  282. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  283. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  284. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  285. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  293. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  304. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  308. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  310. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  312. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  313. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  314. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  315. package/lib/vendor/blamejs/test/smoke.js +79 -21
  316. package/package.json +1 -1
  317. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  318. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  319. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  320. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  321. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  322. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  323. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  324. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  325. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  326. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  327. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  328. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  329. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  330. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  331. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  332. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  333. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  334. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  335. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  336. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  337. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  338. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ /**
3
+ * b.db.stream OOM-safety + streamLimit cap, and sealed-column byte-
4
+ * fidelity across hostile payload shapes.
5
+ *
6
+ * Two guarantees under test, both on the production encrypted-at-rest
7
+ * path (real wrapped vault + sealed db.enc + audit chain via
8
+ * setupTestDb):
9
+ *
10
+ * (a) b.db.stream yields rows from node:sqlite's iterate() WITHOUT
11
+ * materializing the whole result set (the documented win over
12
+ * .all()), preserves row order, and DESTROYS the stream with a
13
+ * db/stream-limit-exceeded error once the per-call streamLimit
14
+ * cap is crossed rather than accumulating unboundedly.
15
+ *
16
+ * (b) A sealed column round-trips seal -> store -> read -> unseal
17
+ * byte-identical for the payload shapes a real column carries.
18
+ * This is where a silent value-corruption bug would hide: the
19
+ * seal layer must not lose bytes on the way through the AEAD
20
+ * envelope.
21
+ */
22
+
23
+ var helpers = require("../helpers");
24
+ var b = helpers.b;
25
+ var check = helpers.check;
26
+ var fs = helpers.fs;
27
+ var os = helpers.os;
28
+ var path = helpers.path;
29
+ var setupTestDb = helpers.setupTestDb;
30
+ var waitUntil = helpers.waitUntil;
31
+
32
+ // Stream-test table: a sealed column so the stream's auto-unseal path
33
+ // (opts.table) is exercised, plus an ordering column.
34
+ // Payload-fidelity table: one sealed TEXT column (seal layer) and one
35
+ // NON-sealed BLOB column (raw node:sqlite storage), so the two
36
+ // round-trip paths are isolated from each other.
37
+ var SCHEMA = [
38
+ {
39
+ name: "events",
40
+ columns: {
41
+ _id: "TEXT PRIMARY KEY",
42
+ seq: "INTEGER NOT NULL",
43
+ payload: "TEXT",
44
+ },
45
+ indexes: ["seq"],
46
+ sealedFields: ["payload"],
47
+ },
48
+ {
49
+ name: "blobs",
50
+ columns: {
51
+ _id: "TEXT PRIMARY KEY",
52
+ sealedText: "TEXT", // routed through the seal layer
53
+ rawBlob: "BLOB", // stored as a raw node:sqlite BLOB, not sealed
54
+ },
55
+ sealedFields: ["sealedText"],
56
+ },
57
+ ];
58
+
59
+ // Drain a Readable into an array of rows, honoring backpressure-free
60
+ // flowing mode but capturing order. Rejects on stream error.
61
+ function collectStream(stream) {
62
+ return new Promise(function (resolve, reject) {
63
+ var rows = [];
64
+ stream.on("data", function (row) { rows.push(row); });
65
+ stream.on("end", function () { resolve(rows); });
66
+ stream.on("error", function (err) { reject(err); });
67
+ });
68
+ }
69
+
70
+ // Capture the error a stream destroys with (the cap path). Resolves
71
+ // with { errored, err, rowsBeforeError }.
72
+ function collectStreamError(stream) {
73
+ return new Promise(function (resolve) {
74
+ var rows = [];
75
+ stream.on("data", function (row) { rows.push(row); });
76
+ stream.on("end", function () { resolve({ errored: false, err: null, rowsBeforeError: rows }); });
77
+ stream.on("error", function (err) { resolve({ errored: true, err: err, rowsBeforeError: rows }); });
78
+ });
79
+ }
80
+
81
+ function bufEqual(a, exp) {
82
+ return Buffer.isBuffer(a) || a instanceof Uint8Array
83
+ ? Buffer.compare(Buffer.from(a), exp) === 0
84
+ : false;
85
+ }
86
+
87
+ async function run() {
88
+ var tmp = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-db-stream-shape-"));
89
+ await setupTestDb(tmp, SCHEMA);
90
+
91
+ // ====================================================================
92
+ // (a) b.db.stream — non-materializing, ordered, capped
93
+ // ====================================================================
94
+
95
+ // Insert a few thousand rows so the stream has real work to do and
96
+ // the cap can fire well before the end of the set.
97
+ var N = 4000;
98
+ var events = b.db.from("events");
99
+ for (var i = 0; i < N; i++) {
100
+ events.insertOne({ seq: i, payload: "payload-row-" + i });
101
+ }
102
+ check("inserted N rows", b.db.from("events").count() === N);
103
+
104
+ // ---- order + completeness: every row arrives, in seq order, unsealed
105
+ var streamed = await collectStream(
106
+ b.db.stream("SELECT * FROM events ORDER BY seq ASC", { table: "events" })
107
+ );
108
+ check("stream yielded every row", streamed.length === N);
109
+ var ordered = true;
110
+ var unsealedOk = true;
111
+ for (var j = 0; j < streamed.length; j++) {
112
+ if (streamed[j].seq !== j) ordered = false;
113
+ if (streamed[j].payload !== "payload-row-" + j) unsealedOk = false;
114
+ }
115
+ check("stream preserved row order", ordered);
116
+ check("stream auto-unsealed the sealed column", unsealedOk);
117
+
118
+ // ---- non-materialization: the stream is pull-based. Pause it after
119
+ // the first row and prove no further rows are delivered while paused
120
+ // (a .all()-style eager materialization could not honor this), then
121
+ // resume and confirm the rest flow. This demonstrates the iterate()
122
+ // generator is pulled lazily, not collected up-front.
123
+ var pullStream = b.db.stream("SELECT * FROM events ORDER BY seq ASC", { table: "events" });
124
+ var seen = [];
125
+ var pausedAtCount = -1;
126
+ pullStream.on("data", function (row) {
127
+ seen.push(row);
128
+ if (seen.length === 1) {
129
+ pullStream.pause();
130
+ pausedAtCount = seen.length;
131
+ // Resume on the next tick; while paused, no more 'data' must fire.
132
+ setImmediate(function () {
133
+ // Confirm the stream did NOT race ahead and buffer the whole
134
+ // set while we were paused: at most a tiny highWaterMark of
135
+ // rows can be in flight, never the full N.
136
+ check("paused stream did not materialize the full set",
137
+ seen.length < N);
138
+ pullStream.resume();
139
+ });
140
+ }
141
+ });
142
+ var pulled = await new Promise(function (resolve, reject) {
143
+ pullStream.on("end", function () { resolve(seen); });
144
+ pullStream.on("error", reject);
145
+ });
146
+ check("paused-then-resumed stream still delivered every row", pulled.length === N);
147
+ check("pause point was the first row", pausedAtCount === 1);
148
+
149
+ // ---- cap: a per-call streamLimit BELOW the row count must destroy
150
+ // the stream with db/stream-limit-exceeded, not silently truncate or
151
+ // run to completion.
152
+ var CAP = 100;
153
+ var capResult = await collectStreamError(
154
+ b.db.stream("SELECT * FROM events ORDER BY seq ASC", { table: "events", streamLimit: CAP })
155
+ );
156
+ check("over-cap stream errored (did not run to completion)", capResult.errored === true);
157
+ check("over-cap stream error carries db/stream-limit-exceeded code",
158
+ !!capResult.err && capResult.err.code === "db/stream-limit-exceeded");
159
+ check("over-cap stream stopped at the cap (no unbounded accumulation)",
160
+ capResult.rowsBeforeError.length <= CAP);
161
+
162
+ // ---- a streamLimit AT OR ABOVE the row count runs clean to the end.
163
+ var underCap = await collectStream(
164
+ b.db.stream("SELECT * FROM events ORDER BY seq ASC", { table: "events", streamLimit: N + 10 })
165
+ );
166
+ check("within-cap stream completes without error", underCap.length === N);
167
+
168
+ // ---- a bad streamLimit shape throws at call time (config-time tier).
169
+ var badLimitThrew = false;
170
+ try { b.db.stream("SELECT * FROM events", { table: "events", streamLimit: -1 }); }
171
+ catch (e) { badLimitThrew = e && e.code === "db/bad-stream-limit"; }
172
+ check("negative streamLimit throws db/bad-stream-limit at call time", badLimitThrew);
173
+
174
+ // ====================================================================
175
+ // (b) sealed-column byte-fidelity across hostile payload shapes
176
+ // ====================================================================
177
+ //
178
+ // For each shape, drive the PRODUCTION seal path
179
+ // (cryptoField.sealRow -> store via db -> read -> auto-unseal) and
180
+ // assert byte-identical recovery. Any shape that does not round-trip
181
+ // byte-identical is a real data-integrity bug — the column was
182
+ // declared sealed by the operator and silently corrupting its value
183
+ // defeats the at-rest-encryption guarantee.
184
+
185
+ // --- Shape 1: astral-plane Unicode (emoji + CJK Extension B) ---
186
+ var astral = "rocket\u{1F680} cjkB\u{20000}\u{2A6B2} family\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}";
187
+ b.db.from("blobs").insertOne({ _id: "astral", sealedText: astral });
188
+ var gotAstral = b.db.from("blobs").where({ _id: "astral" }).first();
189
+ check("astral-plane Unicode round-trips byte-identical through sealed column",
190
+ gotAstral.sealedText === astral);
191
+
192
+ // --- Shape 2: a string containing embedded NUL bytes ---
193
+ var withNul = "beforemiddleafter";
194
+ b.db.from("blobs").insertOne({ _id: "nul", sealedText: withNul });
195
+ var gotNul = b.db.from("blobs").where({ _id: "nul" }).first();
196
+ check("string with embedded NUL bytes round-trips byte-identical through sealed column",
197
+ gotNul.sealedText === withNul);
198
+
199
+ // --- Shape 3: a large (~256 KiB) value ---
200
+ var large = "";
201
+ var chunk = "0123456789abcdef"; // 16 bytes
202
+ while (large.length < 256 * 1024) large += chunk; // ~256 KiB
203
+ b.db.from("blobs").insertOne({ _id: "large", sealedText: large });
204
+ var gotLarge = b.db.from("blobs").where({ _id: "large" }).first();
205
+ check("large (~256 KiB) value round-trips byte-identical through sealed column",
206
+ gotLarge.sealedText === large && gotLarge.sealedText.length === large.length);
207
+
208
+ // --- Shape 4: a binary Buffer/BLOB carrying all 256 byte values ---
209
+ // First prove the DB BLOB column itself preserves bytes (the storage
210
+ // floor), so any seal-layer corruption can't be blamed on sqlite.
211
+ var allBytes = Buffer.alloc(256);
212
+ for (var bb = 0; bb < 256; bb++) allBytes[bb] = bb;
213
+ b.db.from("blobs").insertOne({ _id: "rawblob", rawBlob: allBytes });
214
+ var gotRawBlob = b.db.from("blobs").where({ _id: "rawblob" }).first();
215
+ check("raw (non-sealed) BLOB column preserves all 256 byte values byte-identical",
216
+ bufEqual(gotRawBlob.rawBlob, allBytes));
217
+
218
+ // Now the same all-256-bytes Buffer through a SEALED column. A sealed
219
+ // column is the operator's declaration that the value holds protected
220
+ // data; a binary column (e.g. a sealed encryption sub-key, a packed
221
+ // protobuf, a thumbnail) is a legitimate sealed payload. The seal
222
+ // path MUST either preserve the bytes or refuse — silently mangling
223
+ // them is the bug.
224
+ b.db.from("blobs").insertOne({ _id: "sealedblob", sealedText: allBytes });
225
+ var gotSealedBlob = b.db.from("blobs").where({ _id: "sealedblob" }).first();
226
+ var sealedBlobBytes = Buffer.isBuffer(gotSealedBlob.sealedText) || gotSealedBlob.sealedText instanceof Uint8Array
227
+ ? Buffer.from(gotSealedBlob.sealedText)
228
+ : Buffer.from(String(gotSealedBlob.sealedText), "utf8");
229
+ check("all-256-byte Buffer round-trips byte-identical through a SEALED column",
230
+ Buffer.compare(sealedBlobBytes, allBytes) === 0);
231
+
232
+ // --- Shape 5: a deeply-nested JSON object ---
233
+ // A sealed column declared to hold a structured value (the framework
234
+ // even JSON.stringify's containment values elsewhere) must recover the
235
+ // SAME object — not a coerced "[object Object]" / lossy string.
236
+ var nested = { a: { b: { c: { d: [1, 2, { e: "deep", f: [null, true, "xy"] }] } } }, n: 42 };
237
+ b.db.from("blobs").insertOne({ _id: "nested", sealedText: nested });
238
+ var gotNested = b.db.from("blobs").where({ _id: "nested" }).first();
239
+ var recovered;
240
+ try {
241
+ recovered = typeof gotNested.sealedText === "string"
242
+ ? JSON.parse(gotNested.sealedText)
243
+ : gotNested.sealedText;
244
+ } catch (_e) { recovered = gotNested.sealedText; }
245
+ check("deeply-nested object round-trips structurally through a SEALED column",
246
+ JSON.stringify(recovered) === JSON.stringify(nested));
247
+
248
+ // Touch waitUntil so the import is exercised even though every assert
249
+ // above is synchronous against the local sqlite path (keeps the
250
+ // helper dependency honest for future async growth).
251
+ await waitUntil(function () { return true; }, { timeoutMs: 1000, label: "db-stream-shape: trivial settle" });
252
+
253
+ b.db.close();
254
+ b.audit._resetForTest();
255
+ b.db._resetForTest();
256
+ b.vault._resetForTest();
257
+ b.cluster._resetForTest();
258
+ try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (_e) {}
259
+
260
+ console.log("OK — db.stream OOM-safety + cap, sealed-column byte-fidelity");
261
+ }
262
+
263
+ module.exports = { run: run };
264
+ if (require.main === module) {
265
+ run().then(function () { process.exit(0); })
266
+ .catch(function (err) { process.exitCode = 1; throw err; });
267
+ }
@@ -25,6 +25,27 @@ async function run() {
25
25
  },
26
26
  indexes: ["userId"],
27
27
  },
28
+ {
29
+ // Erasure target for the eraseHard happy-path proof. NOT a WORM
30
+ // table (eraseHard's raw DELETE would trip a WORM BEFORE-DELETE
31
+ // trigger), but gated by dual-control so the destructive op only
32
+ // runs against a consumed m-of-n grant.
33
+ // ssn — sealed at rest (exercises cryptoField.eraseRow)
34
+ // tag — non-sealed, INDEXED plaintext column. This is the
35
+ // forensic target: its plaintext lands in the SQLite
36
+ // table-leaf AND index B-tree pages, so DELETE + REINDEX
37
+ // (+ the advertised vacuumAfterErase companion) must
38
+ // leave no on-disk residual.
39
+ name: "stale_pii",
40
+ columns: {
41
+ _id: "TEXT PRIMARY KEY",
42
+ ssn: "TEXT",
43
+ tag: "TEXT",
44
+ keepId: "TEXT",
45
+ },
46
+ indexes: ["tag"],
47
+ sealedFields: ["ssn"],
48
+ },
28
49
  ]);
29
50
 
30
51
  // Insert a row before WORM declaration to exercise the trigger gate.
@@ -83,6 +104,135 @@ async function run() {
83
104
  catch (e) { noGrantRefused = /dual-control/i.test(e.message); }
84
105
  check("eraseHard: dual-control gate refuses without grant", noGrantRefused);
85
106
 
107
+ // ---------------------------------------------------------------------
108
+ // Happy path — eraseHard ACTUALLY destroys data (DELETE + crypto-erase
109
+ // + REINDEX) under a real consumed dual-control grant, and leaves no
110
+ // forensic residual on disk.
111
+ // ---------------------------------------------------------------------
112
+
113
+ // A unique, high-entropy sentinel so a byte-scan of the on-disk files
114
+ // can't false-positive on incidental bytes. Stored in the non-sealed,
115
+ // INDEXED `tag` column → its plaintext is in both the table-leaf and
116
+ // the index B-tree pages of the working SQLite file.
117
+ var SENTINEL = "ERASE-SENTINEL-" +
118
+ b.crypto.generateToken(b.constants.BYTES.bytes(24)) + "-ZZ";
119
+ var SSN_PLAIN = "987-65-4321";
120
+
121
+ b.db.from("stale_pii").insertOne({ _id: "doomed", ssn: SSN_PLAIN, tag: SENTINEL, keepId: "k1" });
122
+ // A second row that must SURVIVE the erase — proves eraseHard scopes
123
+ // to the named rowId and doesn't wipe the table.
124
+ var SURVIVOR = "SURVIVOR-SENTINEL-" +
125
+ b.crypto.generateToken(b.constants.BYTES.bytes(16)) + "-YY";
126
+ b.db.from("stale_pii").insertOne({ _id: "keeper", ssn: "111-22-3333", tag: SURVIVOR, keepId: "k2" });
127
+
128
+ // Gate the erase behind dual-control (m=2, n=3).
129
+ b.db.declareRequireDualControl({ tables: ["stale_pii"], m: 2, n: 3, posture: "gdpr" });
130
+
131
+ // Sealed round-trip: the row reads back with plaintext ssn (proves the
132
+ // value was genuinely sealed-then-unsealed, i.e. encryption is live).
133
+ var before = b.db.from("stale_pii").where({ _id: "doomed" }).first();
134
+ check("eraseHard happy: sealed row reads back before erase", !!before && before.ssn === SSN_PLAIN);
135
+ check("eraseHard happy: tag present in-row before erase", !!before && before.tag === SENTINEL);
136
+
137
+ // Locate the on-disk files. In encrypted-at-rest mode the working
138
+ // SQLite file lives in tmpfs (getDbPath); the durable sealed copy is
139
+ // <dataDir>/db.enc. Force a checkpoint + WAL flush so committed pages
140
+ // are visible in the main file for the forensic scan.
141
+ var dbFile = b.db.getDbPath();
142
+ var walFile = dbFile + "-wal";
143
+ var encFile = path.join(tmpDir, "db.enc");
144
+ b.db.flushToDisk(); // checkpoint(TRUNCATE) + re-seal db.enc
145
+
146
+ function fileHasSentinel(p, needle) {
147
+ if (!fs.existsSync(p)) return false;
148
+ return fs.readFileSync(p).includes(Buffer.from(needle, "utf8"));
149
+ }
150
+ function diskHasSentinel(needle) {
151
+ return fileHasSentinel(dbFile, needle) ||
152
+ fileHasSentinel(walFile, needle);
153
+ }
154
+
155
+ // CONTROL — before erase the sentinel plaintext IS recoverable from the
156
+ // working SQLite file (table-leaf and/or index pages). Without this the
157
+ // "absent after" assertion below would be vacuous.
158
+ check("eraseHard FORENSIC control: sentinel plaintext present on disk before erase",
159
+ diskHasSentinel(SENTINEL));
160
+ // db.enc is an encrypted envelope — the sentinel must NOT be plaintext-
161
+ // visible there at any point (sanity check on at-rest encryption).
162
+ check("eraseHard FORENSIC: db.enc never exposes sentinel plaintext (encrypted at rest)",
163
+ !fileHasSentinel(encFile, SENTINEL));
164
+
165
+ // Build a REAL dual-control grant: request → a different actor approves
166
+ // → quorum → consume. consume() returns { ready: true } which eraseHard
167
+ // requires. No stub — the production dual-control workflow.
168
+ var approvals = b.dualControl.create({
169
+ namespace: "stale_pii.eraseHard",
170
+ cache: b.cache.create({ namespace: "dc-erase", backend: "memory" }),
171
+ audit: b.audit,
172
+ minApprovers: 2,
173
+ });
174
+ var opened = await approvals.request({
175
+ action: "stale_pii.eraseHard",
176
+ resource: { kind: "stale_pii", id: "doomed" },
177
+ requestedBy: { id: "alice" },
178
+ reason: "GDPR Art. 17 erasure request, ticket SUP-9001",
179
+ });
180
+ check("eraseHard happy: grant opened pending", opened && opened.status === "pending");
181
+ await approvals.approve({ grantId: opened.grantId, approver: { id: "bob" }, reason: "verified subject identity" });
182
+ var quorum = await approvals.approve({ grantId: opened.grantId, approver: { id: "carol" }, reason: "second approval" });
183
+ check("eraseHard happy: grant reached quorum", quorum && quorum.status === "approved");
184
+ var grant = await approvals.consume(opened.grantId, {});
185
+ check("eraseHard happy: grant consumed (ready)", grant && grant.ready === true);
186
+
187
+ // The destructive op under the consumed grant.
188
+ var result = await b.db.eraseHard("stale_pii", "doomed", {
189
+ reason: "subject erasure under GDPR Art 17, ticket SUP-9001",
190
+ dualControlGrant: grant,
191
+ });
192
+ check("eraseHard happy: reports rowsDeleted === 1", result && result.rowsDeleted === 1);
193
+
194
+ // (a) Row is gone from the table.
195
+ var afterRow = b.db.from("stale_pii").where({ _id: "doomed" }).first();
196
+ check("eraseHard happy: erased row absent from table", afterRow === null || afterRow === undefined);
197
+ // The unrelated row survives — erase is row-scoped, not a table wipe.
198
+ var keeper = b.db.from("stale_pii").where({ _id: "keeper" }).first();
199
+ check("eraseHard happy: unrelated row survives", !!keeper && keeper.tag === SURVIVOR);
200
+
201
+ // (b) Audit chain still verifies end-to-end (the erase emitted a signed
202
+ // db.erase_hard row; the hash chain must remain intact).
203
+ await b.audit.flush();
204
+ var chain = await b.audit.verify();
205
+ check("eraseHard happy: audit chain verifies intact after erase", chain && chain.ok === true);
206
+
207
+ // (c) FORENSIC — the core guarantee, attributed to eraseHard ITSELF
208
+ // (DELETE + REINDEX), measured BEFORE any later vacuum so the scrub
209
+ // can't be credited to a separate VACUUM pass. Re-checkpoint + re-seal
210
+ // so the on-disk files reflect post-erase state, then assert the
211
+ // deleted row's plaintext sentinel is gone from the working SQLite +
212
+ // WAL. The CONTROL above proved it WAS recoverable before the erase, so
213
+ // this is a real before/after delta, not a vacuous read.
214
+ b.db.flushToDisk();
215
+ check("eraseHard FORENSIC: erased sentinel plaintext ABSENT from working SQLite + WAL after eraseHard+REINDEX",
216
+ !diskHasSentinel(SENTINEL));
217
+ // The survivor's plaintext is still on disk — confirms the scan is live
218
+ // (it isn't reporting "absent" because the whole file was wiped).
219
+ check("eraseHard FORENSIC: survivor sentinel still present on disk (scan is live, not whole-file wipe)",
220
+ diskHasSentinel(SURVIVOR));
221
+ // db.enc is an encrypted envelope — never plaintext-visible at any point.
222
+ check("eraseHard FORENSIC: db.enc holds no sentinel plaintext after erase",
223
+ !fileHasSentinel(encFile, SENTINEL));
224
+
225
+ // Belt-and-suspenders: the advertised vacuumAfterErase companion (full
226
+ // VACUUM rewrites every page) is the operator's defense against any
227
+ // freed-page residual a later large-scale erase batch could leave. Run
228
+ // it and re-confirm the sentinel stays absent and the survivor stays.
229
+ b.db.vacuumAfterErase({ mode: "full" });
230
+ b.db.flushToDisk();
231
+ check("eraseHard FORENSIC: sentinel still absent after vacuumAfterErase(full)",
232
+ !diskHasSentinel(SENTINEL));
233
+ check("eraseHard FORENSIC: survivor still present after vacuumAfterErase(full)",
234
+ diskHasSentinel(SURVIVOR));
235
+
86
236
  await dbHelper.teardownTestDb(tmpDir);
87
237
  }
88
238
 
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ // defineGuard's default gate must resolve the profile + posture BEFORE building
3
+ // the gate, so a guard gated with { compliancePosture } honors that posture's
4
+ // forensicSnippetBytes (and the profile's maxRuntimeMs). The default gate passed
5
+ // RAW opts straight to buildGuardGate, so the posture's forensic cap was dropped
6
+ // to 0 (forensic snapshots disabled) — a regulated-posture refusal carried no
7
+ // evidence, defeating the forensic-capture the posture promises.
8
+
9
+ var helpers = require("../helpers");
10
+ var b = helpers.b;
11
+ var check = helpers.check;
12
+
13
+ async function run() {
14
+ // guard-cidr is built via gateContract.defineGuard with the STANDARD default
15
+ // gate (no bespoke spec.gate), so .gate() exercises the default-gate path.
16
+ // Its hipaa posture sets forensicSnippetBytes = 128.
17
+ var bad = "999.999.999.999/99";
18
+ var g = b.guardCidr.gate({ compliancePosture: "hipaa" });
19
+ var decision = await g.check({ identifier: bad, bytes: Buffer.from(bad, "utf8") });
20
+ check("hipaa gate refuses a malformed CIDR", decision.action === "refuse");
21
+ check("hipaa posture forensicSnippetBytes is applied → refusal carries a forensic snapshot",
22
+ decision.forensicSnapshot != null);
23
+ check("the forensic snapshot is bounded by the posture cap (<= 128 bytes)",
24
+ decision.forensicSnapshot != null && decision.forensicSnapshot.length <= 128);
25
+
26
+ process.stdout.write("OK — defineGuard default-gate posture-cap tests\n");
27
+ }
28
+
29
+ run().then(function () { process.exit(0); })
30
+ .catch(function (e) { process.stderr.write((e && e.stack ? e.stack : String(e)) + "\n"); process.exit(1); });
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ // b.middleware.dpop must REQUIRE a replayStore. The store IS the jti-replay
3
+ // defense: without it a captured DPoP proof can be replayed indefinitely — the
4
+ // exact attack proof-of-possession exists to stop (RFC 9449 §11.1). The doc has
5
+ // always listed replayStore as "required", but create() read it optionally and
6
+ // mounted a gate that performed no replay check when it was omitted. Mounting
7
+ // must fail closed at config time, never silently disable the defense.
8
+
9
+ var helpers = require("../helpers");
10
+ var b = helpers.b;
11
+ var check = helpers.check;
12
+
13
+ function expectThrow(label, fn, codeNeedle) {
14
+ var threw = null;
15
+ try { fn(); } catch (e) { threw = e; }
16
+ check(label + " — throws at create()", threw !== null);
17
+ if (threw && codeNeedle) {
18
+ check(label + " — error names the replay store",
19
+ String((threw.code || threw.message) || "").toLowerCase().indexOf(codeNeedle) !== -1);
20
+ }
21
+ }
22
+
23
+ function run() {
24
+ // 1. Omitting replayStore must throw at create() — no silent replay-off mount.
25
+ expectThrow("dpop middleware without replayStore", function () {
26
+ b.middleware.dpop({ getAccessToken: function () { return null; } });
27
+ }, "replay");
28
+
29
+ // 2. A replayStore lacking checkAndInsert must throw at create() (fail fast on
30
+ // config, not at the first request).
31
+ expectThrow("dpop middleware with a malformed replayStore", function () {
32
+ b.middleware.dpop({ replayStore: { notCheckAndInsert: true } });
33
+ }, "replay");
34
+
35
+ // 3. A valid replayStore mounts cleanly (control — proves the gate is not
36
+ // over-tightened into refusing legitimate config).
37
+ var mounted = null;
38
+ try {
39
+ mounted = b.middleware.dpop({ replayStore: b.nonceStore.create({ backend: "memory" }) });
40
+ } catch (e) { mounted = e; }
41
+ check("dpop middleware with a valid replayStore mounts", typeof mounted === "function");
42
+
43
+ process.stdout.write("OK — dpop middleware replayStore-required tests\n");
44
+ }
45
+
46
+ run();