@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
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ /**
3
+ * b.restore — blob-remap / signature-enforcement integrity (CWE-347).
4
+ *
5
+ * The documented attack: an attacker with write access to a bundle in storage
6
+ * copies one valid blob over another manifest entry (reusing its
7
+ * salt/checksum/size), strips manifest.signature, and restore.run() applies
8
+ * it with NO integrity error — writing one file's plaintext into another's
9
+ * slot (e.g. db.enc's bytes into db.key.enc). Two defenses:
10
+ * 1. each blob is sealed with its relativePath as AEAD associated data, so a
11
+ * remapped blob fails the Poly1305 tag on restore (manifest.aadBound);
12
+ * 2. restore.create accepts a requireSignature policy (threaded into
13
+ * extract) operators opt into to mandate a verified signer.
14
+ */
15
+
16
+ var helpers = require("../helpers");
17
+ var b = helpers.b;
18
+ var check = helpers.check;
19
+ var fs = helpers.fs;
20
+ var os = helpers.os;
21
+ var path = helpers.path;
22
+
23
+ function _seed() {
24
+ var root = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-remap-"));
25
+ var dataDir = path.join(root, "data");
26
+ fs.mkdirSync(dataDir);
27
+ fs.writeFileSync(path.join(dataDir, "db.enc"), "ORIG-DB-CONTENT");
28
+ fs.writeFileSync(path.join(dataDir, "db.key.enc"), "SEALED-DEK");
29
+ return { root: root, dataDir: dataDir, storageRoot: path.join(root, "store") };
30
+ }
31
+
32
+ async function run() {
33
+ var pp = Buffer.from("operator-passphrase-not-secret");
34
+
35
+ // --- Build a bundle. ---
36
+ var fx = _seed();
37
+ try {
38
+ function storage() { return b.backup.diskStorage({ root: fx.storageRoot }); }
39
+ var r = await b.backup.create({
40
+ dataDir: fx.dataDir, storage: storage(), passphrase: pp,
41
+ files: [
42
+ { relativePath: "db.enc", kind: "raw", required: true },
43
+ { relativePath: "db.key.enc", kind: "raw", required: true },
44
+ ],
45
+ vaultKeyJson: '{"vault":"orig"}', audit: false,
46
+ }).run();
47
+ var bdir = path.join(fx.storageRoot, r.bundleId);
48
+ var manifest = JSON.parse(fs.readFileSync(path.join(bdir, "manifest.json"), "utf8"));
49
+ check("backup: new bundle is marked aadBound", manifest.aadBound === true);
50
+
51
+ // --- Clean round-trip still restores (no regression). ---
52
+ fs.writeFileSync(path.join(fx.dataDir, "db.enc"), "MUTATED");
53
+ var rr = await b.restore.create({
54
+ dataDir: fx.dataDir, storage: storage(), passphrase: pp,
55
+ rollbackRoot: path.join(fx.root, "rb"), audit: false,
56
+ }).run({ bundleId: r.bundleId });
57
+ check("restore: clean round-trip succeeds", rr.fileCount === 2);
58
+ check("restore: db.enc restored to bundle bytes",
59
+ fs.readFileSync(path.join(fx.dataDir, "db.enc")).toString() === "ORIG-DB-CONTENT");
60
+
61
+ // --- Blob-remap attack: point db.key.enc's entry at db.enc's blob
62
+ // (reuse its salt/checksum/size), strip the signature, restore. ---
63
+ var dbEnc = manifest.files.filter(function (f) { return f.relativePath === "db.enc"; })[0];
64
+ var dbKey = manifest.files.filter(function (f) { return f.relativePath === "db.key.enc"; })[0];
65
+ dbKey.encryptedPath = dbEnc.encryptedPath;
66
+ dbKey.salt = dbEnc.salt;
67
+ dbKey.checksum = dbEnc.checksum;
68
+ dbKey.size = dbEnc.size;
69
+ dbKey.encryptedSize = dbEnc.encryptedSize;
70
+ delete manifest.signature;
71
+ fs.writeFileSync(path.join(bdir, "manifest.json"), JSON.stringify(manifest));
72
+
73
+ var threw = null;
74
+ try {
75
+ await b.restore.create({
76
+ dataDir: fx.dataDir, storage: storage(), passphrase: pp,
77
+ rollbackRoot: path.join(fx.root, "rb2"), audit: false,
78
+ }).run({ bundleId: r.bundleId });
79
+ } catch (e) { threw = e; }
80
+ check("restore: blob-remap attack is REFUSED (AEAD path-binding)",
81
+ threw && /^restore\/(decrypt-failed|extract-failed)$/.test(threw.code || ""));
82
+ } finally {
83
+ fs.rmSync(fx.root, { recursive: true, force: true });
84
+ }
85
+
86
+ // --- requireSignature policy: an unsigned bundle is refused when the
87
+ // operator mandates a signature (HIPAA/PCI), and restores when not. ---
88
+ var fx2 = _seed();
89
+ try {
90
+ function storage2() { return b.backup.diskStorage({ root: fx2.storageRoot }); }
91
+ var r2 = await b.backup.create({
92
+ dataDir: fx2.dataDir, storage: storage2(), passphrase: pp,
93
+ files: [{ relativePath: "db.enc", kind: "raw", required: true }],
94
+ vaultKeyJson: '{"vault":"orig"}', audit: false,
95
+ }).run();
96
+ var m2 = JSON.parse(fs.readFileSync(path.join(fx2.storageRoot, r2.bundleId, "manifest.json"), "utf8"));
97
+ check("backup: best-effort bundle is unsigned (no audit-sign)", !m2.signature);
98
+
99
+ var threwReq = null;
100
+ try {
101
+ await b.restore.create({
102
+ dataDir: fx2.dataDir, storage: storage2(), passphrase: pp,
103
+ rollbackRoot: path.join(fx2.root, "rb"), audit: false, requireSignature: true,
104
+ }).run({ bundleId: r2.bundleId });
105
+ } catch (e) { threwReq = e; }
106
+ check("restore: requireSignature:true refuses an unsigned bundle",
107
+ threwReq && threwReq.code === "restore/missing-signature");
108
+
109
+ // Default (requireSignature not set) restores the unsigned best-effort
110
+ // bundle — the framework's documented CLI/standalone case.
111
+ var okRun = await b.restore.create({
112
+ dataDir: fx2.dataDir, storage: storage2(), passphrase: pp,
113
+ rollbackRoot: path.join(fx2.root, "rb3"), audit: false,
114
+ }).run({ bundleId: r2.bundleId });
115
+ check("restore: default restores an unsigned best-effort bundle", okRun.fileCount === 1);
116
+ } finally {
117
+ fs.rmSync(fx2.root, { recursive: true, force: true });
118
+ }
119
+
120
+ console.log("OK — restore blob-remap + signature policy (" + helpers.getChecks() + " checks)");
121
+ }
122
+
123
+ module.exports = { run: run };
124
+
125
+ if (require.main === module) {
126
+ run().then(function () { process.exit(0); })
127
+ .catch(function (err) { process.exitCode = 1; throw err; });
128
+ }
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ // B8a: the retention sweep loop must TERMINATE even when a full batch of rows is
3
+ // not removed from the candidate set by its action. The loop paged LIMIT-from-
4
+ // the-top and exited only when `rows.length < batchSize`, so any full batch that
5
+ // mutated nothing was re-selected forever:
6
+ // - dryRun (preview) mutates NOTHING → every table with > batchSize past-TTL
7
+ // rows looped infinitely (a preview that never returns — a DoS);
8
+ // - a full batch all on legal-hold (skipped) → re-selected forever;
9
+ // - (same for "warn"-stage and errored rows).
10
+ // The fix adds keyset pagination (ORDER BY _id, _id > cursor) so the loop always
11
+ // advances past rows it has already seen, regardless of whether they were
12
+ // actioned.
13
+ //
14
+ // RED on the buggy tree: run() never returns (the harness times out).
15
+ // GREEN after the fix: each scenario terminates and scans every row exactly once.
16
+
17
+ var fs = require("fs");
18
+ var path = require("path");
19
+ var os = require("os");
20
+ var helpers = require("../helpers");
21
+ var dbHelper = require("../helpers/db");
22
+ var b = helpers.b;
23
+ var check = helpers.check;
24
+
25
+ var N = 5; // rows past TTL
26
+ var BATCH = 2; // < N so the loop must iterate (and, when buggy, re-select)
27
+
28
+ function _seedRows(table, withHold) {
29
+ var cols = "\"_id\" TEXT PRIMARY KEY, \"createdAt\" INTEGER, \"payload\" TEXT, \"__erasedAt\" INTEGER" +
30
+ (withHold ? ", \"onHold\" INTEGER" : "");
31
+ b.db.prepare("CREATE TABLE \"" + table + "\" (" + cols + ")").run();
32
+ var longAgo = Date.now() - b.constants.TIME.days(400);
33
+ for (var i = 0; i < N; i++) {
34
+ if (withHold) {
35
+ b.db.prepare("INSERT INTO \"" + table + "\" (\"_id\", \"createdAt\", \"payload\", \"__erasedAt\", \"onHold\") " +
36
+ "VALUES (?, ?, ?, NULL, 1)").run("r-" + i, longAgo, "p-" + i);
37
+ } else {
38
+ b.db.prepare("INSERT INTO \"" + table + "\" (\"_id\", \"createdAt\", \"payload\", \"__erasedAt\") " +
39
+ "VALUES (?, ?, ?, NULL)").run("r-" + i, longAgo, "p-" + i);
40
+ }
41
+ }
42
+ }
43
+
44
+ async function run() {
45
+ var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-retention-term-"));
46
+ await dbHelper.setupTestDb(tmpDir);
47
+ try {
48
+ // ---- dryRun: mutates nothing → must still terminate, scanning each row once ----
49
+ b.cryptoField.registerTable("retention_dry", { sealedFields: ["payload"], rowIdField: "_id" });
50
+ _seedRows("retention_dry", false);
51
+ var ret1 = b.retention.create({ db: b.db, audit: false });
52
+ ret1.declare({
53
+ name: "dry-ttl", table: "retention_dry", ageField: "createdAt",
54
+ ttlMs: b.constants.TIME.days(90), action: "erase", batchSize: BATCH,
55
+ });
56
+ var preview = await ret1.run("dry-ttl", { dryRun: true });
57
+ check("dryRun sweep TERMINATES (did not infinite-loop)", !!preview);
58
+ check("dryRun scans each row exactly once (no re-selection)", preview.scanned === N);
59
+ check("dryRun would-process every past-TTL row", preview.processed === N);
60
+ // Nothing was actually erased (preview).
61
+ var stillThere = b.db.prepare("SELECT COUNT(*) AS n FROM \"retention_dry\" WHERE \"payload\" IS NOT NULL").get();
62
+ check("dryRun left every row intact", stillThere.n === N);
63
+
64
+ // ---- all-legal-hold: every candidate skipped → must still terminate ----
65
+ b.cryptoField.registerTable("retention_held", { sealedFields: ["payload"], rowIdField: "_id" });
66
+ _seedRows("retention_held", true);
67
+ var ret2 = b.retention.create({ db: b.db, audit: false });
68
+ ret2.declare({
69
+ name: "held-ttl", table: "retention_held", ageField: "createdAt",
70
+ ttlMs: b.constants.TIME.days(90), action: "erase", batchSize: BATCH,
71
+ legalHoldField: "onHold",
72
+ });
73
+ var heldSweep = await ret2.run("held-ttl", { dryRun: false });
74
+ check("all-legal-hold sweep TERMINATES (did not infinite-loop)", !!heldSweep);
75
+ check("all-legal-hold scans each row exactly once", heldSweep.scanned === N);
76
+ check("all-legal-hold honors the hold on every row", heldSweep.legalHoldsHonored === N);
77
+ check("all-legal-hold processed none (all skipped)", heldSweep.processed === 0);
78
+ var heldIntact = b.db.prepare("SELECT COUNT(*) AS n FROM \"retention_held\" WHERE \"payload\" IS NOT NULL").get();
79
+ check("all-legal-hold left every held row intact", heldIntact.n === N);
80
+
81
+ // ---- real erase still works + terminates (no over/under-scan) ----
82
+ b.cryptoField.registerTable("retention_real", { sealedFields: ["payload"], rowIdField: "_id" });
83
+ _seedRows("retention_real", false);
84
+ var ret3 = b.retention.create({ db: b.db, audit: false });
85
+ ret3.declare({
86
+ name: "real-ttl", table: "retention_real", ageField: "createdAt",
87
+ ttlMs: b.constants.TIME.days(90), action: "erase", batchSize: BATCH,
88
+ });
89
+ var realSweep = await ret3.run("real-ttl", { dryRun: false });
90
+ check("real erase sweep terminates + scans each row once", realSweep.scanned === N);
91
+ check("real erase processed every past-TTL row", realSweep.processed === N);
92
+ var erased = b.db.prepare("SELECT COUNT(*) AS n FROM \"retention_real\" WHERE \"payload\" IS NULL").get();
93
+ check("real erase NULLed every sealed payload", erased.n === N);
94
+ } finally {
95
+ await dbHelper.teardownTestDb(tmpDir);
96
+ }
97
+ console.log("OK — retention sweep termination (" + helpers.getChecks() + " checks)");
98
+ }
99
+
100
+ module.exports = { run: run };
101
+ if (require.main === module) {
102
+ run().then(function () { process.exit(0); })
103
+ .catch(function (err) { process.exitCode = 1; throw err; });
104
+ }
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /**
3
+ * b.router — request-body schema validation must run even when no body was
4
+ * parsed. A route declaring `spec.body` is asserting the body is part of the
5
+ * contract; skipping the check when `req.body === undefined` (no body sent /
6
+ * bodyParser absent / empty POST) silently admits a request that omits a
7
+ * required body straight to the handler. Mirrors the always-run params/query
8
+ * checks.
9
+ */
10
+
11
+ var helpers = require("../helpers");
12
+ var b = helpers.b;
13
+ var check = helpers.check;
14
+ var s = b.safeSchema;
15
+
16
+ function _req(method, url) {
17
+ return { method: method, url: url, headers: { host: "localhost" } };
18
+ }
19
+
20
+ function _res() {
21
+ var res = {
22
+ statusCode: 0,
23
+ headersSent: false,
24
+ writableEnded: false,
25
+ _body: "",
26
+ writeHead: function (status, headers) {
27
+ res.statusCode = status;
28
+ res._headers = headers || {};
29
+ res.headersSent = true;
30
+ },
31
+ end: function (chunk) {
32
+ if (chunk !== undefined) res._body += chunk;
33
+ res.writableEnded = true;
34
+ },
35
+ };
36
+ return res;
37
+ }
38
+
39
+ async function testMissingRequiredBodyRejected() {
40
+ var r = b.router.create();
41
+ var handlerRan = false;
42
+ r.post("/items", { body: s.object({ name: s.string() }) }, function (req, res) {
43
+ handlerRan = true;
44
+ res.writeHead(200); res.end("created");
45
+ });
46
+
47
+ // No body was parsed onto the request (req.body === undefined).
48
+ var res = _res();
49
+ await r.handle(_req("POST", "/items"), res);
50
+ check("missing required body → 400", res.statusCode === 400);
51
+ check("missing required body → handler did not run", handlerRan === false);
52
+ check("missing required body → validation error names the body location",
53
+ /"where":"body"/.test(res._body));
54
+ }
55
+
56
+ async function testValidBodyAccepted() {
57
+ var r = b.router.create();
58
+ var seen = null;
59
+ r.post("/items", { body: s.object({ name: s.string() }) }, function (req, res) {
60
+ seen = req.body;
61
+ res.writeHead(200); res.end("created");
62
+ });
63
+
64
+ var req = _req("POST", "/items");
65
+ req.body = { name: "widget" };
66
+ var res = _res();
67
+ await r.handle(req, res);
68
+ check("valid body → 200", res.statusCode === 200);
69
+ check("valid body → handler sees parsed body", seen && seen.name === "widget");
70
+ }
71
+
72
+ async function testOptionalBodyAbsentAccepted() {
73
+ // An explicitly-optional body schema must still pass when no body is sent —
74
+ // the fix validates, it does not require a body unconditionally.
75
+ var r = b.router.create();
76
+ var handlerRan = false;
77
+ r.post("/items", { body: s.object({ name: s.string() }).optional() }, function (req, res) {
78
+ handlerRan = true;
79
+ res.writeHead(200); res.end("ok");
80
+ });
81
+
82
+ var res = _res();
83
+ await r.handle(_req("POST", "/items"), res);
84
+ check("optional body absent → 200", res.statusCode === 200 && handlerRan === true);
85
+ }
86
+
87
+ async function run() {
88
+ await testMissingRequiredBodyRejected();
89
+ await testValidBodyAccepted();
90
+ await testOptionalBodyAbsentAccepted();
91
+ }
92
+
93
+ module.exports = { run: run };
94
+
95
+ if (require.main === module) {
96
+ run().then(function () { console.log("OK router-body-validation — " + helpers.getChecks() + " checks"); })
97
+ .catch(function (e) { console.error("FAIL:", e && e.stack || e); process.exit(1); });
98
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ /**
3
+ * b.safeJson.stringifyForScript — JSON safe to embed verbatim inside an
4
+ * inline <script>. Raw JSON.stringify leaves <, >, & literal, so a value
5
+ * containing "</script>" closes the surrounding element (XSS); the
6
+ * U+2028 / U+2029 separators are also illegal unescaped in a script on
7
+ * older parsers. This escapes all of them to \uXXXX — the parsed value is
8
+ * byte-identical.
9
+ */
10
+
11
+ var helpers = require("../helpers");
12
+ var b = helpers.b;
13
+ var check = helpers.check;
14
+
15
+ function run() {
16
+ // </script> in a value is neutralised but round-trips.
17
+ var payload = { url: "/a</script><script>alert(1)</script>", amp: "a&b", lt: "x<y>z" };
18
+ var out = b.safeJson.stringifyForScript(payload);
19
+ check("no raw </script> survives", out.indexOf("</script>") === -1);
20
+ check("< is escaped to \\u003c", out.indexOf("\\u003c") !== -1 && out.indexOf("<") === -1);
21
+ check("> is escaped to \\u003e", out.indexOf(">") === -1);
22
+ check("& is escaped to \\u0026", out.indexOf("&") === -1);
23
+ check("round-trips to the identical value", JSON.parse(out).url === payload.url &&
24
+ JSON.parse(out).amp === "a&b" && JSON.parse(out).lt === "x<y>z");
25
+
26
+ // U+2028 / U+2029 (built via code point so this source stays ASCII).
27
+ var sep = { s: "a" + String.fromCharCode(0x2028) + "b" + String.fromCharCode(0x2029) + "c" };
28
+ var outSep = b.safeJson.stringifyForScript(sep);
29
+ check("U+2028 escaped to \\u2028", outSep.indexOf("\\u2028") !== -1 &&
30
+ outSep.indexOf(String.fromCharCode(0x2028)) === -1);
31
+ check("U+2029 escaped to \\u2029", outSep.indexOf("\\u2029") !== -1 &&
32
+ outSep.indexOf(String.fromCharCode(0x2029)) === -1);
33
+ check("separators round-trip to the identical value",
34
+ JSON.parse(outSep).s === sep.s);
35
+
36
+ console.log("OK — safeJson.stringifyForScript (" + helpers.getChecks() + " checks)");
37
+ }
38
+
39
+ module.exports = { run: run };
40
+
41
+ if (require.main === module) {
42
+ try { run(); } catch (e) { console.error(e); process.exit(1); }
43
+ }
@@ -85,11 +85,12 @@ function _buildSignedResponse(idp, parts) {
85
85
  parts.nameId + "</saml:NameID>" +
86
86
  subjectConfirmation +
87
87
  "</saml:Subject>" +
88
- "<saml:Conditions NotBefore=\"" + _isoFromNow(-5 * 60 * 1000) + // allow:raw-time-literal — 5m skew window
89
- "\" NotOnOrAfter=\"" + _isoFromNow(5 * 60 * 1000) + "\">" + // allow:raw-time-literal — 5m skew window
90
- "<saml:AudienceRestriction><saml:Audience>" + SP_ENTITY_ID +
91
- "</saml:Audience></saml:AudienceRestriction>" +
92
- "</saml:Conditions>" +
88
+ (parts.conditions !== undefined ? parts.conditions :
89
+ "<saml:Conditions NotBefore=\"" + _isoFromNow(-5 * 60 * 1000) + // allow:raw-time-literal — 5m skew window
90
+ "\" NotOnOrAfter=\"" + _isoFromNow(5 * 60 * 1000) + "\">" + // allow:raw-time-literal — 5m skew window
91
+ "<saml:AudienceRestriction><saml:Audience>" + SP_ENTITY_ID +
92
+ "</saml:Audience></saml:AudienceRestriction>" +
93
+ "</saml:Conditions>") +
93
94
  "<saml:AuthnStatement SessionIndex=\"_sess-1\" AuthnInstant=\"" + issueInstant + "\">" +
94
95
  "<saml:AuthnContext><saml:AuthnContextClassRef>" +
95
96
  "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" +
@@ -273,11 +274,53 @@ async function testHolderOfKeyNotOnOrAfterRefused() {
273
274
  _verifyThrows(sp, b64bad, vopts) === "auth-saml/no-valid-confirmation");
274
275
  }
275
276
 
277
+ // #B0 — a signed assertion with NO AudienceRestriction is not bound to THIS
278
+ // SP. Accepting it is audience-confusion: an IdP-signed assertion minted for
279
+ // another SP is replayed here. Must fail closed (default on).
280
+ async function testMissingAudienceRestrictionRefused() {
281
+ var idp = await _mintRsaCert("idp.example");
282
+ var sp = _newSp(idp);
283
+ var inResponseTo = "_req-noaud";
284
+ var b64 = _buildSignedResponse(idp, {
285
+ tag: "no-aud",
286
+ method: "urn:oasis:names:tc:SAML:2.0:cm:bearer",
287
+ nameId: "u@example.com",
288
+ scd: _bearerScd(" NotOnOrAfter=\"" + _isoFromNow(5 * 60 * 1000) + "\"", inResponseTo), // allow:raw-time-literal — 5m future
289
+ conditions: "<saml:Conditions NotBefore=\"" + _isoFromNow(-5 * 60 * 1000) + // allow:raw-time-literal — 5m skew
290
+ "\" NotOnOrAfter=\"" + _isoFromNow(5 * 60 * 1000) + "\"></saml:Conditions>", // allow:raw-time-literal — NO AudienceRestriction
291
+ });
292
+ check("signed assertion without AudienceRestriction refused (audience-confusion)",
293
+ _verifyThrows(sp, b64, { expectedInResponseTo: inResponseTo }) === "auth-saml/no-audience-restriction");
294
+ }
295
+
296
+ // #B0 — a present-but-unparseable Conditions NotBefore/NotOnOrAfter must fail
297
+ // CLOSED (the Bearer SCD path already does; the Conditions path skipped it via
298
+ // an isFinite() short-circuit, so an unparseable validity window was ignored).
299
+ async function testUnparseableConditionsTimestampRefused() {
300
+ var idp = await _mintRsaCert("idp.example");
301
+ var sp = _newSp(idp);
302
+ var inResponseTo = "_req-badts";
303
+ var b64 = _buildSignedResponse(idp, {
304
+ tag: "bad-ts",
305
+ method: "urn:oasis:names:tc:SAML:2.0:cm:bearer",
306
+ nameId: "u@example.com",
307
+ scd: _bearerScd(" NotOnOrAfter=\"" + _isoFromNow(5 * 60 * 1000) + "\"", inResponseTo), // allow:raw-time-literal — 5m future
308
+ conditions: "<saml:Conditions NotBefore=\"" + _isoFromNow(-5 * 60 * 1000) + // allow:raw-time-literal — valid NotBefore
309
+ "\" NotOnOrAfter=\"not-a-date\">" +
310
+ "<saml:AudienceRestriction><saml:Audience>" + SP_ENTITY_ID +
311
+ "</saml:Audience></saml:AudienceRestriction></saml:Conditions>",
312
+ });
313
+ check("unparseable Conditions NotOnOrAfter refused (fail-closed)",
314
+ _verifyThrows(sp, b64, { expectedInResponseTo: inResponseTo }) === "auth-saml/conditions-bad-timestamp");
315
+ }
316
+
276
317
  async function run() {
277
318
  await testBearerValidNotOnOrAfterAccepted();
278
319
  await testBearerMissingNotOnOrAfterRefused();
279
320
  await testBearerUnparseableNotOnOrAfterRefused();
280
321
  await testHolderOfKeyNotOnOrAfterRefused();
322
+ await testMissingAudienceRestrictionRefused();
323
+ await testUnparseableConditionsTimestampRefused();
281
324
  }
282
325
 
283
326
  if (require.main === module) {
@@ -21,6 +21,7 @@
21
21
  var helpers = require("../helpers");
22
22
  var b = helpers.b;
23
23
  var check = helpers.check;
24
+ var C = require("../../lib/constants");
24
25
 
25
26
  async function testHappyPath() {
26
27
  var r = await b.sandbox.run({
@@ -171,6 +172,27 @@ async function testProcessUnreachable() {
171
172
  }
172
173
  }
173
174
 
175
+ async function testWebAssemblyStripped() {
176
+ // WebAssembly linear memory is off the V8 heap, so the maxBytes-derived
177
+ // heap cap can't bound `new WebAssembly.Memory(...).grow(N)`. WebAssembly
178
+ // must be stripped from the worker global so the grow-and-touch source
179
+ // cannot commit unbounded off-heap RAM (CWE-770). Stripped → the source
180
+ // ReferenceErrors and the call refuses, rather than resolving after
181
+ // committing ~GiB under a 4 MiB cap.
182
+ try {
183
+ await b.sandbox.run({
184
+ source: "var m = new WebAssembly.Memory({ initial: 1 }); m.grow(30000); " +
185
+ "var v = new Uint8Array(m.buffer); for (var i = 0; i < v.length; i += 4096) v[i] = 1; " +
186
+ "return v.length;",
187
+ maxBytes: C.BYTES.mib(4),
188
+ timeoutMs: 9000,
189
+ });
190
+ check("WebAssembly grow should be refused (stripped)", false);
191
+ } catch (e) {
192
+ check("WebAssembly unreachable in sandbox", e && e.code === "sandbox/runtime-error");
193
+ }
194
+ }
195
+
174
196
  async function testNoNetworkAccess() {
175
197
  // require absent means http unreachable; confirm path.
176
198
  try {
@@ -233,6 +255,7 @@ async function run() {
233
255
  await testBadInput();
234
256
  await testContainment();
235
257
  await testProcessUnreachable();
258
+ await testWebAssemblyStripped();
236
259
  await testNoNetworkAccess();
237
260
  await testKnownSafeBuiltins();
238
261
  }
@@ -318,6 +318,58 @@ async function testPresentWithKeyBinding() {
318
318
  result.holderKey && result.holderKey.kty === "EC");
319
319
  }
320
320
 
321
+ async function testKbRequiresReplayBinding() {
322
+ // A KB-JWT presentation verified WITHOUT opts.audience/opts.nonce must fail
323
+ // closed — otherwise the aud/nonce compares silently skip and the
324
+ // presentation is replayable + acceptable at any verifier.
325
+ var issuer = _newKeyPair();
326
+ var holder = _newKeyPair();
327
+ var sd = sdJwtVc.issue({
328
+ issuer: "https://issuer", vct: "x", claims: { x: 1 },
329
+ issuerKey: issuer.privateKey, holderKey: _jwk(holder.publicKey),
330
+ });
331
+ var pres = sdJwtVc.present({
332
+ sdJwt: sd.token, audience: "https://verifier", nonce: "n-1",
333
+ holderKey: holder.privateKey, algorithm: "ES256",
334
+ });
335
+ // No nonce + no audience supplied to verify → must throw.
336
+ var threwNone = null;
337
+ try {
338
+ await sdJwtVc.verify(pres.presentation, {
339
+ issuerKeyResolver: async function () { return issuer.publicKey; },
340
+ requireKeyBinding: true,
341
+ });
342
+ } catch (e) { threwNone = e; }
343
+ check("verify: KB-JWT without audience+nonce fails closed",
344
+ threwNone && threwNone.code === "auth-sd-jwt-vc/missing-replay-binding");
345
+ // audience but no nonce → still throws (replay defense needs the fresh nonce).
346
+ var threwNoNonce = null;
347
+ try {
348
+ await sdJwtVc.verify(pres.presentation, {
349
+ issuerKeyResolver: async function () { return issuer.publicKey; },
350
+ audience: "https://verifier", requireKeyBinding: true,
351
+ });
352
+ } catch (e) { threwNoNonce = e; }
353
+ check("verify: KB-JWT with audience but no nonce fails closed",
354
+ threwNoNonce && threwNoNonce.code === "auth-sd-jwt-vc/missing-replay-binding");
355
+ }
356
+
357
+ async function testRequireExpOpt() {
358
+ // requireExp + expectedIssuer are accepted opts (previously expectedIssuer
359
+ // was consumed but absent from the allowlist → "unknown option"). A normal
360
+ // token (issue() always writes a numeric exp) still verifies under requireExp.
361
+ var issuer = _newKeyPair();
362
+ var sd = sdJwtVc.issue({
363
+ issuer: "https://issuer", vct: "x", claims: { x: 1 }, issuerKey: issuer.privateKey,
364
+ });
365
+ var ok = await sdJwtVc.verify(sd.token, {
366
+ issuerKeyResolver: async function () { return issuer.publicKey; },
367
+ requireExp: true, expectedIssuer: "https://issuer",
368
+ });
369
+ check("verify: requireExp + expectedIssuer accepted; exp-present token verifies",
370
+ ok.valid === true);
371
+ }
372
+
321
373
  async function testKbWrongAudience() {
322
374
  var issuer = _newKeyPair();
323
375
  var holder = _newKeyPair();
@@ -776,6 +828,8 @@ function testExports() {
776
828
  await testVerifyDisclosureMismatch();
777
829
  await testPresentSubsetThenVerify();
778
830
  await testPresentWithKeyBinding();
831
+ await testKbRequiresReplayBinding();
832
+ await testRequireExpOpt();
779
833
  await testKbWrongAudience();
780
834
  await testKbWrongNonce();
781
835
  await testRequireKeyBindingMissing();
@@ -181,6 +181,29 @@ async function testPluggableStore() {
181
181
  }
182
182
  }
183
183
 
184
+ async function testDestroyAllForUserPluggableNoDb() {
185
+ // #340: a pluggable-store consumer who never ran b.db.init() must get a
186
+ // clear, actionable error from destroyAllForUser — not the opaque
187
+ // db/not-initialized that bubbled out of the stateless valid-from bump
188
+ // (which writes to the framework db, not the pluggable session store).
189
+ var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "ses-nodb-"));
190
+ try {
191
+ await helpers.setupVaultOnly(tmpDir); // vault up; b.db deliberately NOT initialized
192
+ b.session.useStore({
193
+ execute: function () { return Promise.resolve({ rowCount: 1 }); },
194
+ executeOne: function () { return Promise.resolve(null); },
195
+ });
196
+ var err = null;
197
+ try { await b.session.destroyAllForUser("u-1"); }
198
+ catch (e) { err = e; }
199
+ check("destroyAllForUser (pluggable store, no b.db) → clear error, not db/not-initialized",
200
+ err !== null && err.code !== "db/not-initialized" && /b\.db\.init\(\)/.test(err.message || ""));
201
+ } finally {
202
+ b.session.useStore(null);
203
+ helpers.teardownVaultOnly(tmpDir);
204
+ }
205
+ }
206
+
184
207
  async function testPluggableStoreValidation() {
185
208
  var threw = false;
186
209
  try { b.session.useStore({ execute: function () {} }); }
@@ -381,6 +404,7 @@ async function run() {
381
404
  await testClientIpPrefixV6();
382
405
  await testClientIpPrefixV4MappedV6();
383
406
  await testPluggableStore();
407
+ await testDestroyAllForUserPluggableNoDb();
384
408
  await testPluggableStoreValidation();
385
409
  await testUpdateDataReplaceAndMerge();
386
410
  await testUpdateDataPreservesFingerprint();
@@ -44,10 +44,11 @@ async function _server() {
44
44
  };
45
45
  }
46
46
 
47
- function _get(port, urlPath) {
47
+ function _get(port, urlPath, headers) {
48
48
  return new Promise(function (resolve, reject) {
49
49
  var req = http.request({
50
50
  hostname: "127.0.0.1", port: port, path: urlPath, method: "GET",
51
+ headers: headers || undefined,
51
52
  }, function (res) {
52
53
  var chunks = [];
53
54
  res.on("data", function (c) { chunks.push(c); });
@@ -484,7 +485,47 @@ async function testDirectoryIndexServed() {
484
485
  }
485
486
  }
486
487
 
488
+ // RFC 7232 §3.3/§6: If-None-Match takes precedence over If-Modified-Since,
489
+ // and If-Match takes precedence over If-Unmodified-Since. When the strong
490
+ // entity-tag precondition is present, the recipient MUST ignore the
491
+ // date-based one — otherwise a changed resource is falsely reported 304, or
492
+ // an unchanged resource falsely 412'd.
493
+ async function testConditionalEntityTagPrecedence() {
494
+ var ctx = await _server();
495
+ _writeFile(ctx.dir, "doc.txt", "hello conditional");
496
+ var srv = await ctx.start({ contentSafety: null });
497
+ try {
498
+ var base = await _get(srv.port, "/doc.txt");
499
+ check("conditional: baseline serves 200 with ETag",
500
+ base.statusCode === 200 && typeof base.headers.etag === "string");
501
+ var future = new Date(Date.now() + 86400000).toUTCString();
502
+ var past = new Date(Date.now() - 86400000).toUTCString();
503
+
504
+ // If-None-Match present + non-matching → resource changed; the
505
+ // If-Modified-Since (future → would 304) must be ignored, serve 200.
506
+ var r1 = await _get(srv.port, "/doc.txt", {
507
+ "If-None-Match": '"stale-does-not-match"',
508
+ "If-Modified-Since": future,
509
+ });
510
+ check("If-None-Match (stale) overrides If-Modified-Since → 200, not 304",
511
+ r1.statusCode === 200);
512
+
513
+ // If-Match: * (matches) + If-Unmodified-Since (past → would 412) must
514
+ // be ignored because If-Match is present and satisfied, serve 200.
515
+ var r2 = await _get(srv.port, "/doc.txt", {
516
+ "If-Match": "*",
517
+ "If-Unmodified-Since": past,
518
+ });
519
+ check("If-Match (*) overrides If-Unmodified-Since → 200, not 412",
520
+ r2.statusCode === 200);
521
+ } finally {
522
+ srv.close();
523
+ ctx.cleanup();
524
+ }
525
+ }
526
+
487
527
  async function run() {
528
+ await testConditionalEntityTagPrecedence();
488
529
  await testForceAttachmentDefaultOff();
489
530
  await testForceAttachmentOnHtml();
490
531
  await testForceAttachmentOnJs();