@blamejs/blamejs-shop 0.4.69 → 0.4.70

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 (297) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/lib/admin.js +8 -6
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/security-middleware.js +65 -23
  5. package/lib/storefront.js +10 -7
  6. package/lib/vendor/MANIFEST.json +319 -267
  7. package/lib/vendor/blamejs/CHANGELOG.md +2 -0
  8. package/lib/vendor/blamejs/api-snapshot.json +58 -5
  9. package/lib/vendor/blamejs/examples/wiki/README.md +1 -1
  10. package/lib/vendor/blamejs/examples/wiki/docker-compose.prod.yml +9 -8
  11. package/lib/vendor/blamejs/examples/wiki/docker-compose.yml +10 -9
  12. package/lib/vendor/blamejs/examples/wiki/env-snapshot.json +4 -3
  13. package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +33 -17
  14. package/lib/vendor/blamejs/examples/wiki/routes/admin.js +7 -10
  15. package/lib/vendor/blamejs/examples/wiki/server.js +5 -4
  16. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +5 -0
  17. package/lib/vendor/blamejs/lib/a2a-tasks.js +38 -6
  18. package/lib/vendor/blamejs/lib/agent-event-bus.js +13 -0
  19. package/lib/vendor/blamejs/lib/agent-idempotency.js +5 -1
  20. package/lib/vendor/blamejs/lib/agent-snapshot.js +32 -2
  21. package/lib/vendor/blamejs/lib/ai-aedt-bias-audit.js +2 -1
  22. package/lib/vendor/blamejs/lib/ai-content-detect.js +1 -3
  23. package/lib/vendor/blamejs/lib/ai-frontier-protocol.js +1 -1
  24. package/lib/vendor/blamejs/lib/ai-model-manifest.js +1 -1
  25. package/lib/vendor/blamejs/lib/ai-output.js +16 -7
  26. package/lib/vendor/blamejs/lib/api-snapshot.js +4 -1
  27. package/lib/vendor/blamejs/lib/app-shutdown.js +7 -1
  28. package/lib/vendor/blamejs/lib/archive-gz.js +9 -0
  29. package/lib/vendor/blamejs/lib/archive-read.js +9 -7
  30. package/lib/vendor/blamejs/lib/archive-tar-read.js +51 -8
  31. package/lib/vendor/blamejs/lib/archive.js +4 -2
  32. package/lib/vendor/blamejs/lib/asn1-der.js +70 -22
  33. package/lib/vendor/blamejs/lib/atomic-file.js +204 -2
  34. package/lib/vendor/blamejs/lib/audit-chain.js +54 -5
  35. package/lib/vendor/blamejs/lib/audit-daily-review.js +12 -2
  36. package/lib/vendor/blamejs/lib/audit-sign.js +2 -1
  37. package/lib/vendor/blamejs/lib/audit-tools.js +108 -23
  38. package/lib/vendor/blamejs/lib/audit.js +7 -2
  39. package/lib/vendor/blamejs/lib/auth/access-lock.js +2 -2
  40. package/lib/vendor/blamejs/lib/auth/bot-challenge.js +1 -1
  41. package/lib/vendor/blamejs/lib/auth/ciba.js +43 -4
  42. package/lib/vendor/blamejs/lib/auth/dpop.js +6 -1
  43. package/lib/vendor/blamejs/lib/auth/fido-mds3.js +5 -1
  44. package/lib/vendor/blamejs/lib/auth/jwt.js +2 -2
  45. package/lib/vendor/blamejs/lib/auth/lockout.js +18 -2
  46. package/lib/vendor/blamejs/lib/auth/passkey.js +1 -1
  47. package/lib/vendor/blamejs/lib/auth/password.js +1 -1
  48. package/lib/vendor/blamejs/lib/auth/saml.js +33 -13
  49. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +24 -4
  50. package/lib/vendor/blamejs/lib/auth/status-list.js +14 -2
  51. package/lib/vendor/blamejs/lib/auth/step-up.js +9 -1
  52. package/lib/vendor/blamejs/lib/auth-bot-challenge.js +21 -2
  53. package/lib/vendor/blamejs/lib/backup/bundle.js +7 -2
  54. package/lib/vendor/blamejs/lib/backup/crypto.js +23 -8
  55. package/lib/vendor/blamejs/lib/backup/index.js +41 -20
  56. package/lib/vendor/blamejs/lib/backup/manifest.js +7 -1
  57. package/lib/vendor/blamejs/lib/break-glass.js +41 -22
  58. package/lib/vendor/blamejs/lib/cbor.js +34 -11
  59. package/lib/vendor/blamejs/lib/cdn-cache-control.js +7 -3
  60. package/lib/vendor/blamejs/lib/cert.js +5 -3
  61. package/lib/vendor/blamejs/lib/cli.js +5 -1
  62. package/lib/vendor/blamejs/lib/cloud-events.js +3 -2
  63. package/lib/vendor/blamejs/lib/cluster-storage.js +7 -3
  64. package/lib/vendor/blamejs/lib/codepoint-class.js +17 -0
  65. package/lib/vendor/blamejs/lib/compliance-eaa.js +1 -1
  66. package/lib/vendor/blamejs/lib/compliance-sanctions.js +9 -7
  67. package/lib/vendor/blamejs/lib/compliance.js +1 -1
  68. package/lib/vendor/blamejs/lib/config-drift.js +22 -8
  69. package/lib/vendor/blamejs/lib/content-credentials.js +11 -8
  70. package/lib/vendor/blamejs/lib/content-digest.js +11 -4
  71. package/lib/vendor/blamejs/lib/cookies.js +10 -2
  72. package/lib/vendor/blamejs/lib/cose.js +20 -0
  73. package/lib/vendor/blamejs/lib/crdt.js +2 -1
  74. package/lib/vendor/blamejs/lib/crypto-field.js +29 -23
  75. package/lib/vendor/blamejs/lib/crypto.js +18 -4
  76. package/lib/vendor/blamejs/lib/csp.js +4 -0
  77. package/lib/vendor/blamejs/lib/daemon.js +4 -1
  78. package/lib/vendor/blamejs/lib/data-act.js +27 -4
  79. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +14 -6
  80. package/lib/vendor/blamejs/lib/db-query.js +69 -9
  81. package/lib/vendor/blamejs/lib/db.js +32 -15
  82. package/lib/vendor/blamejs/lib/dora.js +43 -13
  83. package/lib/vendor/blamejs/lib/dr-runbook.js +1 -1
  84. package/lib/vendor/blamejs/lib/dsa.js +2 -1
  85. package/lib/vendor/blamejs/lib/dsr.js +22 -8
  86. package/lib/vendor/blamejs/lib/early-hints.js +19 -0
  87. package/lib/vendor/blamejs/lib/eat.js +5 -1
  88. package/lib/vendor/blamejs/lib/external-db.js +60 -4
  89. package/lib/vendor/blamejs/lib/fda-21cfr11.js +30 -5
  90. package/lib/vendor/blamejs/lib/flag-providers.js +6 -2
  91. package/lib/vendor/blamejs/lib/forms.js +1 -1
  92. package/lib/vendor/blamejs/lib/gate-contract.js +46 -5
  93. package/lib/vendor/blamejs/lib/gdpr-ropa.js +18 -9
  94. package/lib/vendor/blamejs/lib/graphql-federation.js +17 -4
  95. package/lib/vendor/blamejs/lib/guard-all.js +2 -2
  96. package/lib/vendor/blamejs/lib/guard-dsn.js +1 -1
  97. package/lib/vendor/blamejs/lib/guard-envelope.js +1 -1
  98. package/lib/vendor/blamejs/lib/guard-html.js +9 -11
  99. package/lib/vendor/blamejs/lib/guard-imap-command.js +1 -1
  100. package/lib/vendor/blamejs/lib/guard-jmap.js +1 -1
  101. package/lib/vendor/blamejs/lib/guard-json.js +14 -6
  102. package/lib/vendor/blamejs/lib/guard-mail-move.js +1 -1
  103. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +1 -1
  104. package/lib/vendor/blamejs/lib/guard-pop3-command.js +1 -1
  105. package/lib/vendor/blamejs/lib/guard-smtp-command.js +1 -1
  106. package/lib/vendor/blamejs/lib/guard-svg.js +8 -9
  107. package/lib/vendor/blamejs/lib/html-balance.js +7 -3
  108. package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +33 -12
  109. package/lib/vendor/blamejs/lib/http-client.js +225 -53
  110. package/lib/vendor/blamejs/lib/iab-tcf.js +3 -2
  111. package/lib/vendor/blamejs/lib/importmap-integrity.js +41 -1
  112. package/lib/vendor/blamejs/lib/incident-report.js +9 -6
  113. package/lib/vendor/blamejs/lib/json-patch.js +1 -1
  114. package/lib/vendor/blamejs/lib/json-path.js +24 -3
  115. package/lib/vendor/blamejs/lib/jtd.js +2 -2
  116. package/lib/vendor/blamejs/lib/legal-hold.js +24 -8
  117. package/lib/vendor/blamejs/lib/log.js +2 -2
  118. package/lib/vendor/blamejs/lib/mail-agent.js +2 -2
  119. package/lib/vendor/blamejs/lib/mail-arf.js +1 -1
  120. package/lib/vendor/blamejs/lib/mail-auth.js +3 -3
  121. package/lib/vendor/blamejs/lib/mail-bimi.js +16 -16
  122. package/lib/vendor/blamejs/lib/mail-bounce.js +3 -3
  123. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +71 -6
  124. package/lib/vendor/blamejs/lib/mail-deploy.js +9 -5
  125. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -4
  126. package/lib/vendor/blamejs/lib/mail-helo.js +2 -4
  127. package/lib/vendor/blamejs/lib/mail-journal.js +11 -8
  128. package/lib/vendor/blamejs/lib/mail-mdn.js +8 -4
  129. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -4
  130. package/lib/vendor/blamejs/lib/mail-scan.js +3 -5
  131. package/lib/vendor/blamejs/lib/mail-server-jmap.js +4 -1
  132. package/lib/vendor/blamejs/lib/mail-server-registry.js +1 -1
  133. package/lib/vendor/blamejs/lib/mail-server-tls.js +9 -2
  134. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -4
  135. package/lib/vendor/blamejs/lib/mail-store-fts.js +1 -1
  136. package/lib/vendor/blamejs/lib/mail.js +22 -2
  137. package/lib/vendor/blamejs/lib/markup-tokenizer.js +24 -0
  138. package/lib/vendor/blamejs/lib/mcp.js +6 -4
  139. package/lib/vendor/blamejs/lib/mdoc.js +26 -3
  140. package/lib/vendor/blamejs/lib/metrics.js +14 -3
  141. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +2 -2
  142. package/lib/vendor/blamejs/lib/middleware/body-parser.js +10 -4
  143. package/lib/vendor/blamejs/lib/middleware/bot-guard.js +26 -18
  144. package/lib/vendor/blamejs/lib/middleware/clear-site-data.js +5 -1
  145. package/lib/vendor/blamejs/lib/middleware/compression.js +9 -0
  146. package/lib/vendor/blamejs/lib/middleware/cors.js +32 -23
  147. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +60 -21
  148. package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +6 -4
  149. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +28 -4
  150. package/lib/vendor/blamejs/lib/middleware/network-allowlist.js +61 -30
  151. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +25 -16
  152. package/lib/vendor/blamejs/lib/middleware/scim-server.js +2 -1
  153. package/lib/vendor/blamejs/lib/middleware/security-headers.js +24 -6
  154. package/lib/vendor/blamejs/lib/middleware/speculation-rules.js +6 -3
  155. package/lib/vendor/blamejs/lib/middleware/tus-upload.js +2 -2
  156. package/lib/vendor/blamejs/lib/money.js +1 -1
  157. package/lib/vendor/blamejs/lib/mtls-ca.js +10 -6
  158. package/lib/vendor/blamejs/lib/network-dns-resolver.js +1 -1
  159. package/lib/vendor/blamejs/lib/network-dns.js +9 -2
  160. package/lib/vendor/blamejs/lib/network-dnssec.js +2 -1
  161. package/lib/vendor/blamejs/lib/network-smtp-policy.js +23 -5
  162. package/lib/vendor/blamejs/lib/network-tls.js +27 -5
  163. package/lib/vendor/blamejs/lib/network-tsig.js +2 -2
  164. package/lib/vendor/blamejs/lib/nis2-report.js +1 -1
  165. package/lib/vendor/blamejs/lib/nist-crosswalk.js +1 -1
  166. package/lib/vendor/blamejs/lib/ntp-check.js +28 -0
  167. package/lib/vendor/blamejs/lib/numeric-bounds.js +9 -0
  168. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +1 -2
  169. package/lib/vendor/blamejs/lib/object-store/gcs-bucket-ops.js +4 -2
  170. package/lib/vendor/blamejs/lib/object-store/gcs.js +6 -4
  171. package/lib/vendor/blamejs/lib/object-store/http-put.js +1 -2
  172. package/lib/vendor/blamejs/lib/object-store/http-request.js +30 -1
  173. package/lib/vendor/blamejs/lib/object-store/local.js +37 -17
  174. package/lib/vendor/blamejs/lib/object-store/sigv4.js +1 -2
  175. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +20 -4
  176. package/lib/vendor/blamejs/lib/outbox.js +11 -4
  177. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +1 -1
  178. package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +21 -3
  179. package/lib/vendor/blamejs/lib/queue-local.js +10 -3
  180. package/lib/vendor/blamejs/lib/redact.js +7 -3
  181. package/lib/vendor/blamejs/lib/request-helpers.js +201 -23
  182. package/lib/vendor/blamejs/lib/resource-access-lock.js +3 -3
  183. package/lib/vendor/blamejs/lib/restore-bundle.js +46 -18
  184. package/lib/vendor/blamejs/lib/restore-rollback.js +10 -4
  185. package/lib/vendor/blamejs/lib/restore.js +19 -0
  186. package/lib/vendor/blamejs/lib/retention.js +20 -4
  187. package/lib/vendor/blamejs/lib/router.js +17 -4
  188. package/lib/vendor/blamejs/lib/safe-ical.js +2 -2
  189. package/lib/vendor/blamejs/lib/safe-icap.js +1 -1
  190. package/lib/vendor/blamejs/lib/safe-json.js +44 -0
  191. package/lib/vendor/blamejs/lib/safe-sieve.js +1 -1
  192. package/lib/vendor/blamejs/lib/safe-vcard.js +1 -1
  193. package/lib/vendor/blamejs/lib/sandbox-worker.js +6 -0
  194. package/lib/vendor/blamejs/lib/sandbox.js +1 -1
  195. package/lib/vendor/blamejs/lib/scheduler.js +17 -1
  196. package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +16 -0
  197. package/lib/vendor/blamejs/lib/session.js +27 -3
  198. package/lib/vendor/blamejs/lib/sql.js +3 -3
  199. package/lib/vendor/blamejs/lib/static.js +65 -13
  200. package/lib/vendor/blamejs/lib/template.js +7 -5
  201. package/lib/vendor/blamejs/lib/tenant-quota.js +52 -19
  202. package/lib/vendor/blamejs/lib/tsa.js +5 -2
  203. package/lib/vendor/blamejs/lib/vault/index.js +5 -0
  204. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +22 -26
  205. package/lib/vendor/blamejs/lib/vault/passphrase-source.js +8 -3
  206. package/lib/vendor/blamejs/lib/vault/rotate.js +13 -18
  207. package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +4 -1
  208. package/lib/vendor/blamejs/lib/vc.js +1 -1
  209. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +10 -10
  210. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +23 -10
  211. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5498 -5494
  212. package/lib/vendor/blamejs/lib/webhook.js +16 -1
  213. package/lib/vendor/blamejs/lib/websocket.js +1 -1
  214. package/lib/vendor/blamejs/lib/worm.js +1 -1
  215. package/lib/vendor/blamejs/lib/ws-client.js +57 -46
  216. package/lib/vendor/blamejs/lib/x509-chain.js +44 -0
  217. package/lib/vendor/blamejs/package.json +1 -1
  218. package/lib/vendor/blamejs/release-notes/v0.15.14.json +122 -0
  219. package/lib/vendor/blamejs/scripts/check-vendor-currency.js +119 -4
  220. package/lib/vendor/blamejs/test/00-primitives.js +5 -1
  221. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +18 -4
  222. package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +42 -0
  223. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +36 -0
  224. package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
  225. package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +47 -0
  226. package/lib/vendor/blamejs/test/layer-0-primitives/archive-gz.test.js +24 -0
  227. package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-hardening.test.js +160 -0
  228. package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +45 -0
  229. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-fd-read.test.js +30 -0
  230. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-open-nofollow.test.js +79 -0
  231. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-write-excl.test.js +132 -0
  232. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-write-stream.test.js +181 -0
  233. package/lib/vendor/blamejs/test/layer-0-primitives/audit-chain-corrupted-anchor.test.js +65 -0
  234. package/lib/vendor/blamejs/test/layer-0-primitives/audit-chain-incremental-verify.test.js +83 -0
  235. package/lib/vendor/blamejs/test/layer-0-primitives/audit-daily-review.test.js +48 -1
  236. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +13 -3
  237. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +122 -0
  238. package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge.test.js +37 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +5 -3
  240. package/lib/vendor/blamejs/test/layer-0-primitives/auth-lockout.test.js +21 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +73 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/bot-guard.test.js +17 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +62 -3
  244. package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +12 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/cluster-storage.test.js +40 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +146 -5
  247. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +14 -0
  248. package/lib/vendor/blamejs/test/layer-0-primitives/compression-range.test.js +115 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +17 -5
  250. package/lib/vendor/blamejs/test/layer-0-primitives/cors.test.js +27 -5
  251. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +83 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +58 -0
  253. package/lib/vendor/blamejs/test/layer-0-primitives/data-act.test.js +30 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +74 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/dora.test.js +20 -6
  256. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-alg-kty.test.js +64 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +32 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/fda-21cfr11.test.js +43 -0
  259. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +30 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +33 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +34 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +12 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +72 -0
  264. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-throttle-transform.test.js +88 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/importmap-integrity.test.js +25 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +28 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +20 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/mail-header-injection.test.js +58 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +63 -9
  270. package/lib/vendor/blamejs/test/layer-0-primitives/mdoc.test.js +41 -14
  271. package/lib/vendor/blamejs/test/layer-0-primitives/network-allowlist.test.js +61 -0
  272. package/lib/vendor/blamejs/test/layer-0-primitives/object-store-range-header.test.js +51 -0
  273. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +35 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/output-header-hardening.test.js +102 -0
  275. package/lib/vendor/blamejs/test/layer-0-primitives/parser-verify-hardening.test.js +191 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/proto-shadow-allowlist.test.js +120 -0
  277. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +75 -0
  278. package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +128 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/restore-blob-remap.test.js +128 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/retention-sweep-termination.test.js +104 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/router-body-validation.test.js +98 -0
  282. package/lib/vendor/blamejs/test/layer-0-primitives/safe-json-stringify-for-script.test.js +43 -0
  283. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +48 -5
  284. package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +23 -0
  285. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +54 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +24 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +42 -1
  288. package/lib/vendor/blamejs/test/layer-0-primitives/step-up.test.js +7 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/template-escape-html.test.js +43 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +15 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/vault-default-store.test.js +48 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/vendor-currency-classify.test.js +77 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +50 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +22 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/x509-chain-ca-enforcement.test.js +149 -0
  296. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +34 -0
  297. package/package.json +1 -1
@@ -181,6 +181,13 @@ async function run() {
181
181
  check("parseChallenge: maxAge", rt.maxAge === 300);
182
182
  check("parseChallenge: non-Bearer → null", b.auth.stepUp.parseChallenge("Basic realm=x") === null);
183
183
 
184
+ // A malformed max_age from the server must not land as NaN (a downstream
185
+ // `age > maxAge` against NaN is always false) — it's omitted so the caller
186
+ // falls back to its own default.
187
+ var badMa = b.auth.stepUp.parseChallenge('Bearer error="insufficient_user_authentication", max_age="not-a-number"');
188
+ check("parseChallenge: non-numeric max_age stays null (not NaN)",
189
+ badMa && badMa.maxAge === null);
190
+
184
191
  // ---- parseAuthorizationDetails (RFC 9396) ----
185
192
  var rar = b.auth.stepUp.parseAuthorizationDetails(JSON.stringify([
186
193
  { type: "payment_initiation", actions: ["initiate"], amount: { currency: "USD", value: 100 } },
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ // b.template.escapeHtml — the {{ expr }} interpolation escaper (XSS boundary).
3
+ // Now delegates to the centralized markup-escape so the five-character HTML set
4
+ // is defined in ONE place; a divergence between this and the shared escaper is
5
+ // an XSS / XML-injection surface. This pins the contract (all five chars, &
6
+ // first so emitted entities aren't double-escaped, null/undefined → "",
7
+ // non-string coercion) so the delegation can't silently regress.
8
+
9
+ var helpers = require("../helpers");
10
+ var b = helpers.b;
11
+ var check = helpers.check;
12
+
13
+ function run() {
14
+ var esc = b.template.escapeHtml;
15
+
16
+ check("escapes <", esc("<script>") === "&lt;script&gt;");
17
+ check("escapes & first (no double-escape of emitted entities)", esc("a&b") === "a&amp;b");
18
+ check("escapes double-quote", esc('say "hi"') === "say &quot;hi&quot;");
19
+ check("escapes apostrophe to numeric form (single-quoted attr safety)",
20
+ esc("it's") === "it&#x27;s");
21
+ check("all five together", esc("<a href='x' title=\"y\">&</a>") ===
22
+ "&lt;a href=&#x27;x&#x27; title=&quot;y&quot;&gt;&amp;&lt;/a&gt;");
23
+
24
+ // & is escaped first so a literal "&lt;" in the input becomes "&amp;lt;",
25
+ // not "&lt;" (which would let an attacker inject a pre-formed entity).
26
+ check("pre-formed entity in input is neutralized (& escaped first)",
27
+ esc("&lt;") === "&amp;lt;");
28
+
29
+ check("null → empty string", esc(null) === "");
30
+ check("undefined → empty string", esc(undefined) === "");
31
+ check("number coerced + returned", esc(42) === "42");
32
+ check("boolean coerced", esc(true) === "true");
33
+ check("plain string unchanged", esc("hello world") === "hello world");
34
+ check("empty string → empty string", esc("") === "");
35
+
36
+ console.log("OK — template.escapeHtml (" + helpers.getChecks() + " checks)");
37
+ }
38
+
39
+ module.exports = { run: run };
40
+ if (require.main === module) {
41
+ try { run(); process.exit(0); }
42
+ catch (e) { console.error("FAIL: " + (e && e.message)); process.exit(1); }
43
+ }
@@ -41,6 +41,21 @@ async function run() {
41
41
  var snapReset = budget.snapshot("tenant-acme");
42
42
  check("reset clears counters", snapReset.calls === 0);
43
43
 
44
+ // ---- budget — TRUE sliding window (no fixed-window boundary doubling) ----
45
+ // maxCalls = floor(qpsCap * window/1s) = 5. Fill 5 near the window's tail,
46
+ // then 1 more just past the nominal reset: a fixed window would admit it
47
+ // (~2x burst), a sliding window refuses (trailing window still covers them).
48
+ var sw = b.tenantQuota.budget({ tenantField: "tenantId", perTenantQpsCap: 5, window: 1000, audit: false });
49
+ for (var i = 0; i < 5; i++) sw.observe("t-burst", { now: 900 });
50
+ var threwBoundary = null;
51
+ try { sw.observe("t-burst", { now: 1001 }); } catch (e) { threwBoundary = e; }
52
+ check("sliding window refuses the boundary-straddling burst (not a fixed window)",
53
+ threwBoundary && threwBoundary.code === "tenant-quota/budget-exceeded");
54
+ // After a full window has elapsed, the old calls scroll out → admitted again.
55
+ var allowedAfterGap = true;
56
+ try { sw.observe("t-burst", { now: 2600 }); } catch (_e) { allowedAfterGap = false; }
57
+ check("sliding window admits again once the prior calls age out", allowedAfterGap === true);
58
+
44
59
  // ---- instrumentQuery — tenant-isolation breach ----
45
60
  var goodCheck = b.tenantQuota.instrumentQuery({
46
61
  rows: [
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /**
3
+ * b.vault.getDefaultStore — the default sealed-storage Store ({ seal, unseal })
4
+ * that b.cert.create and other sealed-disk consumers resolve when no explicit
5
+ * vault is passed. It previously did not exist, so the documented cert default
6
+ * path threw `TypeError: vault(...).getDefaultStore is not a function`.
7
+ */
8
+
9
+ var b = require("../..");
10
+ var check = require("../helpers/check").check;
11
+ var fs = require("fs");
12
+ var path = require("path");
13
+ var os = require("os");
14
+
15
+ async function run() {
16
+ var tmp = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-vault-defstore-"));
17
+ try {
18
+ await b.vault.init({ dataDir: tmp, mode: "plaintext" });
19
+
20
+ check("vault.getDefaultStore is a function", typeof b.vault.getDefaultStore === "function");
21
+ var store = b.vault.getDefaultStore();
22
+ check("getDefaultStore() exposes seal + unseal",
23
+ store && typeof store.seal === "function" && typeof store.unseal === "function");
24
+
25
+ // Round-trip a payload through the default store (the load-bearing
26
+ // guarantee; at-rest encryption strength is mode-dependent and covered by
27
+ // the wrapped-mode vault tests — here the vault is plaintext for speed).
28
+ var plain = "default-store-secret-not-real";
29
+ var sealed = store.seal(plain);
30
+ check("default store seal emits a vault-prefixed value",
31
+ typeof sealed === "string" && sealed.indexOf("vault:") === 0);
32
+ check("default store unseals back to the original", store.unseal(sealed) === plain);
33
+
34
+ check("vault.Store exposes the same seal/unseal pair",
35
+ b.vault.Store && typeof b.vault.Store.seal === "function" && typeof b.vault.Store.unseal === "function");
36
+
37
+ console.log("OK — vault.getDefaultStore (" + (require("../helpers").getChecks ? require("../helpers").getChecks() : "?") + " checks)");
38
+ } finally {
39
+ try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (_e) { /* best-effort */ }
40
+ }
41
+ }
42
+
43
+ module.exports = { run: run };
44
+
45
+ if (require.main === module) {
46
+ run().then(function () { process.exit(0); })
47
+ .catch(function (e) { console.error(e); process.exit(1); });
48
+ }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ /**
3
+ * Vendor-currency content classifier — the logic that makes Public Suffix
4
+ * List drift visible to the release gate. The PSL (and other bare-URL data
5
+ * vendors) carry no npm semver, so the currency checker compares an
6
+ * embedded `// COMMIT:` sha (primary) or `// VERSION:` timestamp (fallback)
7
+ * between the live upstream and our bundled copy. This drift was previously
8
+ * invisible: the checker queried npm, got a 404, and still printed "OK".
9
+ *
10
+ * Pure unit test (no network) — drives _classifyContentCurrency directly.
11
+ */
12
+
13
+ var helpers = require("../helpers");
14
+ var check = helpers.check;
15
+
16
+ var cur = require("../../scripts/check-vendor-currency.js");
17
+
18
+ var PSL = cur.SPECIAL_MAP["publicsuffix-list"];
19
+
20
+ function _doc(commit, version) {
21
+ var head = "// This Source Code Form is subject to the terms of the MPL 2.0.\n";
22
+ if (version) head += "// VERSION: " + version + "\n";
23
+ if (commit) head += "// COMMIT: " + commit + "\n";
24
+ return head + "com\nnet\norg\n";
25
+ }
26
+
27
+ function testCommitMatchIsCurrent() {
28
+ var v = cur._classifyContentCurrency(_doc("abc1234", "2026-06-13_x"), _doc("abc1234", "2026-06-13_x"), PSL);
29
+ check("matching COMMIT -> not stale", v.stale === false && v.basis === "commit");
30
+ }
31
+
32
+ function testCommitDifferIsStale() {
33
+ // Upstream moved (new commit) but our bundled copy is pinned to the old
34
+ // sha — this is exactly the PSL-rot case the gate must now catch.
35
+ var v = cur._classifyContentCurrency(_doc("9186eee", "2026-06-13_x"), _doc("ee780bc", "2026-05-07_y"), PSL);
36
+ check("differing COMMIT -> stale", v.stale === true && v.basis === "commit");
37
+ check("stale verdict carries both ids", v.upstreamId === "9186eee" && v.localId === "ee780bc");
38
+ }
39
+
40
+ function testVersionFallbackWhenNoCommit() {
41
+ // Older list snapshots predate the COMMIT header — fall back to VERSION.
42
+ var up = cur._classifyContentCurrency(_doc(null, "2026-06-13_x"), _doc(null, "2026-05-07_y"), PSL);
43
+ check("no COMMIT, differing VERSION -> stale via version", up.stale === true && up.basis === "version");
44
+ var same = cur._classifyContentCurrency(_doc(null, "2026-06-13_x"), _doc(null, "2026-06-13_x"), PSL);
45
+ check("no COMMIT, matching VERSION -> current via version", same.stale === false && same.basis === "version");
46
+ }
47
+
48
+ function testNoIdentifierThrows() {
49
+ // Neither side yields a comparable id (e.g. upstream format changed) —
50
+ // must throw so the caller reports registry-error, never a silent pass.
51
+ var threw = false;
52
+ try { cur._classifyContentCurrency(_doc(null, null), _doc(null, null), PSL); }
53
+ catch (_e) { threw = true; }
54
+ check("no comparable identifier -> throws (loud, not silent pass)", threw === true);
55
+ }
56
+
57
+ function testGateConfigLockedIn() {
58
+ // The whole point: PSL is content-tracked, BIMI is an intentional skip.
59
+ check("publicsuffix-list mapped to http-content", PSL && PSL.type === "http-content");
60
+ check("publicsuffix-list has an upstream URL", typeof PSL.url === "string" && PSL.url.indexOf("publicsuffix.org") !== -1);
61
+ var bimi = cur.SPECIAL_MAP["bimi-trust-anchors"];
62
+ check("bimi-trust-anchors is a documented skip", bimi && bimi.type === "skip" && typeof bimi.reason === "string");
63
+ }
64
+
65
+ function run() {
66
+ testCommitMatchIsCurrent();
67
+ testCommitDifferIsStale();
68
+ testVersionFallbackWhenNoCommit();
69
+ testNoIdentifierThrows();
70
+ testGateConfigLockedIn();
71
+ console.log("[vendor-currency-classify] OK — " + helpers.getChecks() + " checks passed");
72
+ }
73
+
74
+ module.exports = { run: run };
75
+ if (require.main === module) {
76
+ run();
77
+ }
@@ -256,6 +256,54 @@ async function testNonceStoreErrorPropagates() {
256
256
  threw && /redis down/.test(threw.message));
257
257
  }
258
258
 
259
+ async function testStripeVerifyReplayAtomic() {
260
+ // #328: b.webhook.verify (Stripe HMAC-SHA-256) speaks the SAME atomic
261
+ // checkAndInsert nonce contract as b.webhook.verifier / b.nonceStore — so the
262
+ // framework's own replay store drops in, and concurrent redeliveries can't
263
+ // race a non-atomic check-then-set.
264
+ var secret = "whsec_test_328";
265
+ var body = '{"id":"evt_328"}';
266
+ var header = b.webhook.sign({ alg: "hmac-sha256-stripe", secret: secret, body: body });
267
+ var seen = new Map();
268
+ var ns = { checkAndInsert: function (nonce, expireAt) { if (seen.has(nonce)) return false; seen.set(nonce, expireAt); return true; } };
269
+ var base = { alg: "hmac-sha256-stripe", secret: secret, body: body, header: header, nonceStore: ns };
270
+
271
+ var first = await b.webhook.verify(base);
272
+ check("Stripe verify: first delivery accepted", first && first.ok === true);
273
+
274
+ var threw = null;
275
+ try { await b.webhook.verify(base); } catch (e) { threw = e; }
276
+ check("Stripe verify: replay → webhook/replay", threw && threw.code === "webhook/replay");
277
+
278
+ // The framework's own b.nonceStore plugs in directly, no adapter.
279
+ var fwStore = b.nonceStore.create({ backend: "memory" });
280
+ var body2 = '{"id":"evt_328b"}';
281
+ var header2 = b.webhook.sign({ alg: "hmac-sha256-stripe", secret: secret, body: body2 });
282
+ var okFw = await b.webhook.verify({ alg: "hmac-sha256-stripe", secret: secret, body: body2, header: header2, nonceStore: fwStore });
283
+ check("Stripe verify: b.nonceStore drops in (no adapter)", okFw && okFw.ok === true);
284
+
285
+ // A legacy { has, set } store (no checkAndInsert) is refused at construction.
286
+ var threw2 = null;
287
+ try {
288
+ await b.webhook.verify({ alg: "hmac-sha256-stripe", secret: secret, body: body, header: header,
289
+ nonceStore: { has: function () {}, set: function () {} } });
290
+ } catch (e) { threw2 = e; }
291
+ check("Stripe verify: non-checkAndInsert store → bad-nonce-store",
292
+ threw2 && threw2.code === "webhook/bad-nonce-store");
293
+ }
294
+
295
+ async function testSignerSendRefusesSsrf() {
296
+ // signer.send refuses a private / cloud-metadata destination (SSRF) up front,
297
+ // before the retry loop and any network — webhook-layer gate + the transport's.
298
+ var s = b.webhook.signer({ algo: "hmac-sha3-512", keys: { v1: "secret" } });
299
+ var threw = null;
300
+ try { await s.send({ url: "https://169.254.169.254/hook", body: "{}", kid: "v1" }); }
301
+ catch (e) { threw = e; }
302
+ check("signer.send refuses cloud-metadata IP (SSRF)",
303
+ threw && (threw.code === "SSRF_REFUSED" ||
304
+ /ssrf|metadata|blocked|internal|private/i.test((threw.code || "") + " " + (threw.message || ""))));
305
+ }
306
+
259
307
  // ---- Middleware ----
260
308
 
261
309
  async function testMiddlewareSuccess() {
@@ -704,6 +752,8 @@ async function run() {
704
752
  await testHeaderCaseInsensitive();
705
753
  await testNonceStoreReplay();
706
754
  await testNonceStoreErrorPropagates();
755
+ await testStripeVerifyReplayAtomic();
756
+ await testSignerSendRefusesSsrf();
707
757
  await testMiddlewareSuccess();
708
758
  await testMiddlewareMissingRawBody();
709
759
  await testMiddlewareBadSignature();
@@ -342,6 +342,28 @@ async function run() {
342
342
  await _sleep(150);
343
343
  check("wsClient: custom handshakeGuid accepted at config time", true);
344
344
 
345
+ // ---- urlFor swap is SSRF re-validated (awaited) before connect ----
346
+ // checkUrl is async; the dial used to call it synchronously and discard the
347
+ // Promise, so a urlFor that pointed at a private / cloud-metadata address was
348
+ // connected anyway (the rejection surfaced only as an unhandled rejection).
349
+ // A swap to the metadata IP must now be refused with an SSRF error, and the
350
+ // dial must NOT reach the metadata host.
351
+ var sawUnhandled = null;
352
+ function _onUnhandled(e) { sawUnhandled = e; }
353
+ process.on("unhandledRejection", _onUnhandled);
354
+ var ssrfErr = null;
355
+ var cSwap = b.wsClient.connect("ws://127.0.0.1:1", {
356
+ urlFor: function () { return "ws://169.254.169.254:1/"; }, // cloud-metadata — hard-deny
357
+ reconnect: false, audit: false, allowInternal: true, // allowInternal must NOT bypass metadata
358
+ });
359
+ cSwap.on("error", function (e) { if (!ssrfErr) ssrfErr = e; });
360
+ await helpers.waitUntil(function () { return ssrfErr !== null; },
361
+ { timeoutMs: 5000, label: "ws-client: urlFor-swap SSRF refusal emitted" });
362
+ check("urlFor swap to metadata IP refused with SSRF error",
363
+ ssrfErr && /metadata|ssrf|blocked|private|internal/i.test((ssrfErr.code || "") + " " + (ssrfErr.message || "")));
364
+ check("urlFor swap did not surface an unhandled rejection", sawUnhandled === null);
365
+ process.removeListener("unhandledRejection", _onUnhandled);
366
+
345
367
  console.log("OK — ws-client tests");
346
368
  }
347
369
 
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ /**
3
+ * X.509 basicConstraints cA enforcement across the framework's cert-chain
4
+ * walkers (b.tsa.verifyToken, b.mail.bimi, b.mail.crypto.smime.verify).
5
+ *
6
+ * node:crypto X509Certificate.checkIssued() does NOT enforce basicConstraints
7
+ * cA:TRUE, so a leaf / end-entity cert (cA:FALSE) that omits keyUsage is
8
+ * wrongly accepted as a signing CA — the classic basicConstraints bypass
9
+ * (CVE-2002-0862 class). All three walkers route their issuer test through
10
+ * lib/x509-chain.js, which adds the cA check. This proves:
11
+ * 1. the shared primitive (isCaCert / issuerValidlyIssued) — the exact
12
+ * logic every walker now depends on — rejects a non-CA issuer; and
13
+ * 2. an end-to-end consumer path (b.mail.bimi.fetchAndVerifyMark) refuses
14
+ * a forged mark whose chain hangs off a cA:FALSE intermediate.
15
+ *
16
+ * The intermediate deliberately OMITS keyUsage: checkIssued already rejects
17
+ * keyUsage-without-keyCertSign, so a keyUsage-bearing intermediate would pass
18
+ * even on the buggy code — only the missing basicConstraints check is under
19
+ * test here.
20
+ */
21
+
22
+ var helpers = require("../helpers");
23
+ var b = helpers.b;
24
+ var check = helpers.check;
25
+
26
+ var nodeCrypto = require("crypto");
27
+ var pki = require("../../lib/vendor/pki.cjs");
28
+ var x509 = pki.x509;
29
+ var x509Chain = require("../../lib/x509-chain");
30
+
31
+ var BIMI_EKU_OID = "1.3.6.1.5.5.7.3.31";
32
+
33
+ // Mint root(CA:TRUE) -> intermediate(cA per opts) -> leaf, returning PEMs and
34
+ // node X509Certificate objects. The intermediate omits keyUsage so the cA
35
+ // check is the only thing that can reject it as an issuer.
36
+ async function _mintChain(opts) {
37
+ opts = opts || {};
38
+ var interCa = opts.interCa === true; // default cA:FALSE (the attack)
39
+ var leafSan = opts.leafSan || "victim.example";
40
+ var now = new Date();
41
+ var notAfter = new Date(now.getTime() + 365 * 24 * 60 * 60 * 1000);
42
+ var alg = { name: "ECDSA", namedCurve: "P-256" };
43
+ var sigAlg = { name: "ECDSA", hash: "SHA-256" };
44
+
45
+ var rootKeys = await pki.crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
46
+ var interKeys = await pki.crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
47
+ var leafKeys = await pki.crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
48
+
49
+ var root = await x509.X509CertificateGenerator.createSelfSigned({
50
+ serialNumber: "01", name: "CN=Test Root CA", notBefore: now, notAfter: notAfter,
51
+ signingAlgorithm: sigAlg, keys: rootKeys,
52
+ extensions: [
53
+ new x509.BasicConstraintsExtension(true, 2, true),
54
+ new x509.KeyUsagesExtension(x509.KeyUsageFlags.keyCertSign | x509.KeyUsageFlags.cRLSign, true),
55
+ ],
56
+ });
57
+
58
+ // The intermediate: cA per opts, and NO keyUsage extension at all.
59
+ var inter = await x509.X509CertificateGenerator.create({
60
+ serialNumber: "02", issuer: root.subject, subject: "CN=Test Intermediate",
61
+ notBefore: now, notAfter: notAfter, signingAlgorithm: sigAlg,
62
+ publicKey: interKeys.publicKey, signingKey: rootKeys.privateKey,
63
+ extensions: [ new x509.BasicConstraintsExtension(interCa, interCa ? 0 : undefined, true) ],
64
+ });
65
+
66
+ var leaf = await x509.X509CertificateGenerator.create({
67
+ serialNumber: "03", issuer: inter.subject, subject: "CN=" + leafSan,
68
+ notBefore: now, notAfter: notAfter, signingAlgorithm: sigAlg,
69
+ publicKey: leafKeys.publicKey, signingKey: interKeys.privateKey,
70
+ extensions: [
71
+ new x509.BasicConstraintsExtension(false, undefined, true),
72
+ new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature, true),
73
+ new x509.SubjectAlternativeNameExtension([{ type: "dns", value: leafSan }]),
74
+ new x509.ExtendedKeyUsageExtension([BIMI_EKU_OID], false),
75
+ ],
76
+ });
77
+
78
+ return {
79
+ rootPem: root.toString("pem"),
80
+ interPem: inter.toString("pem"),
81
+ leafPem: leaf.toString("pem"),
82
+ root: new nodeCrypto.X509Certificate(root.toString("pem")),
83
+ inter: new nodeCrypto.X509Certificate(inter.toString("pem")),
84
+ leaf: new nodeCrypto.X509Certificate(leaf.toString("pem")),
85
+ };
86
+ }
87
+
88
+ function _stubHttpClient(body) {
89
+ return {
90
+ request: function () {
91
+ return Promise.resolve({
92
+ statusCode: 200, headers: {},
93
+ body: Buffer.isBuffer(body) ? body : Buffer.from(String(body), "utf8"),
94
+ });
95
+ },
96
+ };
97
+ }
98
+
99
+ async function run() {
100
+ // ---- 1. Shared primitive: the cA enforcement every walker routes through.
101
+ var c = await _mintChain({ interCa: false }); // intermediate is cA:FALSE
102
+ check("isCaCert: root (cA:TRUE) is a CA", x509Chain.isCaCert(c.root) === true);
103
+ check("isCaCert: intermediate (cA:FALSE) is NOT a CA", x509Chain.isCaCert(c.inter) === false);
104
+ check("isCaCert: leaf (cA:FALSE) is NOT a CA", x509Chain.isCaCert(c.leaf) === false);
105
+ check("issuerValidlyIssued: root validly issued the intermediate",
106
+ x509Chain.issuerValidlyIssued(c.root, c.inter) === true);
107
+ // THE FIX: the intermediate genuinely issued + signed the leaf, but it is
108
+ // not a CA, so it must be refused as a chain issuer.
109
+ check("issuerValidlyIssued: cA:FALSE intermediate is REFUSED as the leaf's issuer",
110
+ x509Chain.issuerValidlyIssued(c.inter, c.leaf) === false);
111
+
112
+ // Control: when the intermediate IS a CA, the same call accepts it.
113
+ var ok = await _mintChain({ interCa: true });
114
+ check("issuerValidlyIssued: cA:TRUE intermediate is ACCEPTED as the leaf's issuer",
115
+ x509Chain.issuerValidlyIssued(ok.inter, ok.leaf) === true);
116
+
117
+ // ---- 2. Consumer path: b.mail.bimi.fetchAndVerifyMark must refuse a mark
118
+ // whose chain hangs off the cA:FALSE intermediate (leaf + intermediate
119
+ // served from the VMC URL, root as the trust anchor).
120
+ var threw = null;
121
+ try {
122
+ await b.mail.bimi.fetchAndVerifyMark({
123
+ domain: "victim.example",
124
+ vmcUrl: "https://victim.example/cert.pem",
125
+ trustAnchorsPem: c.rootPem,
126
+ httpClient: _stubHttpClient(c.leafPem + "\n" + c.interPem),
127
+ });
128
+ } catch (e) { threw = e; }
129
+ check("bimi.fetchAndVerifyMark: forged mark via cA:FALSE intermediate is REJECTED",
130
+ threw && threw.code === "bimi/vmc-chain-invalid");
131
+
132
+ // Control: the same wiring with a cA:TRUE intermediate verifies.
133
+ var rv = await b.mail.bimi.fetchAndVerifyMark({
134
+ domain: "victim.example",
135
+ vmcUrl: "https://victim.example/cert.pem",
136
+ trustAnchorsPem: ok.rootPem,
137
+ httpClient: _stubHttpClient(ok.leafPem + "\n" + ok.interPem),
138
+ });
139
+ check("bimi.fetchAndVerifyMark: valid CA chain still verifies", rv.ok === true);
140
+
141
+ console.log("OK — x509 cA enforcement (" + helpers.getChecks() + " checks)");
142
+ }
143
+
144
+ module.exports = { run: run };
145
+
146
+ if (require.main === module) {
147
+ run().then(function () { process.exit(0); })
148
+ .catch(function (err) { process.exitCode = 1; throw err; });
149
+ }
@@ -520,6 +520,40 @@ async function run() {
520
520
  check("omitted-tag read → us-replica wire NOT reached (gate fail-closed)",
521
521
  usReplicaOmit.seen.every(function (s) { return !/SELECT id FROM orders/.test(s); }));
522
522
 
523
+ // ---- read-replica gate must NOT over-reject an UNRESTRICTED replica ----
524
+ // A replica with no residencyTag defaults to "unrestricted" (no residency
525
+ // constraint — it may serve any region's rows). Under a cross-border-regulated
526
+ // posture a read to it WITHOUT opts.rowResidencyTag must still pass: there is
527
+ // no constraint to enforce. (Was: the fail-closed tag-required gate fired on
528
+ // any truthy replica.residencyTag, and "unrestricted" is truthy → it refused
529
+ // every regulated read to an untagged replica that omitted rowResidencyTag.)
530
+ b.externalDb._resetForTest();
531
+ var roPrimaryFree = _trackingDriver("ro-primary-free");
532
+ var freeReplica = _trackingDriver("free-replica");
533
+ b.externalDb.init({
534
+ backends: {
535
+ main: {
536
+ connect: roPrimaryFree.connect, query: roPrimaryFree.query,
537
+ close: roPrimaryFree.close, // primary unrestricted
538
+ replicas: [
539
+ {
540
+ connect: freeReplica.connect, query: freeReplica.query,
541
+ close: freeReplica.close, // no residencyTag → "unrestricted"
542
+ allowCrossBorder: false,
543
+ },
544
+ ],
545
+ replicaFallbackToPrimary: false,
546
+ },
547
+ },
548
+ });
549
+ await _underGdpr(async function () {
550
+ var res = await b.externalDb.read.query("SELECT id FROM orders WHERE id = $1", ["o-1"]);
551
+ check("gdpr + unrestricted replica + omitted tag → passes (no over-reject)",
552
+ res && res.rowCount === 1);
553
+ });
554
+ check("unrestricted-replica untagged read → replica wire reached",
555
+ _saw(freeReplica, /SELECT id FROM orders/));
556
+
523
557
  // ---- write verbs that wear a harmless leading keyword must still be
524
558
  // gated: WITH (CTE) / COPY FROM / EXPLAIN ANALYZE / CALL / EXECUTE /
525
559
  // REPLACE / DO all PLACE rows, so under gdpr + eu backend they require
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.4.69",
3
+ "version": "0.4.70",
4
4
  "description": "Open-source framework built on blamejs. Vendored stack, zero npm runtime deps, PQC-first crypto, security-on by default.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {