@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
@@ -48,6 +48,7 @@
48
48
  */
49
49
 
50
50
  var nodeCrypto = require("node:crypto");
51
+ var numericBounds = require("./numeric-bounds");
51
52
  var bCrypto = require("./crypto");
52
53
  var httpClient = require("./http-client");
53
54
  var safeBuffer = require("./safe-buffer");
@@ -57,6 +58,8 @@ var C = require("./constants");
57
58
  var lazyRequire = require("./lazy-require");
58
59
  var numericChecks = require("./numeric-checks");
59
60
  var requestHelpers = require("./request-helpers");
61
+ // Lazy — ssrf-guard pulls in the DNS/network stack; only touched on send().
62
+ var ssrfGuard = lazyRequire(function () { return require("./ssrf-guard"); });
60
63
  var validateOpts = require("./validate-opts");
61
64
  var { WebhookError } = require("./framework-error");
62
65
  // b.webhook.dispatcher — durable signed-webhook delivery store. Lives in its
@@ -428,6 +431,18 @@ function signer(opts) {
428
431
  allowedProtocols: httpOpts.allowedProtocols || safeUrl.ALLOW_HTTP_TLS,
429
432
  errorClass: WebhookError,
430
433
  });
434
+ // SSRF gate at the webhook layer: refuse private / loopback / link-local /
435
+ // cloud-metadata destinations up front, before the retry loop. httpClient
436
+ // also enforces this on every request, but checking here fails fast (no
437
+ // wasted retries) and keeps the SSRF boundary explicit at the send() API
438
+ // rather than relying on the transport's internal gate. allowInternal
439
+ // mirrors the operator's http opts (its affirmative waiver).
440
+ try {
441
+ await ssrfGuard().checkUrl(url, { allowInternal: httpOpts.allowInternal === true });
442
+ } catch (e) {
443
+ if (e && e.isSsrfError) throw _err("SSRF_REFUSED", "webhook.signer.send: " + e.message);
444
+ throw e;
445
+ }
431
446
  _validateBody(body);
432
447
  var signed = sign(body, { kid: input.kid });
433
448
  var mergedHeaders = Object.assign({}, input.headers || {}, signed.headers);
@@ -645,7 +660,7 @@ function verifier(opts) {
645
660
  throw _failure("MISSING_TIMESTAMP", "webhook: t= field missing from signature header", "missing-timestamp", ctxReq);
646
661
  }
647
662
  var ts = Number(parsed.t);
648
- if (!isFinite(ts) || ts < 0 || Math.floor(ts) !== ts) {
663
+ if (!numericBounds.isNonNegativeFiniteInt(ts)) {
649
664
  throw _failure("BAD_TIMESTAMP", "webhook: t= field is not a non-negative integer, got " + JSON.stringify(parsed.t), "bad-timestamp", ctxReq);
650
665
  }
651
666
  if (parsed.id === null || parsed.id.length === 0) {
@@ -573,7 +573,7 @@ function _negotiatePermessageDeflate(reqHeader) {
573
573
  "server_max_window_bits": true, "client_max_window_bits": true,
574
574
  };
575
575
  var ok = true;
576
- for (var k in p) { if (Object.prototype.hasOwnProperty.call(p, k) && !KNOWN[k]) { ok = false; break; } }
576
+ for (var k in p) { if (Object.prototype.hasOwnProperty.call(p, k) && !Object.prototype.hasOwnProperty.call(KNOWN, k)) { ok = false; break; } }
577
577
  if (!ok) continue;
578
578
  // Always negotiate WITH no_context_takeover in BOTH directions, so
579
579
  // every message uses a fresh zlib state. Echo any client window-
@@ -102,7 +102,7 @@ function create(opts) {
102
102
  opts = opts || {};
103
103
  validateOpts(opts, ["store", "mode", "defaultRetentionMs", "clock"], "worm.create");
104
104
  var mode = opts.mode || "compliance";
105
- if (!MODES[mode]) throw new WormError("worm/bad-mode", "worm.create: mode must be 'compliance' or 'governance'");
105
+ if (!Object.prototype.hasOwnProperty.call(MODES, mode)) throw new WormError("worm/bad-mode", "worm.create: mode must be 'compliance' or 'governance'");
106
106
  var store = opts.store || _memStore();
107
107
  ["get", "set", "delete", "has", "keys"].forEach(function (m) {
108
108
  if (typeof store[m] !== "function") throw new WormError("worm/bad-store", "worm.create: store adapter must implement " + m + "()");
@@ -248,17 +248,13 @@ function connect(target, opts) {
248
248
  });
249
249
  // SSRF gate — refuse private / loopback / link-local / cloud-metadata /
250
250
  // reserved IP destinations by default. Symmetric to b.httpClient. The
251
- // returned `ips` are pinned through tls.connect / net.connect so the
251
+ // validated `ips` are pinned through tls.connect / net.connect so the
252
252
  // actual TCP connect targets the validated address (closes the DNS-
253
253
  // rebinding TOCTOU window). Cloud-metadata IPs are unconditional
254
- // hard-deny — `allowInternal: true` does not bypass them.
255
- var hostnameForUrl = parsed.protocol === "wss:" ? "https:" : "http:";
256
- var probeUrl = new nodeUrl.URL(hostnameForUrl + "//" + parsed.host + parsed.pathname + parsed.search);
257
- ssrfGuard.checkUrl(probeUrl, {
258
- allowInternal: opts.allowInternal,
259
- errorClass: WsClientError,
260
- }).then(function (result) {
261
- client._ssrfPinnedIps = result && result.ips;
254
+ // hard-deny — `allowInternal: true` does not bypass them. _prepareDial
255
+ // performs the (async) check + pinning before the dial and reruns it on
256
+ // every reconnect, so a urlFor-swapped target is validated too.
257
+ client._prepareDial().then(function () {
262
258
  client._dial();
263
259
  }).catch(function (e) {
264
260
  setImmediate(function () { client._handleSocketError(e); });
@@ -315,51 +311,50 @@ class WsClient extends EventEmitter {
315
311
  get subprotocol() { return this._negotiatedSubprotocol; }
316
312
  get url() { return this._opts.target; }
317
313
 
318
- _dial() {
319
- var self = this;
314
+ // Resolve the dial target and re-validate it BEFORE any socket opens.
315
+ // urlFor may swap the URL and tlsOptsFor may rotate TLS material every
316
+ // dial (including reconnects). The resolved target is re-checked through
317
+ // ssrfGuard — AWAITED, because checkUrl is async: the prior code called it
318
+ // synchronously inside _dial and discarded the Promise, so a urlFor that
319
+ // pointed at a private / cloud-metadata address mid-reconnect was connected
320
+ // anyway (the SSRF rejection surfaced only as an unhandled rejection) and
321
+ // the connect was never pinned. Returns a Promise; sync throws from
322
+ // urlFor / tlsOptsFor become rejections (async fn) handled by the caller.
323
+ async _prepareDial() {
320
324
  var opts = this._opts;
321
- this._readyState = "connecting";
322
-
323
- // Per-dial overrides — urlFor swaps the target URL, tlsOptsFor
324
- // overrides TLS material. Both fire every dial including reconnects
325
- // so callers can rotate state. urlFor's result is re-validated
326
- // through ssrfGuard so a hostile upstream can't direct the client
327
- // at a private address mid-reconnect.
328
325
  var attempt = this._reconnectAttempt || 0;
329
- var dialTarget = opts.target;
330
326
  var dialParsed = opts.parsedUrl;
331
327
  if (typeof opts.urlFor === "function") {
332
- try {
333
- var nextTarget = opts.urlFor(attempt);
334
- if (typeof nextTarget === "string" && nextTarget.length > 0 && nextTarget !== dialTarget) {
335
- dialParsed = _parseUrl(nextTarget);
336
- dialTarget = nextTarget;
337
- var probeProto = dialParsed.protocol === "wss:" ? "https:" : "http:";
338
- var probeUrl = new nodeUrl.URL(probeProto + "//" + dialParsed.host + dialParsed.pathname + dialParsed.search);
339
- var probe = ssrfGuard.checkUrl(probeUrl, {
340
- allowInternal: opts.allowInternal,
341
- errorClass: WsClientError,
342
- });
343
- this._ssrfPinnedIps = probe && probe.ips ? probe.ips : null;
344
- }
345
- } catch (e) {
346
- return self._handleSocketError(e);
328
+ var nextTarget = opts.urlFor(attempt);
329
+ if (typeof nextTarget === "string" && nextTarget.length > 0 && nextTarget !== opts.target) {
330
+ dialParsed = _parseUrl(nextTarget);
347
331
  }
348
332
  }
349
-
350
333
  var dialTlsOpts = opts.tlsOpts;
351
334
  if (typeof opts.tlsOptsFor === "function") {
352
- try {
353
- var override = opts.tlsOptsFor(attempt);
354
- if (override && typeof override === "object") {
355
- dialTlsOpts = Object.assign({}, opts.tlsOpts || {}, override);
356
- }
357
- } catch (e) {
358
- return self._handleSocketError(e);
335
+ var override = opts.tlsOptsFor(attempt);
336
+ if (override && typeof override === "object") {
337
+ dialTlsOpts = Object.assign({}, opts.tlsOpts || {}, override);
359
338
  }
360
339
  }
340
+ var probeProto = dialParsed.protocol === "wss:" ? "https:" : "http:";
341
+ var probeUrl = new nodeUrl.URL(probeProto + "//" + dialParsed.host + dialParsed.pathname + dialParsed.search);
342
+ var probe = await ssrfGuard.checkUrl(probeUrl, {
343
+ allowInternal: opts.allowInternal,
344
+ errorClass: WsClientError,
345
+ });
346
+ this._ssrfPinnedIps = probe && probe.ips ? probe.ips : null;
347
+ this._dialParsed = dialParsed;
348
+ this._dialTlsOpts = dialTlsOpts;
349
+ }
350
+
351
+ _dial() {
352
+ var self = this;
353
+ this._readyState = "connecting";
361
354
 
362
- var parsed = dialParsed;
355
+ // Target + TLS material were resolved and SSRF-validated by _prepareDial.
356
+ var parsed = this._dialParsed || this._opts.parsedUrl;
357
+ var dialTlsOpts = this._dialTlsOpts || this._opts.tlsOpts;
363
358
  var port = parsed.port ? parseInt(parsed.port, 10) :
364
359
  (parsed.protocol === "wss:" ? 443 : 80); // TLS / HTTP default port
365
360
  var host = parsed.hostname;
@@ -422,8 +417,8 @@ class WsClient extends EventEmitter {
422
417
 
423
418
  this._handshakeTimer = setTimeout(function () {
424
419
  self._handleSocketError(new WsClientError("ws-client/handshake-timeout",
425
- "Handshake exceeded " + opts.handshakeTimeoutMs + "ms"));
426
- }, opts.handshakeTimeoutMs);
420
+ "Handshake exceeded " + self._opts.handshakeTimeoutMs + "ms"));
421
+ }, self._opts.handshakeTimeoutMs);
427
422
  if (typeof this._handshakeTimer.unref === "function") this._handshakeTimer.unref();
428
423
  }
429
424
 
@@ -644,6 +639,17 @@ class WsClient extends EventEmitter {
644
639
  return;
645
640
  }
646
641
  }
642
+ // Fail the connection on any opcode outside the six RFC 6455 §5.2-defined
643
+ // values (CONT/TEXT/BINARY/CLOSE/PING/PONG). Without this, a reserved
644
+ // opcode (0x3-0x7, 0xB-0xF) from a malicious server fell through every
645
+ // branch to the FIN block and emitted a (stale/empty) message — a
646
+ // fragmented-message desync / frame-injection lever.
647
+ if (frame.opcode !== OPCODE_CONT && frame.opcode !== OPCODE_TEXT &&
648
+ frame.opcode !== OPCODE_BINARY && !isControl) {
649
+ this._handleSocketError(new WsClientError("ws-client/reserved-opcode",
650
+ "reserved/unknown WebSocket opcode 0x" + frame.opcode.toString(16) + " (RFC 6455 §5.2)"));
651
+ return;
652
+ }
647
653
  // Continuation frames MUST NOT carry rsv1 (RFC 7692 §6.1) — only
648
654
  // the first frame of a compressed message sets rsv1.
649
655
  if (frame.opcode === OPCODE_CONT && frame.rsv1) {
@@ -926,7 +932,12 @@ class WsClient extends EventEmitter {
926
932
  this._reconnectTimer = setTimeout(function () {
927
933
  self._reconnectTimer = null;
928
934
  if (self._closed) return; // operator-cancelled in flight
929
- self._dial();
935
+ // Re-resolve + re-validate the target (urlFor swap, DNS rebind) before
936
+ // reconnecting — awaited, so a now-private/metadata address is refused.
937
+ self._prepareDial().then(function () {
938
+ if (self._closed) return;
939
+ self._dial();
940
+ }).catch(function (e) { self._handleSocketError(e); });
930
941
  }, delay);
931
942
  if (typeof this._reconnectTimer.unref === "function") this._reconnectTimer.unref();
932
943
  this.emit("reconnecting", { attempt: this._reconnectAttempt, delayMs: delay });
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ // Internal X.509 path-validation helpers shared by the framework's
4
+ // certificate-chain walkers (b.tsa.verifyToken, b.mail.bimi VMC/CMC,
5
+ // b.mail.crypto.smime.verify). They exist because node:crypto's
6
+ // X509Certificate.checkIssued() validates the issuer/subject DN match,
7
+ // the AKI/SKI linkage, and — when a keyUsage extension is present —
8
+ // keyCertSign, but it does NOT enforce basicConstraints cA:TRUE. A
9
+ // leaf / end-entity certificate (cA:FALSE) that omits keyUsage is
10
+ // therefore wrongly accepted as a signing CA for the next cert in the
11
+ // chain — the classic basicConstraints bypass (CVE-2002-0862 class).
12
+ // Every chain walker routes its issuer test through these helpers so the
13
+ // cA enforcement can never be forgotten in one walker but present in
14
+ // another.
15
+
16
+ // True only when `cert` asserts basicConstraints cA:TRUE. node's
17
+ // X509Certificate exposes `.ca` (a boolean); a cert with no
18
+ // basicConstraints extension or with cA:FALSE returns false. A missing
19
+ // cert or a non-boolean `.ca` (parse failure / unsupported runtime)
20
+ // fails closed.
21
+ function isCaCert(cert) {
22
+ return !!cert && cert.ca === true;
23
+ }
24
+
25
+ // True when `issuer` validly issued `subject` AND is itself a CA: the
26
+ // DN / AKI-SKI / keyUsage linkage (checkIssued), the cryptographic
27
+ // signature (verify), and basicConstraints cA:TRUE (isCaCert). The cA
28
+ // check runs first so a non-CA cert is rejected before the expensive
29
+ // signature verification. Any exception (malformed cert, unsupported
30
+ // key type) fails closed to false.
31
+ function issuerValidlyIssued(issuer, subject) {
32
+ try {
33
+ return isCaCert(issuer) &&
34
+ subject.checkIssued(issuer) &&
35
+ subject.verify(issuer.publicKey);
36
+ } catch (_e) {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ module.exports = {
42
+ isCaCert: isCaCert,
43
+ issuerValidlyIssued: issuerValidlyIssued,
44
+ };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.15.13",
3
+ "version": "0.15.14",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
@@ -0,0 +1,122 @@
1
+ {
2
+ "$schema": "../scripts/release-notes-schema.json",
3
+ "version": "0.15.14",
4
+ "date": "2026-06-20",
5
+ "headline": "A broad security-hardening release: every request-IP trust decision is peer-gated against the immediate connection (closing an X-Forwarded-For spoofing class — one behavior change, see Migration), a wide set of authentication and signature verifiers now fail closed, pre-auth parsers are hardened against algorithmic-complexity and frame-desync attacks, and untrusted-key allowlist lookups, at-rest encryption binding, and on-disk writes are tightened framework-wide; adds streaming and exclusive atomic-write primitives, a script-context JSON serializer, and HTTP-client bandwidth throttling",
6
+ "summary": "This release tightens existing protections across the framework rather than adding opt-ins. The headline behavior change is how the client IP is trusted: the IP gates that make security decisions (network allowlist, rate limiter, break-glass grant pin) no longer trust a bare leftmost X-Forwarded-For value when trustProxy is set without an explicit proxy allowlist — they resolve the client IP by a peer-gated right-to-left walk anchored at the connecting socket, so a direct caller can no longer forge an allowed address. Operators who terminate behind a proxy now declare its address range in trustedProxies (see Migration). Alongside that, a large set of verifiers that could accept a malformed or unsigned credential now fail closed (SAML audience restriction, FDA electronic-signature, SD-JWT key-binding replay, DPoP, CIBA, mdoc trust anchor, COSE algorithm/curve binding, agent-snapshot plaintext under a regulated posture); pre-authentication parsers that an attacker reaches before any credential check are hardened against quadratic-time and frame-desync denial of service (CBOR, JSONPath, tar/PAX, gzip, YAML, ASN.1 DER, the scheduler's timer math); allowlist and posture lookups keyed by untrusted input can no longer reach Object.prototype; the framework's own file writes are now symlink-refusing and atomic; at-rest and egress paths bind ciphertext to its location and redact nested secrets; and several lost-update races are serialized. New surface is additive: streaming and exclusive atomic-write primitives, a serializer safe to embed in an inline <script>, and download/upload bandwidth throttling on the HTTP client.",
7
+ "sections": [
8
+ {
9
+ "heading": "Migration",
10
+ "items": [
11
+ {
12
+ "title": "X-Forwarded-For trust now requires an explicit proxy allowlist",
13
+ "body": "The IP gates that enforce a security decision — network allowlist, rate limiter, and the break-glass grant IP pin — previously resolved the client IP from the leftmost X-Forwarded-For value whenever trustProxy was truthy. A direct caller could set that header to any address and walk an IP allowlist or evade a rate-limit key (CWE-290 / CWE-348). These gates now fail closed: with trustProxy set but no trustedProxies, they ignore X-Forwarded-For and use the connecting socket's peer address. To keep trusting a forwarded client IP, set trustedProxies to the CIDR(s) of your front proxy/load balancer — the client IP is then resolved by a peer-gated right-to-left walk that stops at the first hop not in the allowlist. For an allowlist / rate-limit / break-glass gate the new default is stricter (it trusts less), so an operator who has not migrated sees tighter enforcement, never looser. Display-only consumers (error pages, request logs) keep the lenient resolution and carry a docstring warning. The wiki example's WIKI_TRUST_PROXY flag is replaced by WIKI_ADMIN_TRUSTED_PROXIES (a CIDR list)."
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "heading": "Added",
19
+ "items": [
20
+ {
21
+ "title": "b.atomicFile.writeStream(filepath, source, opts?) and b.atomicFile.writeExclSync(filepath, data, opts?)",
22
+ "body": "Two atomic-write primitives that complete the read-side fdSafeReadSync family. writeStream writes a stream to a fresh O_EXCL|O_NOFOLLOW temporary file with an output-byte cap, then renames into place; writeExclSync stages a synchronous write through a verify-then-rename (clearing any stale temp under O_EXCL|O_NOFOLLOW first) for the small sealed-state round-trips (vault seal / unseal / rotate). Both refuse to follow a symlink at the destination or temp path, so an attacker who pre-creates a link can't redirect the write. The framework's own predictable-temp and symlink-following write sites now route through them; fdSafeReadSync also gains a withStat option that returns the bound fd's stat alongside the bytes."
23
+ },
24
+ {
25
+ "title": "b.safeJson.stringifyForScript(value, opts?)",
26
+ "body": "A JSON serializer safe to embed directly in an inline <script> block: it escapes <, >, and & and the U+2028 / U+2029 line separators so a value containing </script> or a JS line terminator cannot break out of the script context (DOM-XSS). The import-map helper gains scriptTag(importMap, { nonce }) built on it, and the speculation-rules inline emitter uses it."
27
+ },
28
+ {
29
+ "title": "HTTP-client bandwidth throttling",
30
+ "body": "b.httpClient requests accept a bandwidth limit that interposes a throttling transform on both the download and upload paths, so a large transfer can be rate-shaped without buffering the whole body. Useful for bounding the egress a single outbound call can consume."
31
+ },
32
+ {
33
+ "title": "Incremental audit-chain verification",
34
+ "body": "The audit verifiers accept a from/to range so a long chain can be verified in increments rather than head-to-tail every time, report an accurate rowsVerified count, and keep the newest review when reconciling. b.vault also exposes getDefaultStore() for callers that need the process's default seal/unseal store handle."
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "heading": "Security",
40
+ "items": [
41
+ {
42
+ "title": "Request-IP trust is peer-gated end to end",
43
+ "body": "The client-IP resolution that every IP gate depends on is extracted into one trust model (a trustedProxies-aware, peer-gated right-to-left walk) so the network allowlist, rate limiter, break-glass pin, and bot-guard audit attribution all decide identity the same way. X-Forwarded-Proto is peer-gated across the security middleware on the same model, and a WebSocket reconnect to a swapped target is re-validated against the SSRF / blocked-host guard before the connection is reused. See Migration for the one behavior change."
44
+ },
45
+ {
46
+ "title": "Authentication and signature verifiers fail closed",
47
+ "body": "A set of verifiers that could accept a malformed or unsigned credential now refuse it: SAML verifyResponse fails closed on a missing AudienceRestriction or unparseable Conditions; the FDA 21 CFR 11 electronic-signature verify refuses a record with no signature (recordHash alone is self-consistency, not authentication); the SD-JWT key-binding JWT requires an audience and nonce (replay binding) and an expiry when configured; DPoP cross-checks the proof's alg against its embedded JWK key type before importing it; CIBA verifies the ID token on poll and notification; the mdoc trust anchor is enforced (no fail-open); COSE binds the signature algorithm to the key curve; and agent-snapshot refuses allowPlaintext under a regulated compliance posture and gates sealing on vault initialization."
48
+ },
49
+ {
50
+ "title": "Pre-auth parsers hardened against complexity and frame-desync denial of service",
51
+ "body": "Parsers an attacker reaches before any credential check are bounded. CBOR rejects the quadratic-time duplicate-map-key construction (it ran O(n^2) on every COSE / CWT / EAT / mdoc verify) and rejects non-minimal integer/key encodings in canonical mode; JSONPath caps the node-list cross-product that could exhaust memory and rejects the filter expression that could overflow the parser stack; the tar reader caps a random-access entry read and resynchronizes a PAX header whose size field is non-numeric; the gzip reader caps a random-access output size; the YAML scanner replaces a backtracking pattern with a linear scan; the ASN.1 DER reader caps high-tag-number and OID sub-identifier decoding and rejects non-minimal forms; and the scheduler clamps far-future timers so a 32-bit overflow can't make a timer fire immediately or an interval tight-loop."
52
+ },
53
+ {
54
+ "title": "Allowlist and posture lookups keyed by untrusted input can't reach the prototype",
55
+ "body": "Lookups of the form map[untrustedKey] across the framework's allowlist and compliance-posture tables are now own-property checks, so a key like __proto__ or constructor resolves as absent instead of returning an inherited value and walking the gate (CWE-1321). The shared posture-accessor that several guards built by hand is centralized, and an inverse detector flags any re-introduced unguarded indexed lookup."
56
+ },
57
+ {
58
+ "title": "Framework file writes are symlink-refusing and atomic",
59
+ "body": "The predictable-temp and symlink-following write sites (cookie jar, config-drift snapshot, archive extraction, local object store, backup bundle, and the vault passphrase seal/unseal/rotate) now write through the atomic-write primitives above, so a pre-planted symlink at the destination or temp path can no longer redirect a privileged write, and a partial write can't leave a torn file. A re-introduced raw write without the exclusive/no-follow flags is flagged by a detector."
60
+ },
61
+ {
62
+ "title": "X.509 issuer chains enforce the CA basic constraint",
63
+ "body": "Node's checkIssued() does not verify that the issuer certificate is actually a CA (CVE-2002-0862 class), so a leaf signed by a non-CA certificate could be accepted as an issuer. Certificate-chain walking is centralized in one helper that requires the issuer's basicConstraints cA bit and verifies the signature, and every chain consumer (TSA, BIMI, S/MIME, mdoc, content-credentials, the FIDO MDS3 anchor match) routes through it."
64
+ },
65
+ {
66
+ "title": "Header, value, and markup injection closed",
67
+ "body": "Mail rejects CRLF in Reply-To and in custom header keys/values (SMTP header injection); the GDPR data-export CSV writer neutralizes formula-injection prefixes; the content guards decode no-semicolon numeric HTML entities when revealing a dangerous URL scheme (so &#106;avascript: is caught); the early-hints emitter rejects CRLF in pushed link values; the HTML/SVG/BIMI comment scanners honor the WHATWG abrupt-comment-close forms (--!>, <!-->) so a mutation-XSS comment can't smuggle markup; and the CSP builder no longer emits a host source alongside 'none'."
68
+ },
69
+ {
70
+ "title": "At-rest and egress data is bound to its context",
71
+ "body": "Backup restore binds each encrypted file blob to its manifest path as additional authenticated data, so a blob remapped to another entry fails the AEAD tag on restore (signed and unsigned bundles alike), and threads the signature-verification options through extraction; the data-subject-request lookup dual-reads the keyed and legacy subject hashes so a subject is still found across the hashing change (GDPR Art. 17); the log redactor collapses a secret nested as an array or object value, not only as a scalar; and the OTLP exporter redacts the span name and status message, not just attributes, before they leave the process."
72
+ },
73
+ {
74
+ "title": "Network-protocol verification hardened",
75
+ "body": "OCSP responses are bound to the requested certificate serial; the WebSocket reader rejects reserved opcodes and reserved bits; the DNS parser refuses a record that reads past the message buffer; the NTP client rejects a reply whose origin timestamp does not echo the request (and the SNTP cookie path was reworked to bind the exchange); DANE TLSA matching is constant-time; and MTA-STS policy fetch fails closed rather than degrading to no-policy on an ambiguous response."
76
+ },
77
+ {
78
+ "title": "Byte caps are measured in bytes, and middleware enforcement is per-instance",
79
+ "body": "Size limits that compared a byte budget against a string's character length (so multibyte input slipped a ~2-4x looser bound) now measure actual bytes — guard-json per-string cap, the per-tenant quota header budget, the JMAP request cap. The CSRF and fetch-metadata middleware track their gate per instance, so mounting a second, stricter instance still enforces (a shared request flag previously let the first, lenient mount disable it); the CSRF cookie is still issued once per response. The WebAssembly global is stripped from the sandbox worker so sandboxed code cannot JIT around the isolation."
80
+ },
81
+ {
82
+ "title": "Lost-update races serialized",
83
+ "body": "Concurrent failure recording in the login lockout and bot-challenge gates is serialized per key, so parallel failed attempts can no longer each read the same pre-write counter and stay under the lockout / challenge threshold. The inline webhook delivery is claimed before the POST so a concurrent retry pass can't double-send it, and a transient transport failure now backs off and retries instead of being dead-lettered."
84
+ }
85
+ ]
86
+ },
87
+ {
88
+ "heading": "Changed",
89
+ "items": [
90
+ {
91
+ "title": "DORA major-incident reporting clock is four hours",
92
+ "body": "The Digital Operational Resilience Act initial-notification deadline is corrected to four hours from classification (it had used the generic 24-hour default), incident classification weighs the affected-client-base percentage, and the deadline anchors to the prior filing where one exists."
93
+ },
94
+ {
95
+ "title": "Range and conditional-request correctness",
96
+ "body": "Compression skips a 206 Partial Content or any Content-Range response (RFC 7233 §4.1), so it can't drop Content-Length over a now-compressed byte interval; static-file conditional handling gives If-None-Match precedence over If-Modified-Since and If-Match over If-Unmodified-Since (RFC 7232); and the object store honors the [start, end] array range contract that previously fell through to a full read."
97
+ },
98
+ {
99
+ "title": "Declared request bodies are always validated",
100
+ "body": "A route that declares a body schema now validates it even when no body was parsed, so an omitted required body is rejected at the router instead of reaching the handler — mirroring the always-run params and query checks."
101
+ }
102
+ ]
103
+ },
104
+ {
105
+ "heading": "Fixed",
106
+ "items": [
107
+ {
108
+ "title": "Object store getStream works on remote backends",
109
+ "body": "getStream() returned a stream wrapping an unresolved promise on every remote backend, so it never delivered bytes; it now resolves the object before streaming."
110
+ },
111
+ {
112
+ "title": "Queue and body-parser correctness",
113
+ "body": "The local queue's fail()/complete() paths guard on the current row status so a stale lease can't resurrect or double-process a job, and the body parser's raw() mode no longer matches every content type by default (it had answered 415 to nothing or everything depending on the wildcard)."
114
+ },
115
+ {
116
+ "title": "Sanctions screening and GraphQL SDL probing no longer under-match",
117
+ "body": "Sanctions screening no longer skips a candidate whose type doesn't match the query's type filter (a sanctioned entity of an unspecified type was missed) and reports a typeMatch flag; the GraphQL federation guard probes for the _service / _entities introspection fields by word boundary (an aliased field bypassed the prefix check) and over batched-array request bodies. The per-tenant query budget is a true sliding window, so a burst straddling the window boundary can't briefly double the rate."
118
+ }
119
+ ]
120
+ }
121
+ ]
122
+ }
@@ -51,7 +51,27 @@ var TIMEOUT_MS = 10000;
51
51
  // { type: "npm-meta", components: [...] } — meta-bundle: each component must be current
52
52
  // { type: "github-raw", note: "..." } — GitHub raw download (no version semantics)
53
53
  // { type: "skip", reason: "..." } — skip with documented reason
54
+ // { type: "http-content", url, localFile, commitRe?, versionRe? }
55
+ // — data file tracked off a
56
+ // bare upstream URL (no
57
+ // version semantics); compare
58
+ // an embedded COMMIT/VERSION
54
59
  var SPECIAL_MAP = {
60
+ "publicsuffix-list": {
61
+ type: "http-content",
62
+ url: "https://publicsuffix.org/list/public_suffix_list.dat",
63
+ localFile: "public-suffix-list.dat",
64
+ // The PSL embeds an immutable git COMMIT sha and a VERSION timestamp
65
+ // in its header; either changes iff the list changed. Comparing them
66
+ // is robust to our appended canary block — a plain content hash is
67
+ // not (the canary makes our copy hash differ from upstream forever).
68
+ commitRe: /^\/\/ COMMIT:\s*([0-9a-f]{7,40})/m,
69
+ versionRe: /^\/\/ VERSION:\s*(\S+)/m
70
+ },
71
+ "bimi-trust-anchors": {
72
+ type: "skip",
73
+ reason: "operator-managed VMC/CMC trust anchors — empty source-tree default by design; operators populate + refresh per the file-header procedure, so there is no single upstream version to track"
74
+ },
55
75
  "SecLists-common-passwords-top-10000": {
56
76
  type: "github-master",
57
77
  owner: "danielmiessler",
@@ -138,6 +158,66 @@ function _registryFetch(name) {
138
158
  });
139
159
  }
140
160
 
161
+ function _httpGetText(url, redirectsLeft) {
162
+ // Plain text GET that follows redirects (publicsuffix.org has issued
163
+ // 30x to a CDN host in the past). Same node:https rationale as
164
+ // _registryFetch — the gate runs before any framework state exists.
165
+ if (redirectsLeft === undefined) redirectsLeft = 5;
166
+ return new Promise(function (resolve, reject) {
167
+ var req = https.get(url, { timeout: TIMEOUT_MS,
168
+ headers: { "User-Agent": "blamejs-vendor-currency/1", "Accept": "text/plain,*/*" }
169
+ }, function (res) {
170
+ var sc = res.statusCode;
171
+ if (sc >= 300 && sc < 400 && res.headers.location) {
172
+ res.resume();
173
+ if (redirectsLeft <= 0) return reject(new Error("too many redirects for " + url));
174
+ var next;
175
+ try { next = new URL(res.headers.location, url).toString(); }
176
+ catch (e) { return reject(e); }
177
+ return resolve(_httpGetText(next, redirectsLeft - 1));
178
+ }
179
+ if (sc !== 200) {
180
+ res.resume();
181
+ return reject(new Error("GET " + url + " status " + sc));
182
+ }
183
+ var chunks = [];
184
+ res.on("data", function (c) { chunks.push(c); });
185
+ res.on("end", function () { resolve(Buffer.concat(chunks).toString("utf8")); });
186
+ });
187
+ req.on("timeout", function () { req.destroy(new Error("GET " + url + " timed out after " + TIMEOUT_MS + "ms")); });
188
+ req.on("error", reject);
189
+ });
190
+ }
191
+
192
+ // Pure comparison for content-tracked data-file vendors (no semver,
193
+ // no npm registry). Extracted so it is unit-testable without network:
194
+ // feed it the upstream + local file text and the SPECIAL_MAP entry.
195
+ //
196
+ // Primary signal is an immutable upstream identifier embedded in the
197
+ // file (the PSL ships `// COMMIT: <sha>`); the fallback is a version/
198
+ // timestamp header (`// VERSION: <ts>`). Both survive our appended
199
+ // canary block and trailing-whitespace churn — they change iff the
200
+ // upstream data changed. Returns { stale, basis, upstreamId, localId }
201
+ // or throws when neither side yields a comparable identifier (the
202
+ // caller maps that to "registry-error" so an upstream-format change
203
+ // surfaces loudly instead of silently passing the gate).
204
+ function _classifyContentCurrency(upstreamText, localText, special) {
205
+ function pick(text, re) { var m = re && text.match(re); return (m && m[1]) || null; }
206
+ var uCommit = pick(upstreamText, special.commitRe);
207
+ var lCommit = pick(localText, special.commitRe);
208
+ var uVer = pick(upstreamText, special.versionRe);
209
+ var lVer = pick(localText, special.versionRe);
210
+ if (uCommit && lCommit) {
211
+ return { stale: uCommit !== lCommit, basis: "commit", upstreamId: uCommit, localId: lCommit,
212
+ upstreamVersion: uVer, localVersion: lVer };
213
+ }
214
+ if (uVer && lVer) {
215
+ return { stale: uVer !== lVer, basis: "version", upstreamId: uVer, localId: lVer,
216
+ upstreamVersion: uVer, localVersion: lVer };
217
+ }
218
+ throw new Error("no comparable COMMIT/VERSION identifier in upstream or local copy");
219
+ }
220
+
141
221
  function _semverParse(v) {
142
222
  // Strip leading "v" + any pre-release tail. Returns [maj, min, pat]
143
223
  // as numbers, or null if not parseable.
@@ -197,6 +277,30 @@ async function _checkOne(key, manifestEntry) {
197
277
  };
198
278
  }
199
279
  }
280
+ if (special && special.type === "http-content") {
281
+ // Data file tracked off a bare upstream URL (e.g. the PSL). Fetch
282
+ // upstream, read our bundled copy, compare embedded COMMIT/VERSION.
283
+ try {
284
+ var upstreamText = await _httpGetText(special.url);
285
+ var localPath = path.join(__dirname, "..", "lib", "vendor", special.localFile);
286
+ var localText = fs.readFileSync(localPath, "utf8");
287
+ var verdict = _classifyContentCurrency(upstreamText, localText, special);
288
+ return {
289
+ key: key,
290
+ upstream: special.url,
291
+ bundledAt: verdict.localVersion || verdict.localId,
292
+ upstreamAt: verdict.upstreamVersion || verdict.upstreamId,
293
+ basis: verdict.basis,
294
+ status: verdict.stale ? "stale" : "current",
295
+ };
296
+ } catch (e) {
297
+ return {
298
+ key: key,
299
+ status: "registry-error",
300
+ error: (e && e.message) || String(e),
301
+ };
302
+ }
303
+ }
200
304
  if (special && special.type === "npm-meta") {
201
305
  // Check each component independently. Aggregate results.
202
306
  var componentResults = [];
@@ -343,7 +447,18 @@ async function main() {
343
447
  process.exit(0);
344
448
  }
345
449
 
346
- main().catch(function (e) {
347
- process.stderr.write("[vendor-currency] script crashed: " + (e && e.stack || e) + "\n");
348
- process.exit(2);
349
- });
450
+ // Exported for hermetic unit tests (the content-currency classifier is
451
+ // pure no network and is the load-bearing logic for PSL drift).
452
+ module.exports = {
453
+ _classifyContentCurrency: _classifyContentCurrency,
454
+ _semverParse: _semverParse,
455
+ _semverCompare: _semverCompare,
456
+ SPECIAL_MAP: SPECIAL_MAP,
457
+ };
458
+
459
+ if (require.main === module) {
460
+ main().catch(function (e) {
461
+ process.stderr.write("[vendor-currency] script crashed: " + (e && e.stack || e) + "\n");
462
+ process.exit(2);
463
+ });
464
+ }
@@ -5876,6 +5876,7 @@ async function testBackupBundleCreateEndToEnd() {
5876
5876
  check("emitted manifest is parse-valid", m.version === 1);
5877
5877
  check("manifest has 4 file entries", m.files.length === 4);
5878
5878
  check("manifest carries operator metadata", m.metadata && m.metadata.reason === "test-end-to-end");
5879
+ check("manifest marks blobs AEAD path-bound", m.aadBound === true);
5879
5880
 
5880
5881
  // Vault key round-trip — decrypt with passphrase + bundled salt
5881
5882
  var vkBytes = await b.backupCrypto.decryptWithPassphrase(
@@ -5891,7 +5892,10 @@ async function testBackupBundleCreateEndToEnd() {
5891
5892
  var blob = fs.readFileSync(blobPath);
5892
5893
  check("blob size matches manifest.encryptedSize for " + entry.relativePath,
5893
5894
  blob.length === entry.encryptedSize);
5894
- var dec = await b.backupCrypto.decryptWithPassphrase(blob, passphrase, entry.salt);
5895
+ // Blobs are sealed with their relativePath as AEAD associated data
5896
+ // (manifest.aadBound) — pass it so the manual decrypt matches.
5897
+ var blobAad = m.aadBound === true ? entry.relativePath : undefined;
5898
+ var dec = await b.backupCrypto.decryptWithPassphrase(blob, passphrase, entry.salt, blobAad);
5895
5899
  var origPath = path.join(fx.dataDir, entry.relativePath);
5896
5900
  var orig = fs.readFileSync(origPath);
5897
5901
  check("decrypted blob matches original plaintext for " + entry.relativePath,
@@ -52,11 +52,14 @@ function _x509GeneralizedTime(d) {
52
52
  }
53
53
  function _buildMlDsaCert(opts) {
54
54
  // opts: { subjectCn, subjectPubKey, issuerCn, issuerSecretKey, serial,
55
- // notBefore, notAfter }. signatureAlgorithm == subject key alg
56
- // (ML-DSA-65) for both self-signed (CA) and issued (leaf) shapes.
55
+ // notBefore, notAfter, isCa }. signatureAlgorithm == subject key
56
+ // alg (ML-DSA-65) for both self-signed (CA) and issued (leaf) shapes.
57
+ // isCa: true emits a critical basicConstraints cA:TRUE extension so the
58
+ // cert is accepted as a chain issuer (a CA cert without it is rejected
59
+ // by the cA-enforcing chain walk — RFC 5280 §4.2.1.9 / CVE-2002-0862).
57
60
  var algId = asn1.writeSequence([asn1.writeOid(cms.OID.mldsa65)]);
58
61
  var spki = asn1.writeSequence([algId, asn1.writeBitString(Buffer.from(opts.subjectPubKey), 0)]);
59
- var tbs = asn1.writeSequence([
62
+ var tbsFields = [
60
63
  asn1.writeContextExplicit(0, asn1.writeInteger(Buffer.from([2]))), // version v3
61
64
  asn1.writeInteger(Buffer.from([opts.serial])), // serialNumber
62
65
  algId, // signature AlgorithmIdentifier
@@ -65,7 +68,17 @@ function _buildMlDsaCert(opts) {
65
68
  _x509GeneralizedTime(opts.notAfter)]), // validity
66
69
  _x509Name(opts.subjectCn), // subject
67
70
  spki, // SubjectPublicKeyInfo
68
- ]);
71
+ ];
72
+ if (opts.isCa) {
73
+ var bcValue = asn1.writeSequence([asn1.writeBoolean(true)]); // BasicConstraints { cA TRUE }
74
+ var bcExt = asn1.writeSequence([
75
+ asn1.writeOid("2.5.29.19"), // basicConstraints
76
+ asn1.writeBoolean(true), // critical
77
+ asn1.writeOctetString(Buffer.from(bcValue)), // extnValue
78
+ ]);
79
+ tbsFields.push(asn1.writeContextExplicit(3, asn1.writeSequence([bcExt]))); // [3] EXPLICIT Extensions
80
+ }
81
+ var tbs = asn1.writeSequence(tbsFields);
69
82
  var sig = b.pqcSoftware.ml_dsa_65.sign(new Uint8Array(tbs), opts.issuerSecretKey);
70
83
  return asn1.writeSequence([tbs, algId, asn1.writeBitString(Buffer.from(sig), 0)]);
71
84
  }
@@ -195,6 +208,7 @@ async function run() {
195
208
  serial: 1,
196
209
  notBefore: notBefore,
197
210
  notAfter: notAfter,
211
+ isCa: true, // trust anchor must assert cA:TRUE
198
212
  });
199
213
  var pqcLeafCertDer = _buildMlDsaCert({
200
214
  subjectCn: "smime-signer.blamejs-test.example",