@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,121 @@
1
+ "use strict";
2
+ /**
3
+ * azure-blob — blob key percent-encoding.
4
+ *
5
+ * Blob names are interpolated into the request URL path. A key with
6
+ * reserved characters (`?` / `#` / space) MUST be percent-encoded, or
7
+ * the `?` starts the query string / `#` starts the fragment (truncating
8
+ * the path → wrong object or container root) and spaces / control bytes
9
+ * corrupt the request line (CWE-20). `/` separators are preserved
10
+ * (blob names are hierarchical virtual directories). A null byte is
11
+ * refused. We assert the on-the-wire request line by recording the URL
12
+ * the production adapter actually sends to a mock HTTP server.
13
+ */
14
+ var http = require("node:http");
15
+ var helpers = require("../helpers");
16
+ var check = helpers.check;
17
+ var b = helpers.b;
18
+ var azure = require("../../lib/object-store/azure-blob");
19
+
20
+ function listenRecording() {
21
+ return new Promise(function (resolve) {
22
+ var requests = [];
23
+ var server = http.createServer(function (req, res) {
24
+ requests.push({ method: req.method, url: req.url });
25
+ res.statusCode = 200;
26
+ res.setHeader("etag", "\"mock-etag\"");
27
+ res.end();
28
+ });
29
+ server.listen(0, "127.0.0.1", function () {
30
+ resolve({
31
+ port: server.address().port,
32
+ requests: requests,
33
+ close: function () { return new Promise(function (r) { server.close(r); }); },
34
+ });
35
+ });
36
+ });
37
+ }
38
+
39
+ function _client(port) {
40
+ var key = Buffer.from("test-shared-key-32-bytes-padded__", "utf8").toString("base64");
41
+ return azure.create({
42
+ accountName: "blamejstest",
43
+ accountKey: key,
44
+ container: "cont",
45
+ endpoint: "http://127.0.0.1:" + port,
46
+ allowedProtocols: b.safeUrl.ALLOW_HTTP_ALL,
47
+ allowInternal: true,
48
+ timeoutMs: 5000,
49
+ });
50
+ }
51
+
52
+ async function run() {
53
+ var srv = await listenRecording();
54
+ try {
55
+ var c = _client(srv.port);
56
+
57
+ // Each tuple: [rawKey, expectedRequestPath]. The mock records req.url
58
+ // (path + query). The container is "cont".
59
+ var cases = [
60
+ ["a b.txt", "/cont/a%20b.txt"],
61
+ ["with?q.txt", "/cont/with%3Fq.txt"],
62
+ ["with#h.txt", "/cont/with%23h.txt"],
63
+ ["dir/sub dir/f.txt", "/cont/dir/sub%20dir/f.txt"],
64
+ ["a+b&c.txt", "/cont/a%2Bb%26c.txt"],
65
+ ["plain.txt", "/cont/plain.txt"],
66
+ ];
67
+
68
+ for (var i = 0; i < cases.length; i++) {
69
+ var raw = cases[i][0];
70
+ var expected = cases[i][1];
71
+ srv.requests.length = 0;
72
+ await c.put(raw, Buffer.from("body", "utf8"), { contentType: "text/plain" });
73
+ var sent = srv.requests.length === 1 ? srv.requests[0].url : "(no request)";
74
+ check("put key " + JSON.stringify(raw) + " → encoded path " + JSON.stringify(expected),
75
+ sent === expected);
76
+ }
77
+
78
+ // get() must encode the same way (the path is what the server matches).
79
+ srv.requests.length = 0;
80
+ await c.get("with?q.txt");
81
+ check("get key with '?' uses the encoded path",
82
+ srv.requests.length === 1 && srv.requests[0].url === "/cont/with%3Fq.txt");
83
+
84
+ // A reserved char must NOT leak into the query string — the `?` is
85
+ // encoded, so the recorded URL has no real query separator.
86
+ srv.requests.length = 0;
87
+ await c.head("q?inject=1.txt");
88
+ check("reserved '?' does not start a query string",
89
+ srv.requests.length === 1 &&
90
+ srv.requests[0].url === "/cont/q%3Finject%3D1.txt" &&
91
+ srv.requests[0].url.indexOf("?") === -1);
92
+
93
+ // Null byte refused (CWE-20 hostile key) before any request.
94
+ srv.requests.length = 0;
95
+ var threwNull = false;
96
+ try { await c.put("bad" + String.fromCharCode(0) + "key.txt", Buffer.from("x", "utf8")); }
97
+ catch (e) { threwNull = e && e.code === "INVALID_KEY"; }
98
+ check("null-byte key refused with INVALID_KEY", threwNull);
99
+ check("null-byte key sends no request", srv.requests.length === 0);
100
+
101
+ // Presigned URL encodes the key in the PATH while the SAS signature
102
+ // is computed over the raw resource — the returned URL must carry the
103
+ // encoded path AND the SAS query params.
104
+ var pre = c.presignedDownloadUrl({ key: "a b?c.txt", expiresIn: 300 });
105
+ check("presigned URL encodes the key path",
106
+ pre.url.indexOf("/cont/a%20b%3Fc.txt") !== -1);
107
+ check("presigned URL still carries the SAS signature",
108
+ pre.url.indexOf("sig=") !== -1 && pre.url.indexOf("se=") !== -1);
109
+ } finally {
110
+ await srv.close();
111
+ }
112
+ }
113
+
114
+ module.exports = { run: run };
115
+
116
+ if (require.main === module) {
117
+ run().then(
118
+ function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
119
+ function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
120
+ );
121
+ }
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ /**
3
+ * b.backup.create — residency posture enforcement.
4
+ *
5
+ * The backup-create residency gate (lib/backup/index.js) is advertised to
6
+ * refuse a cross-border destination under a regulated posture, advise on an
7
+ * undeclared destination, and (the real bug) is blind to per-row residency
8
+ * tags: a deployment whose region is EU but which holds per-row us-east-1
9
+ * rows backs up to an EU destination with no warning about the US rows.
10
+ */
11
+
12
+ var helpers = require("../helpers");
13
+ var b = helpers.b;
14
+ var check = helpers.check;
15
+ var fs = helpers.fs;
16
+ var os = helpers.os;
17
+ var path = helpers.path;
18
+
19
+ // Capture audit.safeEmit events by call-through wrapping (same spy shape the
20
+ // erase-posture-vacuum test uses for vacuumAfterErase). The backup gate emits
21
+ // via b.audit.safeEmit, so wrapping it records the advisory.
22
+ function captureAudit() {
23
+ var events = [];
24
+ var real = b.audit.safeEmit;
25
+ b.audit.safeEmit = function (ev) {
26
+ try { events.push(ev); } catch (_e) { /* ignore */ }
27
+ return real.call(b.audit, ev);
28
+ };
29
+ return { events: events, restore: function () { b.audit.safeEmit = real; } };
30
+ }
31
+
32
+ async function initDbWithRegion(tmpDir, region) {
33
+ process.env.BLAMEJS_SKIP_NTP_CHECK = "1";
34
+ helpers.setTestPassphraseEnv();
35
+ b.cluster._resetForTest();
36
+ b.audit._resetForTest();
37
+ b.vault._resetForTest();
38
+ b.db._resetForTest();
39
+ await b.vault.init({ dataDir: tmpDir });
40
+ await b.db.init({
41
+ dataDir: tmpDir,
42
+ tmpDir: path.join(tmpDir, "tmpfs"),
43
+ dataResidency: { region: region, allowedStorageRegions: ["us-east-1", "global"] },
44
+ schema: [{
45
+ name: "residents",
46
+ columns: { _id: "TEXT PRIMARY KEY", name: "TEXT", dataRegion: "TEXT" },
47
+ }],
48
+ });
49
+ }
50
+
51
+ // Minimal valid backup opts that reach the residency block in create()
52
+ // (dataDir exists, storage/passphrase/files/vaultKeyJson present).
53
+ function backupOpts(dataDir, root, extra) {
54
+ fs.writeFileSync(path.join(dataDir, "db.enc"), Buffer.from([1, 2, 3]));
55
+ fs.writeFileSync(path.join(dataDir, "db.key.enc"), Buffer.from([4, 5, 6]));
56
+ var opts = {
57
+ dataDir: dataDir,
58
+ storage: b.backup.diskStorage({ root: root }),
59
+ passphrase: Buffer.from("operator-backup-passphrase-not-secret"),
60
+ files: [
61
+ { relativePath: "db.enc", kind: "raw", required: true },
62
+ { relativePath: "db.key.enc", kind: "raw", required: true },
63
+ ],
64
+ vaultKeyJson: '{"version":1,"kid":"k1"}',
65
+ };
66
+ if (extra) for (var k in extra) opts[k] = extra[k];
67
+ return opts;
68
+ }
69
+
70
+ function createCode(opts) {
71
+ try { b.backup.create(opts); } catch (e) { return e && e.code; }
72
+ return null;
73
+ }
74
+
75
+ async function run() {
76
+ var tmp = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-backupresid-"));
77
+ var data = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-backupresid-data-"));
78
+ var root = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-backupresid-root-"));
79
+ await initDbWithRegion(tmp, "eu-west-1");
80
+ b.compliance.clear();
81
+ b.compliance.set("gdpr");
82
+ try {
83
+ // ---- Case 1: MISMATCH-REFUSAL (advertised, never tested to throw) ----
84
+ check("gdpr + eu-west-1 db + us-east-1 destination → backup/residency-mismatch",
85
+ createCode(backupOpts(data, root, { residencyTag: "us-east-1" })) ===
86
+ "backup/residency-mismatch");
87
+ // The allowCrossBorder override path must SUCCEED.
88
+ var okOverride = null;
89
+ try {
90
+ okOverride = b.backup.create(backupOpts(data, root, {
91
+ residencyTag: "us-east-1", allowCrossBorder: true,
92
+ legalBasis: "EU SCCs 2021/914",
93
+ }));
94
+ } catch (_e) { okOverride = null; }
95
+ check("allowCrossBorder + legalBasis override creates the engine",
96
+ okOverride && typeof okOverride.run === "function");
97
+
98
+ // ---- Case 2: UNDECLARED-ADVISORY (advertised, never tested to emit) ----
99
+ var cap = captureAudit();
100
+ try {
101
+ var engNoTag = b.backup.create(backupOpts(data, root, {})); // no residencyTag
102
+ check("undeclared residencyTag does NOT throw (advisory, not refusal)",
103
+ engNoTag && typeof engNoTag.run === "function");
104
+ } finally { cap.restore(); }
105
+ check("undeclared residency under gdpr emits backup.residency_undeclared advisory",
106
+ cap.events.some(function (e) { return e && e.action === "backup.residency_undeclared"; }));
107
+
108
+ // ---- Case 3: PER-ROW BLINDNESS (the real bug — RED today) ----
109
+ // The deployment region is eu-west-1, but a per-row residency-tagged
110
+ // table admits a us-east-1 row (us-east-1 is in allowedStorageRegions).
111
+ // Backing up to an eu-west-1 destination must warn/refuse about the US
112
+ // rows — today it passes silently because the gate only sees the
113
+ // deployment-level region (eu-west-1 == residencyTag eu-west-1 → clean).
114
+ b.cryptoField.clearResidencyForTest();
115
+ b.cryptoField.declarePerRowResidency("residents", {
116
+ residencyColumn: "dataRegion",
117
+ allowedTags: ["eu-west-1", "us-east-1", "global"],
118
+ });
119
+ // The backup advisory reads its table set from this enumerator — assert it
120
+ // directly so the deployment-wide view the gate depends on is covered.
121
+ var declared = b.cryptoField.listPerRowResidency();
122
+ check("listPerRowResidency enumerates the declared table",
123
+ declared.length === 1 && declared[0].table === "residents" &&
124
+ declared[0].residencyColumn === "dataRegion" &&
125
+ declared[0].allowedTags.indexOf("us-east-1") !== -1);
126
+ b.db.from("residents").insertOne({ _id: "row-us", name: "u", dataRegion: "us-east-1" });
127
+ b.db.from("residents").insertOne({ _id: "row-eu", name: "e", dataRegion: "eu-west-1" });
128
+
129
+ var cap2 = captureAudit();
130
+ var sawPerRowAdvisory = false;
131
+ var threwPerRow = null;
132
+ try {
133
+ b.backup.create(backupOpts(data, root, { residencyTag: "eu-west-1" }));
134
+ } catch (e) { threwPerRow = e; }
135
+ finally {
136
+ sawPerRowAdvisory = cap2.events.some(function (e) {
137
+ return e && typeof e.action === "string" &&
138
+ /backup\.residency/.test(e.action) &&
139
+ e.metadata && (e.metadata.scope === "per-row" ||
140
+ /per[-_]?row|row[-_]?tag/.test(JSON.stringify(e.metadata)));
141
+ });
142
+ cap2.restore();
143
+ }
144
+ // RED: the US rows must be surfaced — either a per-row cross-border
145
+ // advisory is emitted, or create() refuses. Today neither happens.
146
+ check("per-row us-east-1 rows under an EU destination trigger a cross-border advisory or refusal",
147
+ sawPerRowAdvisory === true ||
148
+ (threwPerRow && threwPerRow.code === "backup/residency-mismatch"));
149
+ } finally {
150
+ b.cryptoField.clearResidencyForTest();
151
+ b.compliance.clear();
152
+ try { b.db.close(); } catch (_e) {}
153
+ b.audit._resetForTest();
154
+ b.db._resetForTest();
155
+ b.vault._resetForTest();
156
+ b.cluster._resetForTest();
157
+ fs.rmSync(tmp, { recursive: true, force: true });
158
+ fs.rmSync(data, { recursive: true, force: true });
159
+ fs.rmSync(root, { recursive: true, force: true });
160
+ }
161
+ console.log("OK — backup residency posture tests");
162
+ }
163
+
164
+ module.exports = { run: run };
165
+ if (require.main === module) {
166
+ run().then(function () { process.exit(0); })
167
+ .catch(function (err) { process.exitCode = 1; throw err; });
168
+ }
@@ -0,0 +1,318 @@
1
+ "use strict";
2
+ /**
3
+ * b.backup.scheduleTest — HIPAA §164.308(a)(7)(ii)(D) restore-drill.
4
+ *
5
+ * The drill is the regulator-facing proof that a backup is RESTORABLE,
6
+ * not merely that bytes landed in storage. The framework advertises that
7
+ * scheduleTest:
8
+ * (1) restores the latest bundle into an operator staging dir,
9
+ * (2) verifies the bundle manifest's SLH-DSA / ML-DSA signature,
10
+ * (3) hands the restored bundle + parsed manifest to the operator
11
+ * verify callback, and
12
+ * (4) records a pass/fail result in the signed audit chain
13
+ * (backup.test.passed / backup.test.failed).
14
+ *
15
+ * This drives the real drill end-to-end against a real sealed-at-rest
16
+ * data dir + signed audit chain + local diskStorage backend, then drives
17
+ * a FAILING drill (tampered manifest signature) and asserts it reports
18
+ * failure rather than a false pass.
19
+ *
20
+ * Two dispatch paths are exercised:
21
+ * - the closure scheduleTest builds (captured + awaited directly so we
22
+ * observe completion deterministically), and
23
+ * - the genuine b.scheduler dispatch (instance._fireOnce → drill runs).
24
+ */
25
+
26
+ var fs = require("fs");
27
+ var os = require("os");
28
+ var path = require("path");
29
+
30
+ var helpers = require("../helpers");
31
+ var check = helpers.check;
32
+ var b = helpers.b;
33
+ var waitUntil = helpers.waitUntil;
34
+ var { setupTestDb, teardownTestDb } = require("../helpers/db");
35
+
36
+ // audit_log stores metadata as a JSON string column; query() returns it
37
+ // as-is. Parse it for field assertions.
38
+ function _meta(row) {
39
+ if (row && typeof row.metadata === "string") {
40
+ try { return JSON.parse(row.metadata); } catch (_e) { return {}; }
41
+ }
42
+ return (row && row.metadata) || {};
43
+ }
44
+
45
+ // A scheduler stand-in whose create() returns a fake whose schedule()
46
+ // captures the spec.run closure. This lets us AWAIT the exact drill
47
+ // closure scheduleTest registers (the full restore+verify+audit body)
48
+ // to completion — the real scheduler's fire path is fire-and-forget.
49
+ function _makeCapturingScheduler() {
50
+ var captured = { specs: [] };
51
+ return {
52
+ captured: captured,
53
+ create: function () {
54
+ return {
55
+ schedule: function (spec) {
56
+ captured.specs.push(spec);
57
+ return spec;
58
+ },
59
+ start: function () {},
60
+ stop: function () {},
61
+ };
62
+ },
63
+ };
64
+ }
65
+
66
+ function _seedSealedDataDir() {
67
+ // Real bytes resembling the framework's encrypted-at-rest layout —
68
+ // an opaque db envelope + the sealed DEK. The drill restores + the
69
+ // operator verify hook confirms the restored bytes round-trip.
70
+ var dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "drill-data-"));
71
+ var dbEnc = Buffer.from("ENVELOPE-" + b.crypto.generateToken(48), "utf8");
72
+ var keyEnc = Buffer.from("SEALED-DEK-" + b.crypto.generateToken(32), "utf8");
73
+ fs.writeFileSync(path.join(dataDir, "db.enc"), dbEnc);
74
+ fs.writeFileSync(path.join(dataDir, "db.key.enc"), keyEnc);
75
+ return { dataDir: dataDir, dbEnc: dbEnc, keyEnc: keyEnc };
76
+ }
77
+
78
+ async function main() {
79
+ var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "drill-fw-"));
80
+ await setupTestDb(tmpDir);
81
+
82
+ var seeded = _seedSealedDataDir();
83
+ var storageRoot = fs.mkdtempSync(path.join(os.tmpdir(), "drill-store-"));
84
+ var restoreRoot = fs.mkdtempSync(path.join(os.tmpdir(), "drill-restore-"));
85
+
86
+ var storage = b.backup.diskStorage({ root: storageRoot });
87
+ var capSched = _makeCapturingScheduler();
88
+
89
+ var engine = b.backup.create({
90
+ dataDir: seeded.dataDir,
91
+ storage: storage,
92
+ passphrase: Buffer.from("operator-backup-passphrase-256bit-entropy-here"),
93
+ files: [
94
+ { relativePath: "db.enc", kind: "raw", required: true },
95
+ { relativePath: "db.key.enc", kind: "raw", required: true },
96
+ ],
97
+ vaultKeyJson: '{"version":1,"kid":"k1"}',
98
+ scheduler: capSched,
99
+ });
100
+
101
+ // ---- Produce a real, signed backup bundle --------------------------
102
+ var runSummary = await engine.run({ metadata: { reason: "drill-fixture" } });
103
+ check("backup.run produced a bundle id", typeof runSummary.bundleId === "string" && runSummary.bundleId.length > 0);
104
+ var bundles = await engine.list();
105
+ check("one bundle is in storage after run", bundles.length === 1);
106
+
107
+ // Sanity: the stored bundle's manifest is genuinely signed (the drill's
108
+ // signature-verify step is only meaningful if there is a signature).
109
+ var storedBundleId = bundles[0].bundleId;
110
+ var manifestOnDisk = JSON.parse(
111
+ fs.readFileSync(path.join(storageRoot, storedBundleId, "manifest.json"), "utf8"));
112
+ check("stored manifest carries a signature block",
113
+ manifestOnDisk.signature && typeof manifestOnDisk.signature.value === "string" &&
114
+ manifestOnDisk.signature.value.length > 0);
115
+ var signedFingerprint = manifestOnDisk.signature.fingerprint;
116
+
117
+ // =====================================================================
118
+ // PART 1 — PASSING DRILL via the captured closure (awaited to done)
119
+ // =====================================================================
120
+ var verifyCall = null;
121
+ var notifyCalls = [];
122
+ var schedResult = engine.scheduleTest({
123
+ cron: "0 3 * * 0",
124
+ restoreTo: restoreRoot,
125
+ posture: "hipaa",
126
+ verify: async function (ctx) {
127
+ // Operator-side verification: confirm the restored payload matches
128
+ // the original sealed bytes AND the framework handed us a parsed,
129
+ // signature-verified manifest + the outDir it restored into.
130
+ verifyCall = {
131
+ outDir: ctx.outDir,
132
+ bundleId: ctx.bundleId,
133
+ sigFingerprint: ctx.sigFingerprint,
134
+ manifestVersion: ctx.manifest && ctx.manifest.version,
135
+ dbEncMatch: false,
136
+ keyEncMatch: false,
137
+ outDirExists: fs.existsSync(ctx.outDir),
138
+ };
139
+ // The bundle stores ENCRYPTED file bytes under files/; decrypt-on-
140
+ // restore is the backup-crypto layer's job and is exercised by the
141
+ // bundle tests. Here the drill's contract is "restored bundle is
142
+ // present + manifest verifies"; the operator hook proves the bundle
143
+ // dir + manifest are real by reading back the manifest's file list
144
+ // and confirming the encrypted blobs exist on disk.
145
+ var allBlobsPresent = ctx.manifest.files.every(function (f) {
146
+ return fs.existsSync(path.join(ctx.outDir, f.encryptedPath));
147
+ });
148
+ verifyCall.allBlobsPresent = allBlobsPresent;
149
+ // Confirm the manifest describes the two files we backed up.
150
+ var rels = ctx.manifest.files.map(function (f) { return f.relativePath; }).sort();
151
+ verifyCall.dbEncMatch = rels.indexOf("db.enc") !== -1;
152
+ verifyCall.keyEncMatch = rels.indexOf("db.key.enc") !== -1;
153
+ return true;
154
+ },
155
+ notify: async function (info) { notifyCalls.push(info); },
156
+ });
157
+
158
+ check("scheduleTest returned an instance handle",
159
+ schedResult && typeof schedResult.name === "string" && schedResult.instance);
160
+ check("scheduleTest registered exactly one scheduler task",
161
+ capSched.captured.specs.length === 1);
162
+ var drillSpec = capSched.captured.specs[0];
163
+ check("the registered task carries the operator cron",
164
+ drillSpec.cron === "0 3 * * 0");
165
+ check("the registered task carries an async run closure",
166
+ typeof drillSpec.run === "function");
167
+
168
+ // Fire the real drill closure and await it to completion.
169
+ await drillSpec.run();
170
+
171
+ check("(1) drill restored the bundle into a staging dir under restoreTo",
172
+ verifyCall !== null && verifyCall.outDirExists === true &&
173
+ verifyCall.outDir.indexOf(restoreRoot) === 0);
174
+ check("(1b) restored staging dir carried the encrypted file blobs",
175
+ verifyCall.allBlobsPresent === true);
176
+ check("(2) drill verified the manifest signature before calling verify (fingerprint passed through)",
177
+ verifyCall.sigFingerprint === signedFingerprint &&
178
+ typeof verifyCall.sigFingerprint === "string" && verifyCall.sigFingerprint.length > 0);
179
+ check("(3) drill handed the operator a parsed manifest covering the backed-up files",
180
+ verifyCall.manifestVersion === 1 && verifyCall.dbEncMatch && verifyCall.keyEncMatch);
181
+ check("(3b) drill targeted the newest stored bundle",
182
+ verifyCall.bundleId === storedBundleId);
183
+
184
+ // (4) pass/fail recorded in the signed audit chain.
185
+ await b.audit.flush();
186
+ var passRows = await b.audit.query({ action: "backup.test.passed" });
187
+ check("(4) drill emitted exactly one backup.test.passed audit row", passRows.length === 1);
188
+ var passMeta = _meta(passRows[0]);
189
+ check("(4b) passed row records the bundle id + posture + fingerprint",
190
+ passMeta.bundleId === storedBundleId &&
191
+ passMeta.posture === "hipaa" &&
192
+ passMeta.fingerprint === signedFingerprint);
193
+ check("(4c) passed row outcome is success", passRows[0].outcome === "success");
194
+
195
+ check("notify hook fired with success outcome on a passing drill",
196
+ notifyCalls.length === 1 && notifyCalls[0].outcome === "success" &&
197
+ notifyCalls[0].bundleId === storedBundleId);
198
+
199
+ // The drill cleans staging by default — confirm it didn't leave the
200
+ // restore dir behind (regulator drills must not accumulate plaintext-
201
+ // restored bundles).
202
+ check("drill cleaned its staging dir after a passing run",
203
+ !fs.existsSync(verifyCall.outDir));
204
+
205
+ // =====================================================================
206
+ // PART 2 — PASSING DRILL via the GENUINE scheduler dispatch path
207
+ // =====================================================================
208
+ // Re-run the drill through a real b.scheduler instance + its _fireOnce
209
+ // test hook so the genuine schedule()/fire path is exercised, not just
210
+ // the captured closure.
211
+ b.scheduler._resetForTest && b.scheduler._resetForTest();
212
+ var realVerifyHit = false;
213
+ var realEngine = b.backup.create({
214
+ dataDir: seeded.dataDir,
215
+ storage: storage,
216
+ passphrase: Buffer.from("operator-backup-passphrase-256bit-entropy-here"),
217
+ files: [
218
+ { relativePath: "db.enc", kind: "raw", required: true },
219
+ { relativePath: "db.key.enc", kind: "raw", required: true },
220
+ ],
221
+ vaultKeyJson: '{"version":1,"kid":"k1"}',
222
+ scheduler: b.scheduler,
223
+ });
224
+ var realDrill = realEngine.scheduleTest({
225
+ name: "drill.real.path",
226
+ cron: "0 4 * * 0",
227
+ restoreTo: restoreRoot,
228
+ posture: "hipaa",
229
+ verify: async function (ctx) {
230
+ realVerifyHit = ctx && typeof ctx.outDir === "string" &&
231
+ ctx.sigFingerprint === signedFingerprint;
232
+ return true;
233
+ },
234
+ });
235
+ // Fire via the real scheduler dispatch. _fireOnce is fire-and-forget;
236
+ // wait for the drill's audit row to appear.
237
+ realDrill.instance._fireOnce("drill.real.path");
238
+ await waitUntil(async function () {
239
+ await b.audit.flush();
240
+ var rows = await b.audit.query({ action: "backup.test.passed" });
241
+ return rows.length >= 2;
242
+ }, { timeoutMs: 5000, label: "real-scheduler drill: backup.test.passed lands" });
243
+ check("genuine scheduler dispatch ran the drill verify hook", realVerifyHit === true);
244
+
245
+ // =====================================================================
246
+ // PART 3 — FAILING DRILL: tamper the stored bundle's manifest signature
247
+ // =====================================================================
248
+ // Flip a byte in the signature value of the stored manifest so the
249
+ // signature no longer verifies. The drill MUST report failure — not a
250
+ // false pass — and MUST NOT call the operator verify hook (verification
251
+ // gates the hand-off).
252
+ var manifestPath = path.join(storageRoot, storedBundleId, "manifest.json");
253
+ var tampered = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
254
+ var sigBuf = Buffer.from(tampered.signature.value, "base64");
255
+ sigBuf[0] = sigBuf[0] ^ 0xff; // corrupt the first signature byte
256
+ tampered.signature.value = sigBuf.toString("base64");
257
+ fs.writeFileSync(manifestPath, JSON.stringify(tampered, null, 2) + "\n");
258
+
259
+ var failVerifyCalled = false;
260
+ var failNotify = [];
261
+ var capSched2 = _makeCapturingScheduler();
262
+ var failEngine = b.backup.create({
263
+ dataDir: seeded.dataDir,
264
+ storage: storage,
265
+ passphrase: Buffer.from("operator-backup-passphrase-256bit-entropy-here"),
266
+ files: [
267
+ { relativePath: "db.enc", kind: "raw", required: true },
268
+ { relativePath: "db.key.enc", kind: "raw", required: true },
269
+ ],
270
+ vaultKeyJson: '{"version":1,"kid":"k1"}',
271
+ scheduler: capSched2,
272
+ });
273
+ failEngine.scheduleTest({
274
+ cron: "0 5 * * 0",
275
+ restoreTo: restoreRoot,
276
+ posture: "hipaa",
277
+ verify: async function () { failVerifyCalled = true; return true; },
278
+ notify: async function (info) { failNotify.push(info); },
279
+ });
280
+ await capSched2.captured.specs[0].run();
281
+
282
+ check("tampered-signature drill did NOT call the operator verify hook (verify gates hand-off)",
283
+ failVerifyCalled === false);
284
+
285
+ await b.audit.flush();
286
+ var failRows = await b.audit.query({ action: "backup.test.failed" });
287
+ check("failing drill emitted a backup.test.failed audit row (no false pass)",
288
+ failRows.length === 1);
289
+ check("failed row outcome is failure", failRows[0].outcome === "failure");
290
+ var failMeta = _meta(failRows[0]);
291
+ check("failed row reason names the signature failure",
292
+ typeof failMeta.reason === "string" && /signature/i.test(failMeta.reason));
293
+
294
+ // Critically: the passing-row count must NOT have grown — a tampered
295
+ // bundle must never be recorded as a passed drill.
296
+ var passRowsAfterTamper = await b.audit.query({ action: "backup.test.passed" });
297
+ check("tampering did not produce an additional backup.test.passed row",
298
+ passRowsAfterTamper.length === 2);
299
+
300
+ check("notify hook fired with failure outcome on the tampered drill",
301
+ failNotify.length === 1 && failNotify[0].outcome === "failure" &&
302
+ /signature/i.test(failNotify[0].reason || ""));
303
+
304
+ // ---- cleanup -------------------------------------------------------
305
+ await teardownTestDb(tmpDir);
306
+ try { fs.rmSync(seeded.dataDir, { recursive: true, force: true }); } catch (_e) {}
307
+ try { fs.rmSync(storageRoot, { recursive: true, force: true }); } catch (_e) {}
308
+ try { fs.rmSync(restoreRoot, { recursive: true, force: true }); } catch (_e) {}
309
+
310
+ // check() throws on the first failed assertion, so reaching here means
311
+ // every assertion passed.
312
+ console.log("backup-scheduletest-drill: OK — " + helpers.getChecks() + " checks passed");
313
+ }
314
+
315
+ main().catch(function (e) {
316
+ console.error("FAIL: backup-scheduletest-drill threw:", e && e.stack || e);
317
+ process.exitCode = 1;
318
+ });