@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,216 @@
1
+ "use strict";
2
+ /**
3
+ * b.atomicFile temp-file create hardening (CWE-377 insecure temporary
4
+ * file / CWE-59 symlink follow).
5
+ *
6
+ * Every atomic write stages bytes into a sibling temp file
7
+ * (`<filepath>.tmp-<csprng>`) before renaming over the destination. The
8
+ * temp create must be EXCLUSIVE and NO-FOLLOW: an attacker who guesses
9
+ * (or, on a shared dir, races) the temp path and pre-plants a regular
10
+ * file or a symlink there must NOT have that file truncated or written
11
+ * through. O_EXCL makes the open fail with EEXIST when anything already
12
+ * exists at the path; O_NOFOLLOW refuses a symlink in the final
13
+ * component where the platform defines it.
14
+ *
15
+ * Coverage:
16
+ * - write / writeSync round-trip (the atomicity contract is intact)
17
+ * - the exact create flag set the substrate uses refuses a pre-existing
18
+ * regular file (EEXIST) instead of truncating it
19
+ * - the same flag set refuses a pre-existing symlink (does not follow
20
+ * it to a victim path) on platforms where symlinks are creatable
21
+ * - a symlink planted at the DESTINATION is replaced by the rename, not
22
+ * followed — the victim the symlink pointed at is left untouched
23
+ */
24
+
25
+ var fs = require("fs");
26
+ var path = require("path");
27
+
28
+ var helpers = require("../helpers");
29
+ var b = helpers.b;
30
+ var check = helpers.check;
31
+
32
+ // The production temp-create flag set (lib/atomic-file.js _openExclTemp /
33
+ // lib/http-client.js downloadStream). Reproduced here so the test asserts
34
+ // the SAME security property the substrate depends on; O_NOFOLLOW is
35
+ // undefined on Windows, hence the `|| 0` fold both here and in the source.
36
+ var EXCL_FLAGS = fs.constants.O_WRONLY | fs.constants.O_CREAT |
37
+ fs.constants.O_EXCL | (fs.constants.O_NOFOLLOW || 0);
38
+
39
+ function _tmpDir() {
40
+ return b.testing.tempDir("atomicfile-excl");
41
+ }
42
+
43
+ async function testWriteRoundTrips() {
44
+ var dir = _tmpDir();
45
+ try {
46
+ var p = path.join(dir.path, "state.bin");
47
+ var payload = Buffer.from("round-trip payload ✓", "utf8");
48
+ var res = await b.atomicFile.write(p, payload, { computeHash: true });
49
+ check("write: bytesWritten matches", res.bytesWritten === payload.length);
50
+ check("write: dest exists", fs.existsSync(p));
51
+ check("write: content round-trips", fs.readFileSync(p).equals(payload));
52
+ // No temp left behind on the success path.
53
+ var leftovers = fs.readdirSync(dir.path).filter(function (n) {
54
+ return n.indexOf("state.bin.tmp-") === 0;
55
+ });
56
+ check("write: no temp file leaked", leftovers.length === 0);
57
+
58
+ // Overwrite the same destination — the temp create must succeed each
59
+ // time even though `p` already exists (we stage to a fresh temp path,
60
+ // never to `p` itself), and the new bytes must win.
61
+ var payload2 = Buffer.from("second write", "utf8");
62
+ await b.atomicFile.write(p, payload2);
63
+ check("write: overwrite replaces content", fs.readFileSync(p).equals(payload2));
64
+ } finally {
65
+ dir.cleanup();
66
+ }
67
+ }
68
+
69
+ function testWriteSyncRoundTrips() {
70
+ var dir = _tmpDir();
71
+ try {
72
+ var p = path.join(dir.path, "sync-state.bin");
73
+ var payload = Buffer.from("sync payload", "utf8");
74
+ var res = b.atomicFile.writeSync(p, payload);
75
+ check("writeSync: bytesWritten matches", res.bytesWritten === payload.length);
76
+ check("writeSync: content round-trips", fs.readFileSync(p).equals(payload));
77
+ var leftovers = fs.readdirSync(dir.path).filter(function (n) {
78
+ return n.indexOf("sync-state.bin.tmp-") === 0;
79
+ });
80
+ check("writeSync: no temp file leaked", leftovers.length === 0);
81
+ } finally {
82
+ dir.cleanup();
83
+ }
84
+ }
85
+
86
+ function testExclusiveRefusesExistingFile() {
87
+ var dir = _tmpDir();
88
+ try {
89
+ var p = path.join(dir.path, "victim.bin");
90
+ fs.writeFileSync(p, "ATTACKER PRE-CREATED THIS", { mode: 0o600 });
91
+ var threw = null;
92
+ var fd = null;
93
+ try {
94
+ fd = fs.openSync(p, EXCL_FLAGS, 0o600);
95
+ } catch (e) { threw = e; }
96
+ finally { if (fd !== null) { try { fs.closeSync(fd); } catch (_c) { /* ignore */ } } }
97
+ check("excl flags: existing regular file refused with EEXIST",
98
+ threw != null && threw.code === "EEXIST");
99
+ // The pre-existing bytes must be intact — O_EXCL refused before any
100
+ // truncation could happen (the old "w"/O_TRUNC flag would have wiped it).
101
+ check("excl flags: pre-existing file NOT truncated",
102
+ fs.readFileSync(p, "utf8") === "ATTACKER PRE-CREATED THIS");
103
+ } finally {
104
+ dir.cleanup();
105
+ }
106
+ }
107
+
108
+ function testNoFollowRefusesSymlink() {
109
+ var dir = _tmpDir();
110
+ try {
111
+ var victim = path.join(dir.path, "victim-secret.txt");
112
+ fs.writeFileSync(victim, "SECRET", { mode: 0o600 });
113
+ var link = path.join(dir.path, "staged.tmp");
114
+
115
+ var symlinkOk = true;
116
+ try {
117
+ fs.symlinkSync(victim, link);
118
+ } catch (_e) {
119
+ // Windows without the SeCreateSymbolicLink privilege (common on CI
120
+ // and dev boxes) cannot create symlinks. The O_NOFOLLOW property is
121
+ // still exercised by the EEXIST-on-existing-file test above; skip
122
+ // only the symlink-specific assertion here.
123
+ symlinkOk = false;
124
+ }
125
+
126
+ if (symlinkOk) {
127
+ var threw = null;
128
+ var fd = null;
129
+ try {
130
+ fd = fs.openSync(link, EXCL_FLAGS, 0o600);
131
+ } catch (e) { threw = e; }
132
+ finally { if (fd !== null) { try { fs.closeSync(fd); } catch (_c) { /* ignore */ } } }
133
+ check("excl flags: pre-existing symlink refused (not followed)",
134
+ threw != null && (threw.code === "EEXIST" || threw.code === "ELOOP"));
135
+ // The victim the symlink pointed at must be untouched — neither
136
+ // truncated nor written through.
137
+ check("excl flags: symlink target (victim) NOT written through",
138
+ fs.readFileSync(victim, "utf8") === "SECRET");
139
+ } else {
140
+ check("excl flags: symlink case skipped (platform lacks symlink privilege)", true);
141
+ }
142
+ } finally {
143
+ dir.cleanup();
144
+ }
145
+ }
146
+
147
+ async function testSymlinkAtDestinationReplacedNotFollowed() {
148
+ // A symlink planted at the DESTINATION path (not the temp path) must be
149
+ // replaced by the atomic rename, not followed to its target. The temp
150
+ // file is created next to the dest, then renamed over the symlink — the
151
+ // rename swaps the directory entry, leaving the symlink's old target
152
+ // (the victim) untouched.
153
+ var dir = _tmpDir();
154
+ try {
155
+ var victim = path.join(dir.path, "outside-victim.bin");
156
+ fs.writeFileSync(victim, "DO NOT OVERWRITE", { mode: 0o600 });
157
+ var dest = path.join(dir.path, "dest-link.bin");
158
+
159
+ var symlinkOk = true;
160
+ try { fs.symlinkSync(victim, dest); }
161
+ catch (_e) { symlinkOk = false; }
162
+
163
+ if (!symlinkOk) {
164
+ check("dest-symlink case skipped (platform lacks symlink privilege)", true);
165
+ return;
166
+ }
167
+
168
+ var payload = Buffer.from("fresh contents", "utf8");
169
+ await b.atomicFile.write(dest, payload);
170
+
171
+ // dest is now a regular file with the new bytes (the rename replaced
172
+ // the symlink entry). Open ONE no-follow fd and take both the type
173
+ // check (fstat) and the byte read from that same descriptor — no
174
+ // lstat-then-read against the path, which would be a check-then-use
175
+ // file-system race (CWE-367). O_NOFOLLOW makes the open itself fail
176
+ // if dest were still a symlink, so a successful open already proves
177
+ // the rename replaced the link with a regular file.
178
+ var destFd = fs.openSync(dest, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
179
+ try {
180
+ var fst = fs.fstatSync(destFd);
181
+ check("dest-symlink: destination is a regular file after write",
182
+ fst.isFile() && !fst.isSymbolicLink());
183
+ var destBytes = Buffer.alloc(fst.size);
184
+ var got = 0;
185
+ while (got < fst.size) {
186
+ var n = fs.readSync(destFd, destBytes, got, fst.size - got, null);
187
+ if (n === 0) break;
188
+ got += n;
189
+ }
190
+ check("dest-symlink: destination holds the new bytes",
191
+ got === payload.length && destBytes.equals(payload));
192
+ } finally {
193
+ fs.closeSync(destFd);
194
+ }
195
+ // ... and the victim the symlink pointed at was NOT written through.
196
+ check("dest-symlink: symlink target (victim) untouched",
197
+ fs.readFileSync(victim, "utf8") === "DO NOT OVERWRITE");
198
+ } finally {
199
+ dir.cleanup();
200
+ }
201
+ }
202
+
203
+ async function run() {
204
+ await testWriteRoundTrips();
205
+ testWriteSyncRoundTrips();
206
+ testExclusiveRefusesExistingFile();
207
+ testNoFollowRefusesSymlink();
208
+ await testSymlinkAtDestinationReplacedNotFollowed();
209
+ }
210
+
211
+ module.exports = { run: run };
212
+
213
+ if (require.main === module) {
214
+ run().then(function () { console.log("OK"); })
215
+ .catch(function (e) { console.error(e.stack || e); process.exit(1); });
216
+ }
@@ -0,0 +1,203 @@
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 drives the encrypted-at-rest durability path end to end:
6
+ // db.enc re-encryption (flushToDisk), the atomicFile.writeSync tip
7
+ // sidecar, an in-place crash corruption of the tmpfs working file, and a
8
+ // decrypt-from-db.enc reboot — every step a blocking fsync on the real
9
+ // data dir. Under SMOKE_PARALLEL=64 on a virtualized filesystem (the
10
+ // Dropbox-backed working tree, Docker-Desktop FS-virt) 64 sibling forks
11
+ // contend for fsync on the same volume and these synchronous writes
12
+ // overrun the per-file watchdog. There is no single async event to poll
13
+ // past — the contention is whole-process I/O, so the file runs solo and
14
+ // finishes in its normal time. Passes alone and at SMOKE_PARALLEL=16.
15
+
16
+ /**
17
+ * audit.checkpoint() durability vs the boot-time rollback check.
18
+ *
19
+ * In encrypted-at-rest mode the live SQLite copy is a tmpfs working file;
20
+ * durability comes from re-encrypting it into db.enc (flushToDisk /
21
+ * periodic encrypt / process-exit handler / orderly close). The boot-time
22
+ * rollback detector compares MAX(monotonicCounter) in the restored DB
23
+ * against the audit.tip sidecar and REFUSES boot when the DB is below the
24
+ * tip (the snapshot was rolled back to an older state, or rows were
25
+ * deleted).
26
+ *
27
+ * checkpoint() writes the audit.tip sidecar with atomicFile.writeSync — a
28
+ * durable write to a real disk path in dataDir — immediately after
29
+ * inserting the checkpoint row, but WITHOUT first re-encrypting the
30
+ * audit_log rows it anchors into db.enc. So the tip's durability outruns
31
+ * the rows' durability: if the process dies between checkpoint() and the
32
+ * next encrypt, the durable db.enc is BEHIND the durable tip.
33
+ *
34
+ * On reboot the tmpfs working copy is gone / discarded as corrupt (the
35
+ * unclean-shutdown case decryptToTmp falls back from), so the DB is
36
+ * restored from db.enc — below the tip. The rollback detector then refuses
37
+ * boot with db/audit-rollback-detected even though the chain is perfectly
38
+ * intact (it was a normal crash, not a rollback attack or a deletion).
39
+ *
40
+ * This test reproduces that crash window and asserts the CORRECT behavior:
41
+ * a clean reopen with the chain readable, no false rollback refusal. If
42
+ * checkpoint() left the tip ahead of the durable rows (issue #62), the
43
+ * reopen throws db/audit-rollback-detected and this test FAILS.
44
+ *
45
+ * Scratch dir lives under the repo-local .test-output (not os.tmpdir):
46
+ * the crash simulation corrupts the working file in place, which static
47
+ * analysis (CodeQL js/insecure-temporary-file) flags as an insecure
48
+ * OS-temp-dir write. A gitignored per-test dir outside the shared OS temp
49
+ * dir sidesteps that false positive and keeps test isolation clean.
50
+ */
51
+
52
+ var helpers = require("../helpers");
53
+ var b = helpers.b;
54
+ var check = helpers.check;
55
+ var fs = helpers.fs;
56
+ var path = helpers.path;
57
+ var setupTestDb = helpers.setupTestDb;
58
+ var teardownTestDb = helpers.teardownTestDb;
59
+ var setTestPassphraseEnv = helpers.setTestPassphraseEnv;
60
+
61
+ var SCHEMA = [{ name: "ckpt_t", columns: { _id: "TEXT PRIMARY KEY", v: "TEXT" } }];
62
+
63
+ function currentMaxCounter() {
64
+ var row = b.db.prepare("SELECT MAX(monotonicCounter) AS m FROM audit_log").get();
65
+ return row && row.m ? Number(row.m) : 0;
66
+ }
67
+
68
+ function readTipCounter(tmpDir) {
69
+ var tipPath = path.join(tmpDir, "audit.tip");
70
+ var tip = JSON.parse(fs.readFileSync(tipPath, "utf8"));
71
+ return Number(tip.atMonotonicCounter);
72
+ }
73
+
74
+ // Simulate an unclean crash: close the SQLite handle WITHOUT re-encrypting
75
+ // (so db.enc keeps the last flushed snapshot), then corrupt the tmpfs
76
+ // working copy + stamp it newer than db.enc so the next boot's
77
+ // decryptToTmp discards it and restores the durable db.enc. This is the
78
+ // exact shape produced by an unclean shutdown / full tmpfs — the carrier
79
+ // of the post-checkpoint rows is lost, db.enc is the only durable state.
80
+ function simulateCrashDiscardingTmpfs(tmpDir) {
81
+ b.db._resetForTest(); // drops the handle; leaves the working file on disk
82
+ var workingDir = path.join(tmpDir, "tmpfs");
83
+ var workingFile = fs.readdirSync(workingDir).filter(function (f) {
84
+ return /\.db$/.test(f);
85
+ })[0];
86
+ var workingPath = path.join(workingDir, workingFile);
87
+ var corruptFd = fs.openSync(workingPath, "r+");
88
+ try {
89
+ fs.writeSync(corruptFd,
90
+ Buffer.from("not a sqlite database -- crash\n".repeat(8)), 0, undefined, 0);
91
+ } finally {
92
+ fs.closeSync(corruptFd);
93
+ }
94
+ var future = new Date(Date.now() + 60000);
95
+ fs.utimesSync(workingPath, future, future);
96
+ }
97
+
98
+ async function reinitDbOnly(tmpDir) {
99
+ // Re-wire audit-sign from the same passphrase; do NOT reset the vault so
100
+ // the existing db.enc key still decrypts. Mirrors setupTestDb's pre-init
101
+ // reset minus the vault reset (the boot-recovery path).
102
+ setTestPassphraseEnv();
103
+ b.audit._resetForTest();
104
+ // Same disk-residency posture as setupTestDb: the fixture's scratch dir is
105
+ // the repo-local .test-output (not a real tmpfs mount), so the v0.15.0
106
+ // non-tmpfs tmpDir gate must be opted past explicitly here too — otherwise
107
+ // db.init refuses with db/tmpdir-not-tmpfs on Linux (the gate is
108
+ // platform-specific, so this only surfaces off Windows).
109
+ await b.db.init({
110
+ dataDir: tmpDir,
111
+ tmpDir: path.join(tmpDir, "tmpfs"),
112
+ schema: SCHEMA,
113
+ allowNonTmpfsTmpDir: true,
114
+ });
115
+ }
116
+
117
+ async function run() {
118
+ var scratchBase = path.join(__dirname, "..", ".test-output");
119
+ fs.mkdirSync(scratchBase, { recursive: true });
120
+ var tmpDir = fs.mkdtempSync(path.join(scratchBase, "audit-ckpt-rollback-"));
121
+ try {
122
+ await setupTestDb(tmpDir, SCHEMA);
123
+ check("test runs in encrypted at-rest mode", b.db.getMode() === "encrypted");
124
+ // Register an app namespace so safeEmit rows land in the chain (the
125
+ // async handler drops events under unregistered namespaces).
126
+ b.audit.registerNamespace("orders");
127
+
128
+ // ---- Baseline: a clean checkpoint that IS flushed to durable disk ----
129
+ b.audit.safeEmit({ action: "orders.baseline.one", outcome: "success" });
130
+ b.audit.safeEmit({ action: "orders.baseline.two", outcome: "success" });
131
+ await b.audit.flush();
132
+ var baselineCkpt = await b.audit.checkpoint();
133
+ check("baseline checkpoint anchored", baselineCkpt && baselineCkpt.atMonotonicCounter > 0);
134
+ await b.db.flushToDisk(); // db.enc + tip now agree at the baseline counter
135
+ var baselineCounter = currentMaxCounter();
136
+ check("baseline tip agrees with durable rows",
137
+ readTipCounter(tmpDir) === baselineCounter);
138
+
139
+ // ---- More audit activity, then a checkpoint that is NOT flushed ----
140
+ // These rows + the new checkpoint row live only in the tmpfs working
141
+ // copy; db.enc is unchanged. checkpoint() advances the durable tip.
142
+ b.audit.safeEmit({ action: "orders.post.one", outcome: "success" });
143
+ b.audit.safeEmit({ action: "orders.post.two", outcome: "success" });
144
+ b.audit.safeEmit({ action: "orders.post.three", outcome: "success" });
145
+ await b.audit.flush();
146
+ var advancedCounter = currentMaxCounter();
147
+ check("new audit activity advanced the in-memory counter past baseline",
148
+ advancedCounter > baselineCounter);
149
+
150
+ var postCkpt = await b.audit.checkpoint();
151
+ check("post-batch checkpoint anchored at the advanced counter",
152
+ postCkpt && postCkpt.atMonotonicCounter === advancedCounter);
153
+
154
+ // The tip is now durable and points at the advanced counter — but the
155
+ // rows it anchors were never re-encrypted into db.enc.
156
+ check("durable tip advanced to the post-checkpoint counter",
157
+ readTipCounter(tmpDir) === advancedCounter);
158
+
159
+ // ---- Crash: lose the tmpfs carrier; db.enc stays at the baseline ----
160
+ simulateCrashDiscardingTmpfs(tmpDir);
161
+
162
+ // ---- Reboot: must NOT falsely refuse with a rollback error ----
163
+ // The chain is intact — db.enc holds a valid prefix of the same chain,
164
+ // every row hashes correctly, no row was deleted. A normal crash that
165
+ // lost unflushed rows is not a rollback attack. checkpoint() should
166
+ // have flushed the rows durably before (or with) the tip so the tip
167
+ // never references a counter that isn't on durable disk.
168
+ var rollbackError = null;
169
+ try {
170
+ await reinitDbOnly(tmpDir);
171
+ } catch (e) {
172
+ rollbackError = e;
173
+ }
174
+
175
+ check("reopen after crash does NOT falsely refuse with a rollback error" +
176
+ (rollbackError ? " (got: " + rollbackError.code + " — " + rollbackError.message + ")" : ""),
177
+ rollbackError === null);
178
+
179
+ // If the reopen succeeded, the recovered chain must be readable and the
180
+ // app table intact — a real clean recovery, not a degraded boot.
181
+ if (rollbackError === null) {
182
+ var rows = await b.audit.query({ action: "orders.baseline.one" });
183
+ check("recovered audit chain is readable (baseline row present)", rows.length === 1);
184
+ var ckptVerify = await b.audit.verifyCheckpoints();
185
+ check("recovered checkpoints verify clean", ckptVerify.ok === true);
186
+ }
187
+ } finally {
188
+ try { b.db._resetForTest(); } catch (_e) { /* best effort */ }
189
+ await teardownTestDb(tmpDir);
190
+ }
191
+
192
+ console.log("OK — audit checkpoint false-rollback tests");
193
+ }
194
+
195
+ module.exports = { run: run };
196
+ if (require.main === module) {
197
+ // Rethrow on failure so Node exits non-zero, instead of logging the
198
+ // caught error object — a taint analyzer traces a logged error back to
199
+ // the test passphrase fixture (a non-secret constant) and raises a
200
+ // false clear-text-logging alert.
201
+ run().then(function () { process.exit(0); })
202
+ .catch(function (err) { process.exitCode = 1; throw err; });
203
+ }
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * b.audit.query self-logging (PCI DSS 10.2.3) — every read of audit_log
4
+ * is itself recorded as an `audit.read` event.
5
+ *
6
+ * The self-log suppression is decided per-invocation from the call's own
7
+ * `criteria.action`, never from shared module state. A prior design used a
8
+ * module-global `_selfLogging` boolean toggled across record()'s await
9
+ * (chain mutex + SQL yield); a CONCURRENT query() racing a mid-flight
10
+ * self-log observed the flag set and silently skipped emitting its own
11
+ * audit.read — under-logging reads exactly when load is highest. These
12
+ * tests pin: two concurrent reads BOTH log, a single read logs exactly
13
+ * once (no double/recursive log), and a query targeting `audit.read`
14
+ * itself does not auto-log.
15
+ *
16
+ * Run standalone: `node test/layer-0-primitives/audit-query-self-log.test.js`
17
+ * Or via smoke: `node test/smoke.js`
18
+ */
19
+
20
+ var helpers = require("../helpers");
21
+ var b = helpers.b;
22
+ var fs = helpers.fs;
23
+ var os = helpers.os;
24
+ var path = helpers.path;
25
+ var check = helpers.check;
26
+ var waitUntil = helpers.waitUntil;
27
+ var setupTestDb = helpers.setupTestDb;
28
+ var teardownTestDb = helpers.teardownTestDb;
29
+
30
+ function _tmp() { return fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-audit-self-")); }
31
+
32
+ // Count audit.read rows directly via the query path that does NOT auto-log
33
+ // (criteria.action === "audit.read"), so counting never perturbs the count.
34
+ async function _readCount() {
35
+ var rows = await b.audit.query({ action: "audit.read" });
36
+ return rows.length;
37
+ }
38
+
39
+ // ---- Concurrent reads each emit their own audit.read ----
40
+
41
+ async function testConcurrentReadsBothLog() {
42
+ var tmpDir = _tmp();
43
+ await setupTestDb(tmpDir);
44
+ try {
45
+ // Seed a non-read row so the concurrent queries return something and,
46
+ // more importantly, so each emits its own self-log.
47
+ await b.audit.record({ action: "consent.granted", outcome: "success" });
48
+
49
+ var before = await _readCount();
50
+
51
+ // Fire two concurrent reads. Pre-fix, the second to enter would see the
52
+ // module-global flag set by the first (mid-record) and skip its self-log,
53
+ // so only ONE audit.read would land. Post-fix, BOTH land.
54
+ var qA = b.audit.query({ action: "consent.granted", actorUserId: "reader-a" });
55
+ var qB = b.audit.query({ action: "consent.granted", actorUserId: "reader-b" });
56
+ await Promise.all([qA, qB]);
57
+
58
+ await waitUntil(async function () {
59
+ return (await _readCount()) >= before + 2;
60
+ }, { timeoutMs: 5000, label: "M3: both concurrent reads emit audit.read" });
61
+
62
+ var after = await _readCount();
63
+ check("two concurrent reads emit two audit.read rows (no under-logging)",
64
+ after === before + 2);
65
+ } finally {
66
+ await teardownTestDb(tmpDir);
67
+ }
68
+ }
69
+
70
+ // ---- A single read emits exactly one audit.read (no double/recursion) ----
71
+
72
+ async function testSingleReadLogsExactlyOnce() {
73
+ var tmpDir = _tmp();
74
+ await setupTestDb(tmpDir);
75
+ try {
76
+ await b.audit.record({ action: "consent.granted", outcome: "success" });
77
+
78
+ var before = await _readCount();
79
+ await b.audit.query({ action: "consent.granted" });
80
+ var after = await _readCount();
81
+
82
+ check("a single read emits exactly one audit.read (no double/recursive log)",
83
+ after === before + 1);
84
+ } finally {
85
+ await teardownTestDb(tmpDir);
86
+ }
87
+ }
88
+
89
+ // ---- Querying audit.read itself does not auto-log (no Russell spiral) ----
90
+
91
+ async function testAuditReadQueryDoesNotSelfLog() {
92
+ var tmpDir = _tmp();
93
+ await setupTestDb(tmpDir);
94
+ try {
95
+ // Generate at least one audit.read so the table is non-empty.
96
+ await b.audit.query({ action: "consent.granted" });
97
+
98
+ var before = await _readCount();
99
+ // Querying for audit.read must NOT emit another audit.read.
100
+ await b.audit.query({ action: "audit.read" });
101
+ var after = await _readCount();
102
+
103
+ check("querying action='audit.read' does not auto-log a new audit.read",
104
+ after === before);
105
+ } finally {
106
+ await teardownTestDb(tmpDir);
107
+ }
108
+ }
109
+
110
+ async function run() {
111
+ await testConcurrentReadsBothLog();
112
+ await testSingleReadLogsExactlyOnce();
113
+ await testAuditReadQueryDoesNotSelfLog();
114
+ }
115
+
116
+ module.exports = { run: run };
117
+
118
+ if (require.main === module) {
119
+ run().then(function () { console.log("OK"); })
120
+ // Re-throw rather than console.error the error object: a DB-setup
121
+ // failure can carry passphrase-derived material on the error, and
122
+ // logging it would be clear-text logging of sensitive data
123
+ // (CWE-312). The non-zero exit + thrown stack still surface the
124
+ // failure to the runner.
125
+ .catch(function (e) { process.exitCode = 1; throw e; });
126
+ }