@blamejs/blamejs-shop 0.4.31 → 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 (336) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/vendor/MANIFEST.json +392 -278
  4. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  5. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  6. package/lib/vendor/blamejs/.gitignore +6 -0
  7. package/lib/vendor/blamejs/CHANGELOG.md +26 -0
  8. package/lib/vendor/blamejs/MIGRATING.md +43 -0
  9. package/lib/vendor/blamejs/README.md +8 -6
  10. package/lib/vendor/blamejs/SECURITY.md +19 -3
  11. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  12. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  13. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  14. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  15. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  16. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  17. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  18. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  19. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  20. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  21. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  22. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  23. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  24. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  25. package/lib/vendor/blamejs/index.js +4 -0
  26. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  27. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  28. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  29. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  30. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  31. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  32. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  33. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  34. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  35. package/lib/vendor/blamejs/lib/audit.js +259 -123
  36. package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
  37. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  38. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  39. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
  40. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  41. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  42. package/lib/vendor/blamejs/lib/cache.js +174 -105
  43. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  44. package/lib/vendor/blamejs/lib/cli.js +19 -14
  45. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  46. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  47. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  48. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  49. package/lib/vendor/blamejs/lib/compliance.js +206 -4
  50. package/lib/vendor/blamejs/lib/consent.js +82 -29
  51. package/lib/vendor/blamejs/lib/constants.js +27 -11
  52. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  53. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  54. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  55. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  56. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  57. package/lib/vendor/blamejs/lib/db.js +249 -99
  58. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  59. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  60. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  61. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  62. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  63. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  64. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  65. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  66. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  67. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  68. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  69. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  70. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  71. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  72. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  73. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  74. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  75. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  76. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  77. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  78. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  79. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  80. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  81. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  82. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  83. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  84. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  85. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  86. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  87. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  88. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  89. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  90. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  91. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  92. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  93. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  94. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  95. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  96. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  97. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  98. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  99. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  100. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  101. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  102. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  103. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  104. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  105. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  106. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  107. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  108. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  109. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  110. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  111. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  112. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  113. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  114. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  115. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  116. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  117. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  118. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  119. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  120. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  121. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  122. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  123. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  124. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  125. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  126. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  127. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  128. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  129. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  131. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  132. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  133. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  134. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  135. package/lib/vendor/blamejs/lib/mail.js +8 -4
  136. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  137. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  138. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  139. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  140. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  141. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  142. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  143. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  144. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  145. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  146. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  147. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  148. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  149. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  150. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  151. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  152. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  153. package/lib/vendor/blamejs/lib/observability.js +124 -0
  154. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  155. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  156. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  157. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  158. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  159. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  160. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  161. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  162. package/lib/vendor/blamejs/lib/queue.js +7 -0
  163. package/lib/vendor/blamejs/lib/redact.js +68 -11
  164. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  165. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  166. package/lib/vendor/blamejs/lib/retention.js +101 -40
  167. package/lib/vendor/blamejs/lib/router.js +212 -5
  168. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  169. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  170. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  171. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  172. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  173. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  174. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  175. package/lib/vendor/blamejs/lib/scheduler.js +35 -12
  176. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  177. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  178. package/lib/vendor/blamejs/lib/session.js +175 -77
  179. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  180. package/lib/vendor/blamejs/lib/sse.js +26 -0
  181. package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
  182. package/lib/vendor/blamejs/lib/static.js +177 -34
  183. package/lib/vendor/blamejs/lib/subject.js +96 -49
  184. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  185. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  186. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  187. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  188. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  189. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  190. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  191. package/lib/vendor/blamejs/package.json +2 -2
  192. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  193. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  194. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  195. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  200. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  201. package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
  202. package/lib/vendor/blamejs/scripts/release.js +398 -38
  203. package/lib/vendor/blamejs/test/00-primitives.js +117 -0
  204. package/lib/vendor/blamejs/test/10-state.js +140 -14
  205. package/lib/vendor/blamejs/test/20-db.js +65 -2
  206. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  207. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  208. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  209. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  210. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  211. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  212. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  213. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  214. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  215. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  216. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  217. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  220. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  221. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  222. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  223. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  224. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  225. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  226. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  227. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  228. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  229. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  230. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  231. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  232. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  233. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  234. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  235. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  236. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  237. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  238. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  240. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  248. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
  249. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  250. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  251. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  253. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  254. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  256. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  258. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  259. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  270. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  271. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  272. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  273. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  275. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  277. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  278. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  281. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  282. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  283. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  285. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  291. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  302. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  308. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  310. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  311. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  312. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  313. package/lib/vendor/blamejs/test/smoke.js +79 -21
  314. package/package.json +1 -1
  315. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  316. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  317. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  318. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  319. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  320. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  321. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  322. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  323. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  324. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  325. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  326. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  327. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  328. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  329. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  330. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  331. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  332. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  333. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  334. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  335. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  336. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ /**
3
+ * b.audit.safeEmit scrubs secrets before the tamper-evident signed chain.
4
+ *
5
+ * safeEmit runs in request hot paths where operators routinely pass
6
+ * `metadata: { detail: e.message }` from a caught error — and those error
7
+ * strings carry DB connection strings (with passwords), JWTs, AWS
8
+ * access-key ids, PEM/private-key blocks, and SSNs. The documented
9
+ * guarantee (lib/audit.js safeEmit + the @primitive block) is that
10
+ * actor / reason / metadata pass through b.redact.redact() so those
11
+ * shapes are replaced with markers BEFORE the row is hash-chained and
12
+ * SLH-DSA-checkpointed. Once a secret lands in the append-only signed
13
+ * chain it cannot be UPDATE/DELETE'd out — so redaction has to happen at
14
+ * the write boundary, not after.
15
+ *
16
+ * The prior coverage only asserted safeEmit "never throws". This drives a
17
+ * secret-laden record end-to-end through the REAL path — full vault wrap,
18
+ * at-rest-encrypted db, audit hash chain — then reads the stored row back
19
+ * through b.audit.query (which UNSEALS the sealed columns: actorUserId /
20
+ * actorIp / reason / metadata). The unsealed plaintext is exactly what a
21
+ * forensic reader / auditor sees, so asserting the secret SUBSTRINGS are
22
+ * absent there (and replaced by redact markers) proves the guarantee
23
+ * against the actual stored ciphertext, not an in-memory copy.
24
+ *
25
+ * The secret values are placed under NON-sensitive field names (note /
26
+ * detail / dbDsn / personId / signingPem / accessKeyId) so the proof
27
+ * rests on redact's VALUE-SHAPE detectors (the real claim — a leaked
28
+ * secret is caught by its shape regardless of how the operator named the
29
+ * field), not on a lucky field-name match. The AWS *secret* access key
30
+ * (40-char, no value-shape) is placed under a `secret`-named field to
31
+ * exercise the field-name pass too.
32
+ *
33
+ * Run standalone: `node test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js`
34
+ * Or via smoke: `node test/smoke.js`
35
+ */
36
+
37
+ var helpers = require("../helpers");
38
+ var b = helpers.b;
39
+ var fs = helpers.fs;
40
+ var os = helpers.os;
41
+ var path = helpers.path;
42
+ var check = helpers.check;
43
+ var waitUntil = helpers.waitUntil;
44
+ var setupTestDb = helpers.setupTestDb;
45
+ var teardownTestDb = helpers.teardownTestDb;
46
+
47
+ function _tmp() { return fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-audit-redact-")); }
48
+
49
+ // Clearly-fake but shape-valid secrets. Each is the WHOLE value of its
50
+ // field so redact's anchored value-shape detectors fire as designed.
51
+ var SECRETS = {
52
+ // postgres://user:pass@host/db — connection-string credential leak.
53
+ connString: "postgres://app_user:s3cr3t-Pw0rd@db.internal.example:5432/orders",
54
+ connPw: "s3cr3t-Pw0rd",
55
+ // JWS compact triplet (eyJ... . eyJ... . sig).
56
+ jwt: "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbGljZSIsImFkbWluIjp0cnVlfQ.c2lnbmF0dXJlLWJ5dGVz",
57
+ // AWS access-key id shape (AKIA + 16 upper-alnum). Value-shape detector.
58
+ awsAccessKeyId: "AKIAIOSFODNN7EXAMPLE",
59
+ // AWS secret access key (40 base64-ish chars) — no value-shape; relies
60
+ // on the `secret`-named field-name pass.
61
+ awsSecretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
62
+ // US SSN shape.
63
+ ssn: "123-45-6789",
64
+ // PEM private-key block.
65
+ pem: "-----BEGIN PRIVATE KEY-----\nMIIBVgIBADANBgkqhkiG9w0BAQ\n-----END PRIVATE KEY-----",
66
+ };
67
+
68
+ // A non-secret value that MUST survive verbatim — proves redaction is
69
+ // targeted, not a blanket wipe of the whole event surface.
70
+ var SURVIVOR = "order-shipped-to-warehouse-7";
71
+
72
+ // Read every audit_log row that carries our marker action, through the
73
+ // real unseal path. metadata/reason/actor* are sealed columns, so this
74
+ // returns decrypted plaintext — exactly what an auditor reads.
75
+ async function _storedRows(action) {
76
+ return await b.audit.query({ action: action });
77
+ }
78
+
79
+ // Flatten an unsealed row to a single searchable string. metadata is
80
+ // stored JSON-stringified; actor*/reason come back as plain strings.
81
+ function _rowText(row) {
82
+ return JSON.stringify(row);
83
+ }
84
+
85
+ async function testSafeEmitRedactsSecretsInSignedChain() {
86
+ var tmpDir = _tmp();
87
+ await setupTestDb(tmpDir);
88
+ try {
89
+ var ACTION = "system.error.captured";
90
+
91
+ // Operator's hot-path emission: a caught-error payload packed with
92
+ // secrets across actor, reason, and metadata.
93
+ b.audit.safeEmit({
94
+ action: ACTION,
95
+ outcome: "failure",
96
+ actor: {
97
+ userId: "u-77",
98
+ // An actor field carrying a leaked connection string.
99
+ ip: "10.0.0.5",
100
+ },
101
+ reason: "db connect failed: " + SECRETS.connString,
102
+ metadata: {
103
+ // Non-sensitive field names → value-shape detectors must fire.
104
+ note: "thrown while reconnecting",
105
+ dbDsn: SECRETS.connString,
106
+ // Neutral field name (NOT in the sensitive-field list — avoids
107
+ // `bearer`/`token`/`auth`) so the JWT VALUE-SHAPE detector is
108
+ // what fires, proving shape-based catch rather than name-based.
109
+ compactJws: SECRETS.jwt,
110
+ personId: SECRETS.ssn,
111
+ accessKeyId: SECRETS.awsAccessKeyId,
112
+ signingPem: SECRETS.pem,
113
+ // field-name pass: `secret` substring.
114
+ awsSecretAccessKey: SECRETS.awsSecretKey,
115
+ // The survivor — a plain operational breadcrumb.
116
+ orderRef: SURVIVOR,
117
+ },
118
+ });
119
+
120
+ // Drain the AsyncHandler so the row is durably in the signed chain.
121
+ await b.audit.flush();
122
+ await waitUntil(async function () {
123
+ return (await _storedRows(ACTION)).length >= 1;
124
+ }, { timeoutMs: 5000, label: "safeEmit: secret-laden row reaches audit_log" });
125
+
126
+ var rows = await _storedRows(ACTION);
127
+ check("exactly one " + ACTION + " row landed in the chain", rows.length === 1);
128
+ var row = rows[0];
129
+ var text = _rowText(row);
130
+
131
+ // ---- Each secret SUBSTRING must be ABSENT from the stored row. ----
132
+ // This is read back through the real unseal path, so it is the
133
+ // forensic/auditor view of the immutable chain — not an in-memory copy.
134
+ check("connection-string password absent from stored chain row",
135
+ text.indexOf(SECRETS.connPw) === -1);
136
+ check("full connection string absent from stored chain row",
137
+ text.indexOf(SECRETS.connString) === -1);
138
+ check("JWT absent from stored chain row",
139
+ text.indexOf(SECRETS.jwt) === -1);
140
+ check("AWS access-key id absent from stored chain row",
141
+ text.indexOf(SECRETS.awsAccessKeyId) === -1);
142
+ check("AWS secret access key absent from stored chain row",
143
+ text.indexOf(SECRETS.awsSecretKey) === -1);
144
+ check("SSN absent from stored chain row",
145
+ text.indexOf(SECRETS.ssn) === -1);
146
+ check("PEM private-key body absent from stored chain row",
147
+ text.indexOf("MIIBVgIBADANBgkqhkiG9w0BAQ") === -1);
148
+ check("PEM BEGIN header absent from stored chain row",
149
+ text.indexOf("-----BEGIN PRIVATE KEY-----") === -1);
150
+
151
+ // ---- Markers must be PRESENT (redaction happened, value wasn't just
152
+ // dropped/blanked into ambiguity). ----
153
+ check("connection-string redact marker present",
154
+ text.indexOf("[REDACTED-CONN-STRING]") !== -1);
155
+ check("JWT redact marker present",
156
+ text.indexOf("[REDACTED-JWT]") !== -1);
157
+ check("AWS access-key redact marker present",
158
+ text.indexOf("[REDACTED-AWS-KEY]") !== -1);
159
+ check("SSN redact marker present",
160
+ text.indexOf("[REDACTED-SSN]") !== -1);
161
+ check("PEM redact marker present",
162
+ text.indexOf("[REDACTED-PEM]") !== -1);
163
+ // The 40-char AWS secret has no value-shape; the `secret`-named field
164
+ // is replaced with the default marker.
165
+ check("default redact marker present (field-name pass on awsSecretAccessKey)",
166
+ text.indexOf(b.redact.MARKER) !== -1);
167
+
168
+ // ---- The non-secret breadcrumb survives verbatim. ----
169
+ check("non-secret orderRef survives redaction verbatim",
170
+ text.indexOf(SURVIVOR) !== -1);
171
+
172
+ // ---- The chain still verifies end-to-end (the redacted row is a
173
+ // valid, signed, hash-chained member — redaction at the write
174
+ // boundary didn't corrupt the chain). ----
175
+ var v = await b.audit.verify();
176
+ check("audit chain verifies after the redacted append", v && v.ok === true);
177
+ } finally {
178
+ await teardownTestDb(tmpDir);
179
+ }
180
+ }
181
+
182
+ async function run() {
183
+ await testSafeEmitRedactsSecretsInSignedChain();
184
+ }
185
+
186
+ module.exports = { run: run };
187
+
188
+ if (require.main === module) {
189
+ run().then(function () { console.log("OK"); })
190
+ // Re-throw rather than console.error the error object: a DB-setup
191
+ // failure can carry passphrase-derived material on the error, and
192
+ // logging it would be clear-text logging of sensitive data
193
+ // (CWE-312). The non-zero exit + thrown stack still surface the
194
+ // failure to the runner.
195
+ .catch(function (e) { process.exitCode = 1; throw e; });
196
+ }
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+
3
+ // SMOKE_RUN_SOLO — the smoke runner (test/smoke.js) runs this file ALONE
4
+ // with the whole machine instead of inside the parallel layer-0 pool.
5
+ // The test exercises the encrypted-at-rest audit-signing rotation path:
6
+ // setupTestDb (db.enc + sealed signing key), generating a fresh Ed25519
7
+ // keypair, rotateSigningKey() re-sealing the new key and archiving the
8
+ // old one to a *.history-* file, plus two checkpoints anchored over a
9
+ // flushed chain — every step a blocking key-file / db re-encryption
10
+ // fsync on the real data dir. Under SMOKE_PARALLEL=64 on a virtualized
11
+ // filesystem (the Dropbox-backed working tree, Docker-Desktop FS-virt)
12
+ // 64 sibling forks contend for fsync on the same volume and these
13
+ // synchronous writes overrun the per-file watchdog. There is no single
14
+ // async event to poll past — the contention is whole-process I/O, so the
15
+ // file runs solo and finishes in its normal time. Passes alone and at
16
+ // SMOKE_PARALLEL=16.
17
+
18
+ /**
19
+ * Audit-signing key rotation must preserve historical verifiability.
20
+ *
21
+ * The documented contract (lib/audit-sign.js):
22
+ * - rotateSigningKey() copies the OLD sealed/plaintext key file to a
23
+ * timestamped `*.history-<iso>-<fp>` path "so historical checkpoints
24
+ * can still be verified by readers that load the old key" and returns
25
+ * `historyPath` "so external tools can verify pre-rotation checkpoints
26
+ * later".
27
+ * - verify(payload, signature, publicKeyPem) accepts a third arg to use
28
+ * a HISTORICAL key "so a checkpoint signed years earlier still verifies
29
+ * after rotation".
30
+ *
31
+ * The promise an operator reads from those docstrings: rotating the
32
+ * audit-signing key does NOT strand the audit history. A checkpoint that
33
+ * was anchored under key K1 still verifies after the key is rotated to K2.
34
+ *
35
+ * This test drives the real production path:
36
+ * 1. Build the audit chain, emit records, anchor a checkpoint under K1.
37
+ * 2. b.auditSign.rotateSigningKey() -> live K2 keypair, K1 archived to
38
+ * the documented *.history-* path.
39
+ * 3. Emit more records, anchor a second checkpoint under K2.
40
+ * 4. b.audit.verifyCheckpoints() must report BOTH checkpoints Good.
41
+ *
42
+ * If verifyCheckpoints rejects the K1 checkpoint (fingerprint mismatch
43
+ * against the now-current K2 key, with no key-history lookup), that is the
44
+ * advertised-but-missing behaviour — historical verifiability is broken by
45
+ * rotation. The test asserts the CORRECT behaviour (both verify) so it
46
+ * FAILS and exposes the gap.
47
+ *
48
+ * It also exercises the documented remediation primitive reSignAll(): an
49
+ * operator who walks every checkpoint through reSignAll() after rotating
50
+ * should be able to re-sign K1-anchored checkpoints under K2 and have
51
+ * verifyCheckpoints pass. That asserts the escape hatch the docstrings
52
+ * point to actually closes the gap.
53
+ */
54
+
55
+ var helpers = require("../helpers");
56
+ var b = helpers.b;
57
+ var check = helpers.check;
58
+ var fs = helpers.fs;
59
+ var os = helpers.os;
60
+ var path = helpers.path;
61
+ var setupTestDb = helpers.setupTestDb;
62
+ var teardownTestDb = helpers.teardownTestDb;
63
+ var setTestPassphraseEnv = helpers.setTestPassphraseEnv;
64
+
65
+ var clusterStorage = require("../../lib/cluster-storage");
66
+
67
+ // Seed `count` durable audit rows through the real chain writer, then
68
+ // flush so every row is on disk before we anchor a checkpoint over the tip.
69
+ async function _seedAuditRows(count, tag) {
70
+ b.audit.registerNamespace("test");
71
+ for (var i = 0; i < count; i++) {
72
+ await b.audit.record({
73
+ actor: { userId: "u-" + tag + "-" + i },
74
+ action: "test.seeded",
75
+ outcome: "success",
76
+ metadata: { i: i, tag: tag },
77
+ });
78
+ }
79
+ await b.audit.flush();
80
+ }
81
+
82
+ // The audit checkpoint store keeps the canonical payload triple
83
+ // (atMonotonicCounter, atRowHash, createdAt) plus the signature +
84
+ // publicKeyFingerprint. reSignAll() needs each checkpoint's payload bytes
85
+ // and old signature; rebuild them straight from the stored rows the same
86
+ // way verifyCheckpoints does.
87
+ function _checkpointPayloadFor(row) {
88
+ return Buffer.from(
89
+ b.audit.CHECKPOINT_FORMAT + "\n" +
90
+ String(Number(row.atMonotonicCounter)) + "\n" +
91
+ row.atRowHash + "\n" +
92
+ String(Number(row.createdAt)),
93
+ "utf8"
94
+ );
95
+ }
96
+
97
+ async function run() {
98
+ var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-audit-keyrot-"));
99
+ try {
100
+ await setupTestDb(dir);
101
+
102
+ // ---- Anchor a checkpoint under the first signing key (K1). ----
103
+ var k1Fp = b.auditSign.getPublicKeyFingerprint();
104
+ var k1Pub = b.auditSign.getPublicKey();
105
+ check("K1 fingerprint is present", typeof k1Fp === "string" && k1Fp.length > 0);
106
+
107
+ await _seedAuditRows(4, "k1");
108
+ var ckpt1 = await b.audit.checkpoint();
109
+ check("checkpoint #1 anchored under K1", ckpt1 && ckpt1.atMonotonicCounter >= 4);
110
+ check("checkpoint #1 recorded K1 fingerprint", ckpt1.publicKeyFingerprint === k1Fp);
111
+
112
+ // Baseline: before any rotation, verifyCheckpoints is clean.
113
+ var baseline = await b.audit.verifyCheckpoints();
114
+ check("baseline verifyCheckpoints ok (pre-rotation)", baseline.ok === true);
115
+ check("baseline verified the K1 checkpoint", baseline.checkpointsVerified >= 1);
116
+
117
+ // ---- Rotate the signing key K1 -> K2. ----
118
+ // Wrapped mode re-derives the audit-signing passphrase to re-seal the
119
+ // new key; the passphrase source strips env after boot reads it, so
120
+ // re-supply it the same way an operator would for a rotation.
121
+ setTestPassphraseEnv();
122
+ var rot = await b.auditSign.rotateSigningKey();
123
+ var k2Fp = b.auditSign.getPublicKeyFingerprint();
124
+ var k2Pub = b.auditSign.getPublicKey();
125
+ check("rotation changed the key material", k2Fp !== k1Fp);
126
+ check("rotation reports the previous fingerprint", rot.previousFingerprint === k1Fp);
127
+ check("rotation reports the new fingerprint", rot.newFingerprint === k2Fp);
128
+
129
+ // The docstring promises the OLD key is archived to *.history-* so
130
+ // historical checkpoints can still be verified later. Confirm the
131
+ // history artifact the rotation advertised actually landed on disk.
132
+ check("rotation returned a historyPath", typeof rot.historyPath === "string" && rot.historyPath.length > 0);
133
+ check("the advertised history key file exists on disk", fs.existsSync(rot.historyPath));
134
+
135
+ // The verify-time resolver finds the rotated-out K1 public key (unsealed
136
+ // public-key history) and the live K2 key, but not an unknown fingerprint.
137
+ check("getPublicKeyByFingerprint resolves the rotated-out K1 key",
138
+ b.auditSign.getPublicKeyByFingerprint(k1Fp) === k1Pub);
139
+ check("getPublicKeyByFingerprint resolves the live K2 key",
140
+ b.auditSign.getPublicKeyByFingerprint(k2Fp) === k2Pub);
141
+ check("getPublicKeyByFingerprint returns null for an unknown fingerprint",
142
+ b.auditSign.getPublicKeyByFingerprint("0".repeat(128)) === null);
143
+
144
+ // ---- Anchor a second checkpoint under the new key (K2). ----
145
+ await _seedAuditRows(3, "k2");
146
+ var ckpt2 = await b.audit.checkpoint();
147
+ check("checkpoint #2 anchored under K2", ckpt2 && ckpt2.atMonotonicCounter > ckpt1.atMonotonicCounter);
148
+ check("checkpoint #2 recorded K2 fingerprint", ckpt2.publicKeyFingerprint === k2Fp);
149
+
150
+ // Sanity: the two checkpoints really were signed under DIFFERENT keys,
151
+ // both signatures valid under their own public key. This proves the
152
+ // history is genuinely cross-key, so the verify path below is the real
153
+ // test (not an artifact of identical keys).
154
+ var rows = await clusterStorage.executeAll(
155
+ "SELECT * FROM audit_checkpoints ORDER BY atMonotonicCounter ASC"
156
+ );
157
+ check("two checkpoints are stored", rows.length === 2);
158
+ var c1 = rows[0], c2 = rows[1];
159
+ var p1 = _checkpointPayloadFor(c1);
160
+ var p2 = _checkpointPayloadFor(c2);
161
+ var s1 = Buffer.isBuffer(c1.signature) ? c1.signature : Buffer.from(c1.signature);
162
+ var s2 = Buffer.isBuffer(c2.signature) ? c2.signature : Buffer.from(c2.signature);
163
+ check("K1 checkpoint verifies under K1's archived public key",
164
+ b.auditSign.verify(p1, s1, k1Pub) === true);
165
+ check("K1 checkpoint does NOT verify under K2 (genuinely cross-key)",
166
+ b.auditSign.verify(p1, s1, k2Pub) === false);
167
+ check("K2 checkpoint verifies under K2's current public key",
168
+ b.auditSign.verify(p2, s2, k2Pub) === true);
169
+
170
+ // ---- The contract under test: BOTH checkpoints must verify after
171
+ // rotation. ----
172
+ // verifyCheckpoints must walk both the K1- and K2-anchored
173
+ // checkpoints and report them Good — the K1 checkpoint resolved
174
+ // through the documented key-history, the K2 checkpoint through the
175
+ // current key.
176
+ var afterRotation = await b.audit.verifyCheckpoints();
177
+ check("after rotation: verifyCheckpoints ok (K1 history preserved)",
178
+ afterRotation.ok === true);
179
+ check("after rotation: BOTH checkpoints verified",
180
+ afterRotation.checkpointsVerified === 2);
181
+ check("after rotation: no break reported",
182
+ afterRotation.breakAt === undefined && !afterRotation.reason);
183
+
184
+ console.log("OK — audit signing-key rotation preserves historical verifiability (" +
185
+ helpers.getChecks() + " checks)");
186
+ } finally {
187
+ await teardownTestDb(dir);
188
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_e) {}
189
+ }
190
+ }
191
+
192
+ module.exports = { run: run };
193
+
194
+ if (require.main === module) {
195
+ run().then(function () { process.exit(0); })
196
+ .catch(function (err) { process.exitCode = 1; throw err; });
197
+ }
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ /**
3
+ * b.auditTools.verifyBundle — tamper detection on a REAL archive bundle.
4
+ *
5
+ * The clean path only runs incidentally inside purge(); the only other
6
+ * bundle test seeds fake dummy hashes and never drives verifyBundle, so
7
+ * the rows-checksum-mismatch (rows.enc / manifest checksum) and
8
+ * checkpoint-signature-failed branches were unproven.
9
+ *
10
+ * This test builds a real bundle the production way: seed audit rows
11
+ * through the chain writer, anchor a covering SLH-DSA checkpoint, then
12
+ * archive() to disk. It first proves verifyBundle(clean) -> { ok: true },
13
+ * then for each independent tamper proves the bundle is REJECTED — either
14
+ * a typed read-time throw or { ok: false }:
15
+ *
16
+ * (1) flip a byte in rows.enc -> checksum guard
17
+ * (2) corrupt manifest.checksum.rowsSha3_512 -> manifest checksum guard
18
+ * (3) corrupt the covering checkpoint's
19
+ * SLH-DSA signature (manifest checksum kept
20
+ * consistent so the signature branch is the
21
+ * thing under test) -> signature verify
22
+ *
23
+ * Every tamper operates on a fresh copy of the clean bundle so the
24
+ * cases stay independent and a missed catch is unambiguous.
25
+ */
26
+
27
+ var helpers = require("../helpers");
28
+ var b = helpers.b;
29
+ var check = helpers.check;
30
+ var fs = helpers.fs;
31
+ var os = helpers.os;
32
+ var path = helpers.path;
33
+ var setupTestDb = helpers.setupTestDb;
34
+ var teardownTestDb = helpers.teardownTestDb;
35
+
36
+ var backupCrypto = require("../../lib/backup/crypto");
37
+
38
+ var PASS = Buffer.from("operator-bundle-passphrase-not-secret");
39
+
40
+ async function _seedAuditRows(count) {
41
+ b.audit.registerNamespace("test");
42
+ for (var i = 0; i < count; i++) {
43
+ await b.audit.record({
44
+ actor: { userId: "u-" + i },
45
+ action: "test.seeded",
46
+ outcome: "success",
47
+ metadata: { i: i },
48
+ });
49
+ }
50
+ // Drain the chain writer so every row is durable before we archive.
51
+ await b.audit.flush();
52
+ }
53
+
54
+ // Recursively copy a bundle directory so each tamper works on a fresh,
55
+ // independent clean bundle.
56
+ function _copyDir(src, dest) {
57
+ fs.mkdirSync(dest, { recursive: true });
58
+ var entries = fs.readdirSync(src, { withFileTypes: true });
59
+ for (var i = 0; i < entries.length; i++) {
60
+ var ent = entries[i];
61
+ var s = path.join(src, ent.name);
62
+ var d = path.join(dest, ent.name);
63
+ if (ent.isDirectory()) _copyDir(s, d);
64
+ else fs.copyFileSync(s, d);
65
+ }
66
+ }
67
+
68
+ // Drive verifyBundle and normalize the two legitimate rejection shapes
69
+ // (typed read-time throw OR { ok:false }) into a single descriptor so
70
+ // each tamper case can assert "rejected" uniformly.
71
+ async function _verify(dir) {
72
+ try {
73
+ var res = await b.auditTools.verifyBundle({ in: dir, passphrase: PASS });
74
+ return { rejected: res.ok === false, threw: false, ok: res.ok, reason: res.reason, kind: res.kind, result: res };
75
+ } catch (e) {
76
+ return { rejected: true, threw: true, code: e && e.code, message: e && e.message };
77
+ }
78
+ }
79
+
80
+ async function run() {
81
+ var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-vb-tamper-"));
82
+ try {
83
+ await setupTestDb(dir);
84
+ await _seedAuditRows(6);
85
+ var ckpt = await b.audit.checkpoint();
86
+ check("a covering checkpoint was anchored", ckpt && ckpt.atMonotonicCounter >= 6);
87
+
88
+ var cleanDir = path.join(dir, "bundles", "archive-clean");
89
+ var arch = await b.auditTools.archive({
90
+ before: Date.now() + 60000,
91
+ out: cleanDir,
92
+ passphrase: PASS,
93
+ });
94
+ check("archive produced an archive-kind bundle", arch.manifest.kind === "archive");
95
+ check("archive carried the seeded rows", arch.rowCount >= 6);
96
+
97
+ // The bundle must physically be the 3-file archive shape.
98
+ check("clean bundle has rows.enc", fs.existsSync(path.join(cleanDir, "rows.enc")));
99
+ check("clean bundle has checkpoint.enc", fs.existsSync(path.join(cleanDir, "checkpoint.enc")));
100
+ check("clean bundle has manifest.json", fs.existsSync(path.join(cleanDir, "manifest.json")));
101
+
102
+ // ---- Baseline: the clean bundle verifies ok. ----
103
+ var clean = await _verify(cleanDir);
104
+ check("clean bundle: verifyBundle -> { ok: true }", clean.threw === false && clean.ok === true);
105
+ check("clean bundle: kind is archive", clean.kind === "archive");
106
+ check("clean bundle: rowsVerified covers the slice", clean.result.rowsVerified >= 6);
107
+
108
+ // ---- Tamper 1: flip a byte in rows.enc. ----
109
+ var t1 = path.join(dir, "bundles", "tamper-rows");
110
+ _copyDir(cleanDir, t1);
111
+ var rowsPath = path.join(t1, "rows.enc");
112
+ var rowsBuf = fs.readFileSync(rowsPath);
113
+ // Flip the last byte (inside the ciphertext / auth tag, well past the
114
+ // nonce) so the manifest checksum over the file no longer matches.
115
+ var before1 = rowsBuf[rowsBuf.length - 1];
116
+ rowsBuf[rowsBuf.length - 1] = before1 ^ 0xff;
117
+ fs.writeFileSync(rowsPath, rowsBuf);
118
+ check("tamper-rows: rows.enc byte actually changed",
119
+ fs.readFileSync(rowsPath)[rowsBuf.length - 1] !== before1);
120
+ var r1 = await _verify(t1);
121
+ check("tamper-rows: bundle is REJECTED (typed throw or { ok:false })", r1.rejected === true);
122
+ check("tamper-rows: failure points at the rows blob tamper",
123
+ (r1.threw && r1.code === "audit-tools/rows-checksum-mismatch") ||
124
+ (r1.threw && /tamper|checksum|decrypt/i.test(r1.message || "")) ||
125
+ (!r1.threw && /chain|rowHash|checksum/i.test(r1.reason || "")));
126
+
127
+ // ---- Tamper 2: corrupt the manifest's rows checksum. ----
128
+ var t2 = path.join(dir, "bundles", "tamper-manifest");
129
+ _copyDir(cleanDir, t2);
130
+ var m2Path = path.join(t2, "manifest.json");
131
+ var m2 = JSON.parse(fs.readFileSync(m2Path, "utf8"));
132
+ var origChecksum = m2.checksum.rowsSha3_512;
133
+ // Flip one hex nibble so the stored checksum no longer matches the
134
+ // (untouched) rows.enc bytes.
135
+ var firstChar = origChecksum.charAt(0);
136
+ var swapped = firstChar === "0" ? "1" : "0";
137
+ m2.checksum.rowsSha3_512 = swapped + origChecksum.slice(1);
138
+ fs.writeFileSync(m2Path, JSON.stringify(m2));
139
+ check("tamper-manifest: manifest checksum actually changed",
140
+ JSON.parse(fs.readFileSync(m2Path, "utf8")).checksum.rowsSha3_512 !== origChecksum);
141
+ var r2 = await _verify(t2);
142
+ check("tamper-manifest: bundle is REJECTED (typed throw or { ok:false })", r2.rejected === true);
143
+ check("tamper-manifest: failure points at the manifest/rows checksum mismatch",
144
+ (r2.threw && r2.code === "audit-tools/rows-checksum-mismatch") ||
145
+ (r2.threw && /tamper|checksum/i.test(r2.message || "")) ||
146
+ (!r2.threw && /checksum|chain|rowHash/i.test(r2.reason || "")));
147
+
148
+ // ---- Tamper 3: corrupt the covering checkpoint's SLH-DSA signature. ----
149
+ // Decrypt checkpoint.enc, flip a byte inside the signature, re-encrypt
150
+ // with a fresh salt, and update BOTH manifest.salts.checkpoint and
151
+ // manifest.checksum.checkpointSha3_512 so the checkpoint-checksum guard
152
+ // passes — isolating the signature-verification branch as the thing
153
+ // that must reject the forged checkpoint.
154
+ var t3 = path.join(dir, "bundles", "tamper-sig");
155
+ _copyDir(cleanDir, t3);
156
+ var m3Path = path.join(t3, "manifest.json");
157
+ var m3 = JSON.parse(fs.readFileSync(m3Path, "utf8"));
158
+ var ckptEncPath = path.join(t3, "checkpoint.enc");
159
+ var ckptEnc = fs.readFileSync(ckptEncPath);
160
+ var ckptPlain = (await backupCrypto.decryptWithPassphrase(
161
+ ckptEnc, PASS, m3.salts.checkpoint)).toString("utf8");
162
+ var ckptWire = JSON.parse(ckptPlain);
163
+ check("tamper-sig: checkpoint wire form carries a hex signature",
164
+ typeof ckptWire.signature === "string" && ckptWire.signature.indexOf("hex:") === 0);
165
+ // Flip the final hex nibble of the signature — keeps it valid hex /
166
+ // same length, but the SLH-DSA verify must fail.
167
+ var sigHex = ckptWire.signature;
168
+ var lastNibble = sigHex.charAt(sigHex.length - 1);
169
+ var newNibble = lastNibble === "0" ? "1" : "0";
170
+ var tamperedSigHex = sigHex.slice(0, -1) + newNibble;
171
+ check("tamper-sig: signature hex actually changed", tamperedSigHex !== sigHex);
172
+ ckptWire.signature = tamperedSigHex;
173
+ // The bundle canonicalizes checkpoint JSON; plain JSON.stringify here
174
+ // is fine — verifyBundle re-decrypts + re-parses, it doesn't compare
175
+ // the checkpoint blob byte-for-byte against the manifest beyond the
176
+ // checksum we recompute below.
177
+ var reEnc = await backupCrypto.encryptWithFreshSalt(JSON.stringify(ckptWire), PASS);
178
+ fs.writeFileSync(ckptEncPath, reEnc.encrypted);
179
+ m3.salts.checkpoint = reEnc.salt;
180
+ m3.checksum.checkpointSha3_512 = backupCrypto.checksum(reEnc.encrypted);
181
+ fs.writeFileSync(m3Path, JSON.stringify(m3));
182
+
183
+ var r3 = await _verify(t3);
184
+ check("tamper-sig: bundle is REJECTED (typed throw or { ok:false })", r3.rejected === true);
185
+ check("tamper-sig: failure is the checkpoint signature verification, not an earlier guard",
186
+ (!r3.threw && /signature/i.test(r3.reason || "")) ||
187
+ (r3.threw && /signature/i.test(r3.message || "")));
188
+ // Stronger: the signature branch returns { ok:false } (not a throw),
189
+ // and the chain math + checksums all passed to get there.
190
+ check("tamper-sig: reached the signature branch via a clean read (ok:false, not a throw)",
191
+ r3.threw === false && r3.ok === false &&
192
+ /signature/i.test(r3.reason || ""));
193
+
194
+ console.log("OK — audit verifyBundle tamper-detection (" + helpers.getChecks() + " checks)");
195
+ } finally {
196
+ await teardownTestDb(dir);
197
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_e) {}
198
+ }
199
+ }
200
+
201
+ module.exports = { run: run };
202
+
203
+ if (require.main === module) {
204
+ // Rethrow on failure so Node exits non-zero; logging the caught error
205
+ // would let a taint analyzer trace it back to the non-secret passphrase
206
+ // fixture and raise a false clear-text-logging alert.
207
+ run().then(function () { process.exit(0); })
208
+ .catch(function (err) { process.exitCode = 1; throw err; });
209
+ }