@blamejs/blamejs-shop 0.4.68 → 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 (298) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/admin.js +8 -6
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/giftcards.js +29 -5
  5. package/lib/security-middleware.js +65 -23
  6. package/lib/storefront.js +10 -7
  7. package/lib/vendor/MANIFEST.json +319 -267
  8. package/lib/vendor/blamejs/CHANGELOG.md +2 -0
  9. package/lib/vendor/blamejs/api-snapshot.json +58 -5
  10. package/lib/vendor/blamejs/examples/wiki/README.md +1 -1
  11. package/lib/vendor/blamejs/examples/wiki/docker-compose.prod.yml +9 -8
  12. package/lib/vendor/blamejs/examples/wiki/docker-compose.yml +10 -9
  13. package/lib/vendor/blamejs/examples/wiki/env-snapshot.json +4 -3
  14. package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +33 -17
  15. package/lib/vendor/blamejs/examples/wiki/routes/admin.js +7 -10
  16. package/lib/vendor/blamejs/examples/wiki/server.js +5 -4
  17. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +5 -0
  18. package/lib/vendor/blamejs/lib/a2a-tasks.js +38 -6
  19. package/lib/vendor/blamejs/lib/agent-event-bus.js +13 -0
  20. package/lib/vendor/blamejs/lib/agent-idempotency.js +5 -1
  21. package/lib/vendor/blamejs/lib/agent-snapshot.js +32 -2
  22. package/lib/vendor/blamejs/lib/ai-aedt-bias-audit.js +2 -1
  23. package/lib/vendor/blamejs/lib/ai-content-detect.js +1 -3
  24. package/lib/vendor/blamejs/lib/ai-frontier-protocol.js +1 -1
  25. package/lib/vendor/blamejs/lib/ai-model-manifest.js +1 -1
  26. package/lib/vendor/blamejs/lib/ai-output.js +16 -7
  27. package/lib/vendor/blamejs/lib/api-snapshot.js +4 -1
  28. package/lib/vendor/blamejs/lib/app-shutdown.js +7 -1
  29. package/lib/vendor/blamejs/lib/archive-gz.js +9 -0
  30. package/lib/vendor/blamejs/lib/archive-read.js +9 -7
  31. package/lib/vendor/blamejs/lib/archive-tar-read.js +51 -8
  32. package/lib/vendor/blamejs/lib/archive.js +4 -2
  33. package/lib/vendor/blamejs/lib/asn1-der.js +70 -22
  34. package/lib/vendor/blamejs/lib/atomic-file.js +204 -2
  35. package/lib/vendor/blamejs/lib/audit-chain.js +54 -5
  36. package/lib/vendor/blamejs/lib/audit-daily-review.js +12 -2
  37. package/lib/vendor/blamejs/lib/audit-sign.js +2 -1
  38. package/lib/vendor/blamejs/lib/audit-tools.js +108 -23
  39. package/lib/vendor/blamejs/lib/audit.js +7 -2
  40. package/lib/vendor/blamejs/lib/auth/access-lock.js +2 -2
  41. package/lib/vendor/blamejs/lib/auth/bot-challenge.js +1 -1
  42. package/lib/vendor/blamejs/lib/auth/ciba.js +43 -4
  43. package/lib/vendor/blamejs/lib/auth/dpop.js +6 -1
  44. package/lib/vendor/blamejs/lib/auth/fido-mds3.js +5 -1
  45. package/lib/vendor/blamejs/lib/auth/jwt.js +2 -2
  46. package/lib/vendor/blamejs/lib/auth/lockout.js +18 -2
  47. package/lib/vendor/blamejs/lib/auth/passkey.js +1 -1
  48. package/lib/vendor/blamejs/lib/auth/password.js +1 -1
  49. package/lib/vendor/blamejs/lib/auth/saml.js +33 -13
  50. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +24 -4
  51. package/lib/vendor/blamejs/lib/auth/status-list.js +14 -2
  52. package/lib/vendor/blamejs/lib/auth/step-up.js +9 -1
  53. package/lib/vendor/blamejs/lib/auth-bot-challenge.js +21 -2
  54. package/lib/vendor/blamejs/lib/backup/bundle.js +7 -2
  55. package/lib/vendor/blamejs/lib/backup/crypto.js +23 -8
  56. package/lib/vendor/blamejs/lib/backup/index.js +41 -20
  57. package/lib/vendor/blamejs/lib/backup/manifest.js +7 -1
  58. package/lib/vendor/blamejs/lib/break-glass.js +41 -22
  59. package/lib/vendor/blamejs/lib/cbor.js +34 -11
  60. package/lib/vendor/blamejs/lib/cdn-cache-control.js +7 -3
  61. package/lib/vendor/blamejs/lib/cert.js +5 -3
  62. package/lib/vendor/blamejs/lib/cli.js +5 -1
  63. package/lib/vendor/blamejs/lib/cloud-events.js +3 -2
  64. package/lib/vendor/blamejs/lib/cluster-storage.js +7 -3
  65. package/lib/vendor/blamejs/lib/codepoint-class.js +17 -0
  66. package/lib/vendor/blamejs/lib/compliance-eaa.js +1 -1
  67. package/lib/vendor/blamejs/lib/compliance-sanctions.js +9 -7
  68. package/lib/vendor/blamejs/lib/compliance.js +1 -1
  69. package/lib/vendor/blamejs/lib/config-drift.js +22 -8
  70. package/lib/vendor/blamejs/lib/content-credentials.js +11 -8
  71. package/lib/vendor/blamejs/lib/content-digest.js +11 -4
  72. package/lib/vendor/blamejs/lib/cookies.js +10 -2
  73. package/lib/vendor/blamejs/lib/cose.js +20 -0
  74. package/lib/vendor/blamejs/lib/crdt.js +2 -1
  75. package/lib/vendor/blamejs/lib/crypto-field.js +29 -23
  76. package/lib/vendor/blamejs/lib/crypto.js +18 -4
  77. package/lib/vendor/blamejs/lib/csp.js +4 -0
  78. package/lib/vendor/blamejs/lib/daemon.js +4 -1
  79. package/lib/vendor/blamejs/lib/data-act.js +27 -4
  80. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +14 -6
  81. package/lib/vendor/blamejs/lib/db-query.js +69 -9
  82. package/lib/vendor/blamejs/lib/db.js +32 -15
  83. package/lib/vendor/blamejs/lib/dora.js +43 -13
  84. package/lib/vendor/blamejs/lib/dr-runbook.js +1 -1
  85. package/lib/vendor/blamejs/lib/dsa.js +2 -1
  86. package/lib/vendor/blamejs/lib/dsr.js +22 -8
  87. package/lib/vendor/blamejs/lib/early-hints.js +19 -0
  88. package/lib/vendor/blamejs/lib/eat.js +5 -1
  89. package/lib/vendor/blamejs/lib/external-db.js +60 -4
  90. package/lib/vendor/blamejs/lib/fda-21cfr11.js +30 -5
  91. package/lib/vendor/blamejs/lib/flag-providers.js +6 -2
  92. package/lib/vendor/blamejs/lib/forms.js +1 -1
  93. package/lib/vendor/blamejs/lib/gate-contract.js +46 -5
  94. package/lib/vendor/blamejs/lib/gdpr-ropa.js +18 -9
  95. package/lib/vendor/blamejs/lib/graphql-federation.js +17 -4
  96. package/lib/vendor/blamejs/lib/guard-all.js +2 -2
  97. package/lib/vendor/blamejs/lib/guard-dsn.js +1 -1
  98. package/lib/vendor/blamejs/lib/guard-envelope.js +1 -1
  99. package/lib/vendor/blamejs/lib/guard-html.js +9 -11
  100. package/lib/vendor/blamejs/lib/guard-imap-command.js +1 -1
  101. package/lib/vendor/blamejs/lib/guard-jmap.js +1 -1
  102. package/lib/vendor/blamejs/lib/guard-json.js +14 -6
  103. package/lib/vendor/blamejs/lib/guard-mail-move.js +1 -1
  104. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +1 -1
  105. package/lib/vendor/blamejs/lib/guard-pop3-command.js +1 -1
  106. package/lib/vendor/blamejs/lib/guard-smtp-command.js +1 -1
  107. package/lib/vendor/blamejs/lib/guard-svg.js +8 -9
  108. package/lib/vendor/blamejs/lib/html-balance.js +7 -3
  109. package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +33 -12
  110. package/lib/vendor/blamejs/lib/http-client.js +225 -53
  111. package/lib/vendor/blamejs/lib/iab-tcf.js +3 -2
  112. package/lib/vendor/blamejs/lib/importmap-integrity.js +41 -1
  113. package/lib/vendor/blamejs/lib/incident-report.js +9 -6
  114. package/lib/vendor/blamejs/lib/json-patch.js +1 -1
  115. package/lib/vendor/blamejs/lib/json-path.js +24 -3
  116. package/lib/vendor/blamejs/lib/jtd.js +2 -2
  117. package/lib/vendor/blamejs/lib/legal-hold.js +24 -8
  118. package/lib/vendor/blamejs/lib/log.js +2 -2
  119. package/lib/vendor/blamejs/lib/mail-agent.js +2 -2
  120. package/lib/vendor/blamejs/lib/mail-arf.js +1 -1
  121. package/lib/vendor/blamejs/lib/mail-auth.js +3 -3
  122. package/lib/vendor/blamejs/lib/mail-bimi.js +16 -16
  123. package/lib/vendor/blamejs/lib/mail-bounce.js +3 -3
  124. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +71 -6
  125. package/lib/vendor/blamejs/lib/mail-deploy.js +9 -5
  126. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -4
  127. package/lib/vendor/blamejs/lib/mail-helo.js +2 -4
  128. package/lib/vendor/blamejs/lib/mail-journal.js +11 -8
  129. package/lib/vendor/blamejs/lib/mail-mdn.js +8 -4
  130. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -4
  131. package/lib/vendor/blamejs/lib/mail-scan.js +3 -5
  132. package/lib/vendor/blamejs/lib/mail-server-jmap.js +4 -1
  133. package/lib/vendor/blamejs/lib/mail-server-registry.js +1 -1
  134. package/lib/vendor/blamejs/lib/mail-server-tls.js +9 -2
  135. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -4
  136. package/lib/vendor/blamejs/lib/mail-store-fts.js +1 -1
  137. package/lib/vendor/blamejs/lib/mail.js +22 -2
  138. package/lib/vendor/blamejs/lib/markup-tokenizer.js +24 -0
  139. package/lib/vendor/blamejs/lib/mcp.js +6 -4
  140. package/lib/vendor/blamejs/lib/mdoc.js +26 -3
  141. package/lib/vendor/blamejs/lib/metrics.js +14 -3
  142. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +2 -2
  143. package/lib/vendor/blamejs/lib/middleware/body-parser.js +10 -4
  144. package/lib/vendor/blamejs/lib/middleware/bot-guard.js +26 -18
  145. package/lib/vendor/blamejs/lib/middleware/clear-site-data.js +5 -1
  146. package/lib/vendor/blamejs/lib/middleware/compression.js +9 -0
  147. package/lib/vendor/blamejs/lib/middleware/cors.js +32 -23
  148. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +60 -21
  149. package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +6 -4
  150. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +28 -4
  151. package/lib/vendor/blamejs/lib/middleware/network-allowlist.js +61 -30
  152. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +25 -16
  153. package/lib/vendor/blamejs/lib/middleware/scim-server.js +2 -1
  154. package/lib/vendor/blamejs/lib/middleware/security-headers.js +24 -6
  155. package/lib/vendor/blamejs/lib/middleware/speculation-rules.js +6 -3
  156. package/lib/vendor/blamejs/lib/middleware/tus-upload.js +2 -2
  157. package/lib/vendor/blamejs/lib/money.js +1 -1
  158. package/lib/vendor/blamejs/lib/mtls-ca.js +10 -6
  159. package/lib/vendor/blamejs/lib/network-dns-resolver.js +1 -1
  160. package/lib/vendor/blamejs/lib/network-dns.js +9 -2
  161. package/lib/vendor/blamejs/lib/network-dnssec.js +2 -1
  162. package/lib/vendor/blamejs/lib/network-smtp-policy.js +23 -5
  163. package/lib/vendor/blamejs/lib/network-tls.js +27 -5
  164. package/lib/vendor/blamejs/lib/network-tsig.js +2 -2
  165. package/lib/vendor/blamejs/lib/nis2-report.js +1 -1
  166. package/lib/vendor/blamejs/lib/nist-crosswalk.js +1 -1
  167. package/lib/vendor/blamejs/lib/ntp-check.js +28 -0
  168. package/lib/vendor/blamejs/lib/numeric-bounds.js +9 -0
  169. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +1 -2
  170. package/lib/vendor/blamejs/lib/object-store/gcs-bucket-ops.js +4 -2
  171. package/lib/vendor/blamejs/lib/object-store/gcs.js +6 -4
  172. package/lib/vendor/blamejs/lib/object-store/http-put.js +1 -2
  173. package/lib/vendor/blamejs/lib/object-store/http-request.js +30 -1
  174. package/lib/vendor/blamejs/lib/object-store/local.js +37 -17
  175. package/lib/vendor/blamejs/lib/object-store/sigv4.js +1 -2
  176. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +20 -4
  177. package/lib/vendor/blamejs/lib/outbox.js +11 -4
  178. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +1 -1
  179. package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +21 -3
  180. package/lib/vendor/blamejs/lib/queue-local.js +10 -3
  181. package/lib/vendor/blamejs/lib/redact.js +7 -3
  182. package/lib/vendor/blamejs/lib/request-helpers.js +201 -23
  183. package/lib/vendor/blamejs/lib/resource-access-lock.js +3 -3
  184. package/lib/vendor/blamejs/lib/restore-bundle.js +46 -18
  185. package/lib/vendor/blamejs/lib/restore-rollback.js +10 -4
  186. package/lib/vendor/blamejs/lib/restore.js +19 -0
  187. package/lib/vendor/blamejs/lib/retention.js +20 -4
  188. package/lib/vendor/blamejs/lib/router.js +17 -4
  189. package/lib/vendor/blamejs/lib/safe-ical.js +2 -2
  190. package/lib/vendor/blamejs/lib/safe-icap.js +1 -1
  191. package/lib/vendor/blamejs/lib/safe-json.js +44 -0
  192. package/lib/vendor/blamejs/lib/safe-sieve.js +1 -1
  193. package/lib/vendor/blamejs/lib/safe-vcard.js +1 -1
  194. package/lib/vendor/blamejs/lib/sandbox-worker.js +6 -0
  195. package/lib/vendor/blamejs/lib/sandbox.js +1 -1
  196. package/lib/vendor/blamejs/lib/scheduler.js +17 -1
  197. package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +16 -0
  198. package/lib/vendor/blamejs/lib/session.js +27 -3
  199. package/lib/vendor/blamejs/lib/sql.js +3 -3
  200. package/lib/vendor/blamejs/lib/static.js +65 -13
  201. package/lib/vendor/blamejs/lib/template.js +7 -5
  202. package/lib/vendor/blamejs/lib/tenant-quota.js +52 -19
  203. package/lib/vendor/blamejs/lib/tsa.js +5 -2
  204. package/lib/vendor/blamejs/lib/vault/index.js +5 -0
  205. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +22 -26
  206. package/lib/vendor/blamejs/lib/vault/passphrase-source.js +8 -3
  207. package/lib/vendor/blamejs/lib/vault/rotate.js +13 -18
  208. package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +4 -1
  209. package/lib/vendor/blamejs/lib/vc.js +1 -1
  210. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +10 -10
  211. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +23 -10
  212. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5498 -5494
  213. package/lib/vendor/blamejs/lib/webhook.js +16 -1
  214. package/lib/vendor/blamejs/lib/websocket.js +1 -1
  215. package/lib/vendor/blamejs/lib/worm.js +1 -1
  216. package/lib/vendor/blamejs/lib/ws-client.js +57 -46
  217. package/lib/vendor/blamejs/lib/x509-chain.js +44 -0
  218. package/lib/vendor/blamejs/package.json +1 -1
  219. package/lib/vendor/blamejs/release-notes/v0.15.14.json +122 -0
  220. package/lib/vendor/blamejs/scripts/check-vendor-currency.js +119 -4
  221. package/lib/vendor/blamejs/test/00-primitives.js +5 -1
  222. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +18 -4
  223. package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +42 -0
  224. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +36 -0
  225. package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
  226. package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +47 -0
  227. package/lib/vendor/blamejs/test/layer-0-primitives/archive-gz.test.js +24 -0
  228. package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-hardening.test.js +160 -0
  229. package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +45 -0
  230. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-fd-read.test.js +30 -0
  231. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-open-nofollow.test.js +79 -0
  232. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-write-excl.test.js +132 -0
  233. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-write-stream.test.js +181 -0
  234. package/lib/vendor/blamejs/test/layer-0-primitives/audit-chain-corrupted-anchor.test.js +65 -0
  235. package/lib/vendor/blamejs/test/layer-0-primitives/audit-chain-incremental-verify.test.js +83 -0
  236. package/lib/vendor/blamejs/test/layer-0-primitives/audit-daily-review.test.js +48 -1
  237. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +13 -3
  238. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +122 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge.test.js +37 -0
  240. package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +5 -3
  241. package/lib/vendor/blamejs/test/layer-0-primitives/auth-lockout.test.js +21 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +73 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/bot-guard.test.js +17 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +62 -3
  245. package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +12 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/cluster-storage.test.js +40 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +146 -5
  248. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +14 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/compression-range.test.js +115 -0
  250. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +17 -5
  251. package/lib/vendor/blamejs/test/layer-0-primitives/cors.test.js +27 -5
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +83 -0
  253. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +58 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/data-act.test.js +30 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +74 -0
  256. package/lib/vendor/blamejs/test/layer-0-primitives/dora.test.js +20 -6
  257. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-alg-kty.test.js +64 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +32 -0
  259. package/lib/vendor/blamejs/test/layer-0-primitives/fda-21cfr11.test.js +43 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +30 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +33 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +34 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +12 -0
  264. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +72 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-throttle-transform.test.js +88 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/importmap-integrity.test.js +25 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +28 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +20 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/mail-header-injection.test.js +58 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +63 -9
  271. package/lib/vendor/blamejs/test/layer-0-primitives/mdoc.test.js +41 -14
  272. package/lib/vendor/blamejs/test/layer-0-primitives/network-allowlist.test.js +61 -0
  273. package/lib/vendor/blamejs/test/layer-0-primitives/object-store-range-header.test.js +51 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +35 -0
  275. package/lib/vendor/blamejs/test/layer-0-primitives/output-header-hardening.test.js +102 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/parser-verify-hardening.test.js +191 -0
  277. package/lib/vendor/blamejs/test/layer-0-primitives/proto-shadow-allowlist.test.js +120 -0
  278. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +75 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +128 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/restore-blob-remap.test.js +128 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/retention-sweep-termination.test.js +104 -0
  282. package/lib/vendor/blamejs/test/layer-0-primitives/router-body-validation.test.js +98 -0
  283. package/lib/vendor/blamejs/test/layer-0-primitives/safe-json-stringify-for-script.test.js +43 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +48 -5
  285. package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +23 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +54 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +24 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +42 -1
  289. package/lib/vendor/blamejs/test/layer-0-primitives/step-up.test.js +7 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/template-escape-html.test.js +43 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +15 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/vault-default-store.test.js +48 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/vendor-currency-classify.test.js +77 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +50 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +22 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/x509-chain-ca-enforcement.test.js +149 -0
  297. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +34 -0
  298. package/package.json +1 -1
@@ -42,11 +42,36 @@ function testImportmapErrorClass() {
42
42
  check("ImportmapError carries code", e.code === "importmap/test");
43
43
  }
44
44
 
45
+ function testScriptTagEscapesScriptBreakout() {
46
+ // A module url containing "</script>" must NOT close the injected
47
+ // <script type="importmap"> element. scriptTag uses the <script>-safe
48
+ // serializer; raw JSON.stringify would leave the "</script>" literal.
49
+ var im = b.importmapIntegrity.build({
50
+ modules: { "x": { url: "/a</script><script>alert(1)</script>", body: "code" } },
51
+ });
52
+ var tag = b.importmapIntegrity.scriptTag(im);
53
+ // Exactly one "</script>" — the tag's own close. The url's "</script>"
54
+ // must be escaped.
55
+ check("scriptTag: only the tag's own </script> is present (url breakout escaped)",
56
+ tag.split("</script>").length - 1 === 1);
57
+ check("scriptTag: the url's < is escaped to \\u003c",
58
+ tag.indexOf("u003c/script") !== -1 && tag.indexOf("/a</script>") === -1);
59
+ // The embedded JSON still parses and yields the original url.
60
+ var jsonText = tag.replace(/^<script[^>]*>/, "").replace(/<\/script>$/, "");
61
+ check("scriptTag: embedded JSON round-trips to the original url",
62
+ JSON.parse(jsonText).imports.x === "/a</script><script>alert(1)</script>");
63
+ // nonce validation: a malformed nonce is refused, not escaped into the tag.
64
+ var threw = null;
65
+ try { b.importmapIntegrity.scriptTag(im, { nonce: '" onload="x' }); } catch (e) { threw = e; }
66
+ check("scriptTag: malformed nonce is refused", threw && threw.code === "importmap/bad-nonce");
67
+ }
68
+
45
69
  function run() {
46
70
  testBuildShape();
47
71
  testBuildSha256();
48
72
  testRefusesBadHash();
49
73
  testImportmapErrorClass();
74
+ testScriptTagEscapesScriptBreakout();
50
75
  }
51
76
 
52
77
  if (require.main === module) {
@@ -137,6 +137,34 @@ async function run() {
137
137
  var resRaw = JSON.stringify(b.db.prepare("SELECT reason FROM \"_blamejs_subject_restrictions\"").all());
138
138
  check("#114 subject-restriction reason is sealed at rest (not plaintext)", resRaw.indexOf(RES_SECRET) === -1);
139
139
 
140
+ // ---- B8b: a LAPSED hold must be renewable via place() ----
141
+ // place() rejected "already-held" whenever ANY row existed — including a hold
142
+ // whose retainUntil sunset had passed (isHeld already false). That left a
143
+ // subject both unprotected (isHeld false) AND unable to receive a fresh hold
144
+ // when new litigation arose. A lapsed hold must be replaceable; an ACTIVE one
145
+ // still rejects.
146
+ var lapsedPlace = holds.place("lapse-user", {
147
+ reason: "original matter (sunset already passed)",
148
+ retainUntil: Date.now() - b.constants.TIME.days(1), // born lapsed
149
+ });
150
+ check("B8b: placing a hold with a past retainUntil succeeds", lapsedPlace.placed === true);
151
+ check("B8b: a lapsed hold reads as NOT held", holds.isHeld("lapse-user") === false);
152
+
153
+ var renew = holds.place("lapse-user", {
154
+ reason: "fresh litigation — renewed hold",
155
+ citation: "FRCP-26",
156
+ });
157
+ check("B8b: place() RENEWS a lapsed hold (not rejected already-held)", renew.placed === true);
158
+ check("B8b: renewal is flagged renewedFromLapsed", renew.renewedFromLapsed === true);
159
+ check("B8b: subject is held again after renewal", holds.isHeld("lapse-user") === true);
160
+ check("B8b: the renewed hold carries the new reason",
161
+ holds.get("lapse-user").reason === "fresh litigation — renewed hold");
162
+
163
+ // Control: an ACTIVE (non-lapsed) hold still rejects a second place().
164
+ var activeReplace = holds.place("lapse-user", { reason: "duplicate attempt" });
165
+ check("B8b: re-placing an ACTIVE hold is still rejected already-held",
166
+ activeReplace.error === "already-held");
167
+
140
168
  await dbHelper.teardownTestDb(tmpDir);
141
169
  }
142
170
 
@@ -299,8 +299,28 @@ function testSmimeCertKeyBinding() {
299
299
  smime._certKeyMatches({ publicKey: { export: function () { throw new Error("nope"); } } }, spkiA) === false);
300
300
  }
301
301
 
302
+ function testSmimeContentTypeAttrExtraction() {
303
+ // RFC 5652 §11.1: verify() binds the contentType signed-attr to the
304
+ // eContentType. _extractContentTypeOid must surface a present contentType
305
+ // OID and return null when the attribute is absent (which verify rejects).
306
+ var asn1 = require("../../lib/asn1-der");
307
+ var OID_CT_ATTR = "1.2.840.113549.1.9.3";
308
+ var OID_MD_ATTR = "1.2.840.113549.1.9.4";
309
+ var OID_ID_DATA = "1.2.840.113549.1.7.1";
310
+ function setOf(buf) { return asn1.writeNode(0x31, buf); } // SET
311
+ var ctAttr = asn1.writeSequence([asn1.writeOid(OID_CT_ATTR), setOf(asn1.writeOid(OID_ID_DATA))]);
312
+ var mdAttr = asn1.writeSequence([asn1.writeOid(OID_MD_ATTR), setOf(asn1.writeOctetString(Buffer.alloc(32)))]);
313
+ var withCt = setOf(Buffer.concat([mdAttr, ctAttr]));
314
+ var withoutCt = setOf(mdAttr);
315
+ check("smime contentType: present attribute extracts its eContentType OID",
316
+ smime._extractContentTypeOid(withCt) === OID_ID_DATA);
317
+ check("smime contentType: absent attribute yields null (verify then refuses)",
318
+ smime._extractContentTypeOid(withoutCt) === null);
319
+ }
320
+
302
321
  function run() {
303
322
  testSmimeSurface();
323
+ testSmimeContentTypeAttrExtraction();
304
324
  testSmimeCertKeyBinding();
305
325
  testSmimeSignVerifyRoundtrip();
306
326
  testSmimeVerifyTamperRefused();
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ /**
3
+ * b.mail — outbound header injection (CWE-93 / RFC 5322). Reply-To and custom
4
+ * header KEYS reach the wire in _buildRfc822; a CRLF in either smuggles
5
+ * arbitrary headers (Bcc / Reply-To override / Content-Type). _validateMessage
6
+ * must fail closed on CRLF in replyTo, header keys, and header values.
7
+ */
8
+
9
+ var helpers = require("../helpers");
10
+ var b = helpers.b;
11
+ var check = helpers.check;
12
+
13
+ function _mailer() {
14
+ return b.mail.create({
15
+ transport: function () { return Promise.resolve({ ok: true }); },
16
+ audit: false,
17
+ });
18
+ }
19
+ function _base() {
20
+ return { from: "sender@example.com", to: "rcpt@example.com", subject: "hi", text: "hello" };
21
+ }
22
+ async function _expectThrow(label, message, codeMatch) {
23
+ var m = _mailer();
24
+ try { await m.send(message); check(label + " (did not throw)", false); }
25
+ catch (e) { check(label, (e && e.code || "").indexOf(codeMatch) !== -1); }
26
+ }
27
+
28
+ async function testCleanSends() {
29
+ var m = _mailer();
30
+ var sent = true;
31
+ try { await m.send(_base()); } catch (_e) { sent = false; }
32
+ check("clean message sends", sent === true);
33
+ }
34
+ async function testReplyToCrlfRefused() {
35
+ await _expectThrow("Reply-To CRLF refused (header injection)",
36
+ Object.assign(_base(), { replyTo: "a@b.com\r\nBcc: evil@x.com" }), "mail/invalid-reply-to");
37
+ }
38
+ async function testHeaderKeyCrlfRefused() {
39
+ await _expectThrow("header KEY CRLF refused (header injection)",
40
+ Object.assign(_base(), { headers: { "X-Foo\r\nBcc: evil@x.com": "v" } }), "mail/invalid-header");
41
+ }
42
+ async function testHeaderValueCrlfRefused() {
43
+ await _expectThrow("header VALUE CRLF refused (header injection)",
44
+ Object.assign(_base(), { headers: { "X-Foo": "v\r\nBcc: evil@x.com" } }), "mail/invalid-header");
45
+ }
46
+
47
+ async function run() {
48
+ await testCleanSends();
49
+ await testReplyToCrlfRefused();
50
+ await testHeaderKeyCrlfRefused();
51
+ await testHeaderValueCrlfRefused();
52
+ console.log("[mail-header-injection] OK — " + helpers.getChecks() + " checks passed");
53
+ }
54
+
55
+ module.exports = { run: run };
56
+ if (require.main === module) {
57
+ run().then(function () {}, function (e) { console.error("FAIL: " + helpers.formatErr(e)); process.exit(1); });
58
+ }
@@ -32,14 +32,14 @@ function testBadInput() {
32
32
  function () { b.mail.journal.create({
33
33
  storage: { putObject: function () {} },
34
34
  vault: { seal: function () {} },
35
- legalHold: { isOnHold: function () {} },
35
+ legalHold: { isHeld: function () {} },
36
36
  db: { runSql: function () {} },
37
37
  }); });
38
38
  expectThrow("refuses unknown regime",
39
39
  function () { b.mail.journal.create({
40
40
  storage: { putObject: function () {} },
41
41
  vault: { seal: function () {} },
42
- legalHold: { isOnHold: function () {} },
42
+ legalHold: { isHeld: function () {} },
43
43
  db: { runSql: function () {} },
44
44
  regimes: ["bogus-regime"],
45
45
  }); });
@@ -47,7 +47,7 @@ function testBadInput() {
47
47
  function () { b.mail.journal.create({
48
48
  storage: { putObject: function () {} },
49
49
  vault: { seal: function () {} },
50
- legalHold: { isOnHold: function () {} },
50
+ legalHold: { isHeld: function () {} },
51
51
  db: { runSql: function () {} },
52
52
  regimes: ["hipaa"],
53
53
  namespace: "bad/slash",
@@ -64,7 +64,7 @@ function testRegimeFloorMath() {
64
64
  var calls = [];
65
65
  var db = { runSql: function (sql, args) { calls.push({ sql: sql.slice(0, 40), args: args }); return []; } };
66
66
  var vault = { seal: function (x) { return x; } };
67
- var legalHold = { isOnHold: function () { return false; } };
67
+ var legalHold = { isHeld: function () { return false; } };
68
68
 
69
69
  var j = b.mail.journal.create({
70
70
  storage: storage, vault: vault, legalHold: legalHold, db: db,
@@ -94,7 +94,7 @@ function testCreatesIndexTable() {
94
94
  b.mail.journal.create({
95
95
  storage: { putObject: async function () {} },
96
96
  vault: { seal: function () {} },
97
- legalHold: { isOnHold: function () { return false; } },
97
+ legalHold: { isHeld: function () { return false; } },
98
98
  db: db,
99
99
  regimes: ["hipaa"],
100
100
  namespace: "compliance",
@@ -120,7 +120,7 @@ function testIndexNamesValidSql() {
120
120
  b.mail.journal.create({
121
121
  storage: { putObject: async function () {} },
122
122
  vault: { seal: function () {} },
123
- legalHold: { isOnHold: function () { return false; } },
123
+ legalHold: { isHeld: function () { return false; } },
124
124
  db: db,
125
125
  regimes: ["hipaa"],
126
126
  namespace: "compliance",
@@ -154,11 +154,14 @@ function testSealUnsealRoundTrip() {
154
154
  var db = {
155
155
  runSql: function (sql, args) {
156
156
  if (/INSERT INTO/.test(sql)) {
157
+ // INSERT value order (b.sql binds in object-key order): journal_id,
158
+ // direction, actor_id, message_id, archived_at, size_bytes, regimes,
159
+ // floor_until, legal_hold, storage_key, sealed_payload.
157
160
  insertedRow = {
158
161
  journal_id: args[0], direction: args[1], actor_id: args[2],
159
162
  message_id: args[3], archived_at: args[4], size_bytes: args[5],
160
- regimes: args[6], floor_until: args[7], legal_hold: 0,
161
- storage_key: args[8], sealed_payload: args[9],
163
+ regimes: args[6], floor_until: args[7], legal_hold: args[8],
164
+ storage_key: args[9], sealed_payload: args[10],
162
165
  };
163
166
  return [];
164
167
  }
@@ -172,7 +175,7 @@ function testSealUnsealRoundTrip() {
172
175
  };
173
176
  var j = b.mail.journal.create({
174
177
  storage: storage, vault: fakeVault,
175
- legalHold: { isOnHold: function () { return false; } },
178
+ legalHold: { isHeld: function () { return false; } },
176
179
  db: db, regimes: ["hipaa"], namespace: "rt",
177
180
  });
178
181
  return j.record({
@@ -200,6 +203,56 @@ function testSealUnsealRoundTrip() {
200
203
  });
201
204
  }
202
205
 
206
+ // B8c: the real b.legalHold handle exposes isHeld, NOT isOnHold. create()
207
+ // required isOnHold, so the documented `legalHold: b.legalHold` wiring threw
208
+ // mail-journal/bad-legal-hold and the handle could never be used; the legal_hold
209
+ // flag was also hardcoded 0, never derived from the registry. Fix: require
210
+ // isHeld and auto-flag an entry whose actor is under an active hold. The prior
211
+ // tests stubbed { isOnHold } — fabricating the broken contract — so they never
212
+ // exercised the real handle's shape; this drives the actual consumer contract.
213
+ async function testRealLegalHoldContractAndAutoFlag() {
214
+ var fakeDb = { prepare: function () {
215
+ return { get: function () { return null; }, run: function () {}, all: function () { return []; } };
216
+ } };
217
+ var realHandle = b.legalHold.create({ db: fakeDb, audit: false });
218
+ check("a real b.legalHold handle exposes isHeld (the method create() consumes)",
219
+ typeof realHandle.isHeld === "function");
220
+ check("a real b.legalHold handle does NOT expose the old isOnHold name",
221
+ typeof realHandle.isOnHold === "undefined");
222
+
223
+ var heldActor = "held-subject";
224
+ var legalHold = { isHeld: function (sid) { return sid === heldActor; } };
225
+ var inserted = [];
226
+ var fakeVault = { seal: function (s) { return "vault:" + s; }, unseal: function (s) { return s.slice(6); } };
227
+ var storage = { putObject: async function () {} };
228
+ var db = { runSql: function (sqlText, args) { if (/INSERT INTO/.test(sqlText)) inserted.push(args); return []; } };
229
+
230
+ var j = b.mail.journal.create({
231
+ storage: storage, vault: fakeVault, legalHold: legalHold, db: db,
232
+ regimes: ["hipaa"], namespace: "hold",
233
+ });
234
+ // legal_hold is the 9th bound value (index 8) — see the INSERT order above.
235
+ await j.record({ direction: "inbound", actorId: heldActor, messageId: "<h@x>",
236
+ headers: {}, envelope: {}, bodyBytes: Buffer.from("x") });
237
+ check("entry whose actor is on legal hold is auto-flagged (legal_hold = 1)",
238
+ inserted.length === 1 && inserted[0][8] === 1);
239
+ await j.record({ direction: "inbound", actorId: "free-subject", messageId: "<f@x>",
240
+ headers: {}, envelope: {}, bodyBytes: Buffer.from("y") });
241
+ check("entry whose actor is NOT held is legal_hold = 0",
242
+ inserted.length === 2 && inserted[1][8] === 0);
243
+
244
+ // create() must REJECT a legacy { isOnHold } handle (the wrong contract).
245
+ var threw = null;
246
+ try {
247
+ b.mail.journal.create({
248
+ storage: storage, vault: fakeVault, db: db, regimes: ["hipaa"], namespace: "legacy",
249
+ legalHold: { isOnHold: function () { return false; } },
250
+ });
251
+ } catch (e) { threw = e; }
252
+ check("create() rejects a handle exposing only the legacy isOnHold",
253
+ threw && (threw.code || "").indexOf("mail-journal/bad-legal-hold") === 0);
254
+ }
255
+
203
256
  async function run() {
204
257
  testSurface();
205
258
  testBadInput();
@@ -207,6 +260,7 @@ async function run() {
207
260
  testCreatesIndexTable();
208
261
  testIndexNamesValidSql();
209
262
  await testSealUnsealRoundTrip();
263
+ await testRealLegalHoldContractAndAutoFlag();
210
264
  }
211
265
 
212
266
  module.exports = { run: run };
@@ -99,7 +99,7 @@ function testSurface() {
99
99
  async function testRoundTrip() {
100
100
  var cert = _makeCert();
101
101
  var mdoc = await _makeMdoc(cert);
102
- var out = await b.mdoc.verifyIssuerSigned(mdoc, { algorithms: ["ES256"], expectedDocType: DOCTYPE });
102
+ var out = await b.mdoc.verifyIssuerSigned(mdoc, { algorithms: ["ES256"], expectedDocType: DOCTYPE, trustAnchorsPem: cert.pem });
103
103
  check("verify: docType", out.docType === DOCTYPE);
104
104
  check("verify: alg reported", out.alg === "ES256");
105
105
  check("verify: digestAlgorithm", out.digestAlgorithm === "SHA-256");
@@ -125,7 +125,7 @@ async function testDigestAndSignatureRefusals() {
125
125
  items[0] = new cbor.Tag(24, cbor.encode(inner0));
126
126
  var tampered = cbor.encode(top);
127
127
  var e1 = null;
128
- try { await b.mdoc.verifyIssuerSigned(tampered, { algorithms: ["ES256"] }); } catch (e) { e1 = e; }
128
+ try { await b.mdoc.verifyIssuerSigned(tampered, { algorithms: ["ES256"], trustAnchorsPem: cert.pem }); } catch (e) { e1 = e; }
129
129
  check("verify: tampered element refused (digest-mismatch)", e1 && e1.code === "mdoc/digest-mismatch");
130
130
 
131
131
  // Tamper the COSE signature deterministically (flip a byte in the
@@ -135,19 +135,19 @@ async function testDigestAndSignatureRefusals() {
135
135
  ia[3] = Buffer.from(ia[3]); ia[3][0] ^= 0xff;
136
136
  var bad = cbor.encode(top2);
137
137
  var e2 = null;
138
- try { await b.mdoc.verifyIssuerSigned(bad, { algorithms: ["ES256"] }); } catch (e) { e2 = e; }
138
+ try { await b.mdoc.verifyIssuerSigned(bad, { algorithms: ["ES256"], trustAnchorsPem: cert.pem }); } catch (e) { e2 = e; }
139
139
  check("verify: tampered COSE signature refused", e2 && e2.code === "cose/bad-signature");
140
140
 
141
141
  // A malformed x5chain certificate surfaces a clean error (not raw OpenSSL).
142
142
  var top3 = cbor.decode(mdoc, { allowedTags: [0, 1, 24, 1004] });
143
143
  top3.get("issuerAuth")[1].set(33, Buffer.from([0x30, 0x03, 0x02, 0x01, 0x01]));
144
144
  var e6 = null;
145
- try { await b.mdoc.verifyIssuerSigned(cbor.encode(top3), { algorithms: ["ES256"] }); } catch (e) { e6 = e; }
145
+ try { await b.mdoc.verifyIssuerSigned(cbor.encode(top3), { algorithms: ["ES256"], trustAnchorsPem: cert.pem }); } catch (e) { e6 = e; }
146
146
  check("verify: malformed x5chain cert refused cleanly", e6 && e6.code === "mdoc/bad-cert");
147
147
 
148
148
  // alg outside allowlist
149
149
  var e3 = null;
150
- try { await b.mdoc.verifyIssuerSigned(mdoc, { algorithms: ["EdDSA"] }); } catch (e) { e3 = e; }
150
+ try { await b.mdoc.verifyIssuerSigned(mdoc, { algorithms: ["EdDSA"], trustAnchorsPem: cert.pem }); } catch (e) { e3 = e; }
151
151
  check("verify: alg outside allowlist refused", e3 && (e3.code === "cose/alg-not-allowed"));
152
152
  }
153
153
 
@@ -157,40 +157,43 @@ async function testValidityAndDocType() {
157
157
  // expired (valid window 10 days ago .. 1 day ago)
158
158
  var expired = await _makeMdoc(cert, { validFrom: Date.now() - 86400000 * 10, validUntil: Date.now() - 86400000 });
159
159
  var e1 = null;
160
- try { await b.mdoc.verifyIssuerSigned(expired, { algorithms: ["ES256"] }); } catch (e) { e1 = e; }
160
+ try { await b.mdoc.verifyIssuerSigned(expired, { algorithms: ["ES256"], trustAnchorsPem: cert.pem }); } catch (e) { e1 = e; }
161
161
  check("verify: expired credential refused", e1 && e1.code === "mdoc/expired");
162
162
 
163
163
  // not yet valid
164
164
  var future = await _makeMdoc(cert, { validFrom: Date.now() + 86400000 * 30 });
165
165
  var e2 = null;
166
- try { await b.mdoc.verifyIssuerSigned(future, { algorithms: ["ES256"] }); } catch (e) { e2 = e; }
166
+ try { await b.mdoc.verifyIssuerSigned(future, { algorithms: ["ES256"], trustAnchorsPem: cert.pem }); } catch (e) { e2 = e; }
167
167
  check("verify: not-yet-valid credential refused", e2 && e2.code === "mdoc/not-yet-valid");
168
168
 
169
169
  // opts.at within window accepts an otherwise-expired credential
170
- var ok = await b.mdoc.verifyIssuerSigned(expired, { algorithms: ["ES256"], at: new Date(Date.now() - 86400000 * 2) });
170
+ // allowUntrustedIssuer (not trustAnchorsPem): this asserts the MSO validity
171
+ // window honors opts.at — anchoring would instead chain-validate the cert at
172
+ // the past `at`, where the (created-now) cert isn't yet valid.
173
+ var ok = await b.mdoc.verifyIssuerSigned(expired, { algorithms: ["ES256"], at: new Date(Date.now() - 86400000 * 2), allowUntrustedIssuer: true });
171
174
  check("verify: opts.at within window accepts", ok.docType === DOCTYPE);
172
175
 
173
176
  // docType mismatch
174
177
  var e3 = null;
175
- try { await b.mdoc.verifyIssuerSigned(await _makeMdoc(cert), { algorithms: ["ES256"], expectedDocType: "org.iso.18013.5.1.photoID" }); } catch (e) { e3 = e; }
178
+ try { await b.mdoc.verifyIssuerSigned(await _makeMdoc(cert), { algorithms: ["ES256"], expectedDocType: "org.iso.18013.5.1.photoID", trustAnchorsPem: cert.pem }); } catch (e) { e3 = e; }
176
179
  check("verify: docType mismatch refused", e3 && e3.code === "mdoc/doctype-mismatch");
177
180
 
178
181
  // malformed validUntil (a non-date) fails closed
179
182
  var badValidity = await _makeMdoc(cert, { validUntilRaw: new cbor.Tag(0, "not-a-date") });
180
183
  var e4 = null;
181
- try { await b.mdoc.verifyIssuerSigned(badValidity, { algorithms: ["ES256"] }); } catch (e) { e4 = e; }
184
+ try { await b.mdoc.verifyIssuerSigned(badValidity, { algorithms: ["ES256"], trustAnchorsPem: cert.pem }); } catch (e) { e4 = e; }
182
185
  check("verify: malformed validUntil refused (fail closed)", e4 && e4.code === "mdoc/bad-validity");
183
186
 
184
187
  // invalid opts.at refused (lesson carried from b.tsa / b.vc)
185
188
  var e5 = null;
186
- try { await b.mdoc.verifyIssuerSigned(await _makeMdoc(cert), { algorithms: ["ES256"], at: new Date("nope") }); } catch (e) { e5 = e; }
189
+ try { await b.mdoc.verifyIssuerSigned(await _makeMdoc(cert), { algorithms: ["ES256"], at: new Date("nope"), trustAnchorsPem: cert.pem }); } catch (e) { e5 = e; }
187
190
  check("verify: invalid opts.at refused", e5 && e5.code === "mdoc/bad-at");
188
191
 
189
192
  // Two signed IssuerSignedItems with the same elementIdentifier (each
190
193
  // with a valid MSO digest) is ambiguous → fail closed, not last-wins.
191
194
  var dup = await _makeMdoc(cert, { elements: [["family_name", "Doe"], ["family_name", "Roe"]] });
192
195
  var e6 = null;
193
- try { await b.mdoc.verifyIssuerSigned(dup, { algorithms: ["ES256"] }); } catch (e) { e6 = e; }
196
+ try { await b.mdoc.verifyIssuerSigned(dup, { algorithms: ["ES256"], trustAnchorsPem: cert.pem }); } catch (e) { e6 = e; }
194
197
  check("verify: duplicate elementIdentifier refused", e6 && e6.code === "mdoc/duplicate-element");
195
198
  }
196
199
 
@@ -211,7 +214,7 @@ async function testChainAndInputGuards() {
211
214
 
212
215
  // garbage input → not CBOR / malformed
213
216
  var e3 = null;
214
- try { await b.mdoc.verifyIssuerSigned(Buffer.from([0x00, 0x01]), { algorithms: ["ES256"] }); } catch (e) { e3 = e; }
217
+ try { await b.mdoc.verifyIssuerSigned(Buffer.from([0x00, 0x01]), { algorithms: ["ES256"], trustAnchorsPem: cert.pem }); } catch (e) { e3 = e; }
215
218
  check("verify: garbage input refused", e3 && (e3.code === "mdoc/malformed" || e3.code === "mdoc/bad-input" || /cbor/.test(e3.code || "")));
216
219
 
217
220
  // missing algorithms
@@ -226,7 +229,7 @@ async function testDeviceAuth() {
226
229
  var deviceJwk = device.publicKey.export({ format: "jwk" });
227
230
  // issuer-signed mdoc carrying the device key in the MSO
228
231
  var mdoc = await _makeMdoc(cert, { deviceJwk: deviceJwk });
229
- var issuer = await b.mdoc.verifyIssuerSigned(mdoc, { algorithms: ["ES256"] });
232
+ var issuer = await b.mdoc.verifyIssuerSigned(mdoc, { algorithms: ["ES256"], trustAnchorsPem: cert.pem });
230
233
  check("verifyIssuerSigned: returns the deviceKey", issuer.deviceKey instanceof Map || (issuer.deviceKey && typeof issuer.deviceKey === "object"));
231
234
 
232
235
  // build a DeviceSigned: detached COSE_Sign1 over DeviceAuthentication
@@ -263,12 +266,36 @@ async function testDeviceAuth() {
263
266
  check("verifyDeviceAuth: missing sessionTranscript refused", e4 && e4.code === "mdoc/no-session-transcript");
264
267
  }
265
268
 
269
+ async function testTrustAnchorFailClosedByDefault() {
270
+ // The signer cert rides in the attacker-supplied x5chain, so verifying the
271
+ // COSE_Sign1 against that embedded key only proves the MSO is self-consistent.
272
+ // A forged mDL (attacker keypair + self-signed leaf + self-signed MSO) must be
273
+ // REFUSED by default — issuer trust is not opt-in.
274
+ var forged = _makeCert(); // an arbitrary self-signed issuer
275
+ var mdoc = await _makeMdoc(forged);
276
+
277
+ var e1 = null;
278
+ try { await b.mdoc.verifyIssuerSigned(mdoc, { algorithms: ["ES256"] }); } catch (e) { e1 = e; }
279
+ check("verify: refuses a self-signed mDL with no trust anchor (no fail-open)",
280
+ e1 && e1.code === "mdoc/trust-anchors-required");
281
+
282
+ // The explicit, audited opt-out accepts it but marks it unauthenticated.
283
+ var out = await b.mdoc.verifyIssuerSigned(mdoc, { algorithms: ["ES256"], allowUntrustedIssuer: true });
284
+ check("verify: allowUntrustedIssuer opt-out accepts but flags issuerTrusted:false",
285
+ out.docType === DOCTYPE && out.issuerTrusted === false);
286
+
287
+ // Anchored to its own cert → trusted.
288
+ var anchored = await b.mdoc.verifyIssuerSigned(mdoc, { algorithms: ["ES256"], trustAnchorsPem: forged.pem });
289
+ check("verify: anchored verification reports issuerTrusted:true", anchored.issuerTrusted === true);
290
+ }
291
+
266
292
  async function run() {
267
293
  testSurface();
268
294
  await testRoundTrip();
269
295
  await testDigestAndSignatureRefusals();
270
296
  await testValidityAndDocType();
271
297
  await testChainAndInputGuards();
298
+ await testTrustAnchorFailClosedByDefault();
272
299
  await testDeviceAuth();
273
300
  }
274
301
 
@@ -94,6 +94,67 @@ async function run() {
94
94
  req5.socket = { remoteAddress: "192.168.1.1" };
95
95
  var r5 = await _runMw(fence403, req5);
96
96
  check("custom denyStatus = 403", r5.res._captured().status === 403);
97
+
98
+ // ---- X-Forwarded-For spoofing (CWE-290/348) ----
99
+ // A bare trustProxy honored the leftmost XFF hop, which a direct caller
100
+ // fully controls — forging an allowed IP walked through the gate. The gate
101
+ // now refuses that spoofable config at construction and peer-gates XFF.
102
+
103
+ // Construction fails closed: trustProxy without trustedProxies/resolver.
104
+ var threwBareTrust = null;
105
+ try { b.middleware.networkAllowlist({ paths: ["/admin"], allowedCidrs: ["203.0.113.0/24"], trustProxy: true }); }
106
+ catch (e) { threwBareTrust = e; }
107
+ check("create: bare trustProxy refused (spoofable gate)", threwBareTrust !== null);
108
+ var threwBareNum = null;
109
+ try { b.middleware.networkAllowlist({ paths: ["/admin"], allowedCidrs: ["203.0.113.0/24"], trustProxy: 1 }); }
110
+ catch (e) { threwBareNum = e; }
111
+ check("create: bare trustProxy:N refused too", threwBareNum !== null);
112
+ var threwBadResolver = null;
113
+ try { b.middleware.networkAllowlist({ paths: ["/admin"], allowedCidrs: ["203.0.113.0/24"], clientIpResolver: 123 }); }
114
+ catch (e) { threwBadResolver = e; }
115
+ check("create: non-function clientIpResolver refused", threwBadResolver !== null);
116
+
117
+ function _xffReq(sock, xff) {
118
+ var rq = _mockReq({ url: "/admin/x", method: "GET" });
119
+ rq.pathname = "/admin/x";
120
+ rq.socket = { remoteAddress: sock };
121
+ rq.headers["x-forwarded-for"] = xff;
122
+ return rq;
123
+ }
124
+
125
+ // Peer-gated via trustedProxies.
126
+ var pgFence = b.middleware.networkAllowlist({
127
+ paths: ["/admin"],
128
+ allowedCidrs: ["203.0.113.0/24"],
129
+ trustedProxies: ["10.0.0.0/8"],
130
+ });
131
+ // Direct attacker (untrusted peer) forging an allowed IP → DENIED.
132
+ var rSpoof = await _runMw(pgFence, _xffReq("198.51.100.66", "203.0.113.7"));
133
+ check("spoofed XFF from untrusted peer → blocked", rSpoof.next === false);
134
+ // Trusted proxy forwarding a real allowed client → allowed.
135
+ var rTrusted = await _runMw(pgFence, _xffReq("10.0.0.9", "203.0.113.7"));
136
+ check("trusted proxy + allowed client → next()", rTrusted.next === true);
137
+ // Trusted proxy forwarding a non-allowed client → blocked (real IP honored).
138
+ var rTrustedBad = await _runMw(pgFence, _xffReq("10.0.0.9", "198.51.100.66"));
139
+ check("trusted proxy + non-allowed client → blocked", rTrustedBad.next === false);
140
+
141
+ // clientIpResolver: operator owns resolution entirely.
142
+ var resFence = b.middleware.networkAllowlist({
143
+ paths: ["/admin"],
144
+ allowedCidrs: ["203.0.113.0/24"],
145
+ clientIpResolver: function (rq) { return rq.headers["true-client-ip"]; },
146
+ });
147
+ var resReq = _mockReq({ url: "/admin/x", method: "GET" });
148
+ resReq.pathname = "/admin/x";
149
+ resReq.socket = { remoteAddress: "1.2.3.4" };
150
+ resReq.headers["true-client-ip"] = "203.0.113.50";
151
+ var rRes = await _runMw(resFence, resReq);
152
+ check("clientIpResolver resolves allowed client → next()", rRes.next === true);
153
+
154
+ // Default (no trust config): socket address governs, forged XFF ignored.
155
+ var defFence = b.middleware.networkAllowlist({ paths: ["/admin"], allowedCidrs: ["203.0.113.0/24"] });
156
+ var rDefSpoof = await _runMw(defFence, _xffReq("198.51.100.66", "203.0.113.7"));
157
+ check("default: forged XFF ignored, attacker socket blocked", rDefSpoof.next === false);
97
158
  }
98
159
 
99
160
  module.exports = { run: run };
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ /**
3
+ * b.objectStore conditional-GET Range header (shared across sigv4 / gcs / azure).
4
+ * The documented + shipped contract is `range: [start, end]` (array) — see
5
+ * b.archive.adapters.objectStore and b.backup. Reading .start/.end off an array
6
+ * yielded "bytes=undefined-undefined", which every store IGNORES, silently
7
+ * returning the FULL object instead of the requested byte range.
8
+ */
9
+
10
+ var helpers = require("../helpers");
11
+ var check = helpers.check;
12
+
13
+ var httpReq = require("../../lib/object-store/http-request");
14
+ var apply = httpReq.applyConditionalGetHeaders;
15
+
16
+ function run() {
17
+ // Array contract [start, end] → correct bytes= header (the real caller shape).
18
+ var t1 = apply({}, { range: [0, 1023] }, "Range");
19
+ check("array range [0,1023] → bytes=0-1023", t1.Range === "bytes=0-1023");
20
+
21
+ // Honors the backend-specific range header name (Azure x-ms-range).
22
+ var t2 = apply({}, { range: [100, 199] }, "x-ms-range");
23
+ check("array range honors azure header name", t2["x-ms-range"] === "bytes=100-199");
24
+
25
+ // { start, end } object form still accepted (compatibility).
26
+ var t3 = apply({}, { range: { start: 5, end: 9 } }, "Range");
27
+ check("object {start,end} compat → bytes=5-9", t3.Range === "bytes=5-9");
28
+
29
+ // No range → no Range header at all.
30
+ var t4 = apply({}, {}, "Range");
31
+ check("no range → no Range header emitted", !("Range" in t4));
32
+
33
+ // Malformed range must throw, not emit a garbage header that over-fetches.
34
+ var threw = null;
35
+ try { apply({}, { range: [undefined, 10] }, "Range"); } catch (e) { threw = e; }
36
+ check("malformed range ([undefined,10]) throws, not bytes=undefined",
37
+ threw && /range must be|INVALID_RANGE/.test((threw.message || "") + " " + (threw.code || "")));
38
+ var threwInv = null;
39
+ try { apply({}, { range: [10, 5] }, "Range"); } catch (e) { threwInv = e; }
40
+ check("inverted range (end < start) throws", threwInv !== null);
41
+ var threwNeg = null;
42
+ try { apply({}, { range: [-1, 5] }, "Range"); } catch (e) { threwNeg = e; }
43
+ check("negative start throws", threwNeg !== null);
44
+
45
+ console.log("[object-store-range-header] OK — " + helpers.getChecks() + " checks passed");
46
+ }
47
+
48
+ module.exports = { run: run };
49
+ if (require.main === module) {
50
+ try { run(); } catch (e) { console.error("FAIL: " + helpers.formatErr(e)); process.exit(1); }
51
+ }
@@ -78,6 +78,24 @@ async function run() {
78
78
  check("redactAttrs scrubs sensitive keys", ra.authorization === "[REDACTED]" && ra.password === "[REDACTED]");
79
79
  check("redactAttrs keeps non-sensitive keys", ra["http.method"] === CONTROL_VAL);
80
80
 
81
+ // A sensitive KEY whose VALUE is an array or object must STILL be scrubbed —
82
+ // the field-name rule has to fire before descending into the composite, or an
83
+ // opaque token reaches the collector verbatim (CWE-532). OTLP serializes
84
+ // arrays/objects natively, so these are real wire shapes.
85
+ var raComposite = b.observability.redactAttrs({
86
+ authorization: ["Bearer " + SECRET_TOKEN], password: { nested: SECRET_PW },
87
+ session: [SECRET_API], "http.methods": ["GET", "POST"],
88
+ });
89
+ check("redactAttrs scrubs a sensitive key with an ARRAY value", raComposite.authorization === "[REDACTED]");
90
+ check("redactAttrs scrubs a sensitive key with an OBJECT value", raComposite.password === "[REDACTED]");
91
+ check("redactAttrs scrubs a sensitive key with a single-elem array", raComposite.session === "[REDACTED]");
92
+ check("redactAttrs preserves a NON-sensitive array value",
93
+ Array.isArray(raComposite["http.methods"]) && raComposite["http.methods"][1] === "POST");
94
+ // Value-shape detectors still run inside a non-sensitive composite.
95
+ var raCc = b.observability.redactAttrs({ cards: ["4111 1111 1111 1111"] });
96
+ check("redactAttrs still detects a CC inside a non-sensitive array",
97
+ Array.isArray(raCc.cards) && raCc.cards[0] === "[REDACTED-CC]");
98
+
81
99
  var jsonWire = await captureBody("json");
82
100
  check("json: bearer token redacted on the OTLP wire", jsonWire.indexOf(SECRET_TOKEN) === -1);
83
101
  check("json: password redacted on the OTLP wire", jsonWire.indexOf(SECRET_PW) === -1);
@@ -91,6 +109,23 @@ async function run() {
91
109
  check("protobuf: api key redacted on the OTLP wire", protoWire.indexOf(SECRET_API) === -1);
92
110
  check("protobuf: non-sensitive control attribute survives", protoWire.indexOf(CONTROL_VAL) !== -1);
93
111
 
112
+ // span status.message + name are EGRESS too — the tracer's canonical
113
+ // setStatus("error", e.message) routinely carries a connection string. Both
114
+ // the message AND the name now route through the telemetry redactor, so a
115
+ // value-shape secret (connection string) in either is scrubbed on the wire.
116
+ var CONN = "connect failed: postgres://app:" + SECRET_PW + "@db:5432/prod";
117
+ var spanJson = JSON.stringify(otlp._spanToOtlp({
118
+ traceId: "t", spanId: "s", name: "span " + CONN,
119
+ status: { code: 2, message: CONN }, attributes: {},
120
+ }));
121
+ check("json: span status.message connection-string redacted",
122
+ spanJson.indexOf(SECRET_PW) === -1 && spanJson.indexOf("[REDACTED-CONN-STRING]") !== -1);
123
+ var spanProto = Buffer.from(otlp._spanToProto({
124
+ traceId: "t", spanId: "s", name: "span " + CONN,
125
+ status: { code: 2, message: CONN }, attributes: {},
126
+ })).toString("latin1");
127
+ check("protobuf: span status.message + name connection-string redacted", spanProto.indexOf(SECRET_PW) === -1);
128
+
94
129
  // ---- OTLP LOG sinks are EGRESS too: record-meta + resource attrs must redact ----
95
130
  // The span/metric exporters redact; the log sinks (HTTP-JSON + gRPC) shipped
96
131
  // the same attribute maps to the collector UNREDACTED — a log line carrying a