@blamejs/blamejs-shop 0.4.55 → 0.4.57

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 (399) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/admin.js +385 -2
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/vendor/MANIFEST.json +426 -366
  5. package/lib/vendor/blamejs/.github/codeql/codeql-config.yml +25 -0
  6. package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
  7. package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +1 -1
  8. package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +1 -1
  9. package/lib/vendor/blamejs/.github/workflows/ci.yml +13 -13
  10. package/lib/vendor/blamejs/.github/workflows/codeql.yml +8 -7
  11. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +2 -2
  12. package/lib/vendor/blamejs/.github/workflows/release-container.yml +4 -4
  13. package/lib/vendor/blamejs/.github/workflows/scorecard.yml +1 -1
  14. package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
  15. package/lib/vendor/blamejs/CHANGELOG.md +2 -0
  16. package/lib/vendor/blamejs/api-snapshot.json +1381 -4
  17. package/lib/vendor/blamejs/eslint.config.mjs +1 -0
  18. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +4 -1
  19. package/lib/vendor/blamejs/examples/wiki/lib/symbol-index.js +5 -2
  20. package/lib/vendor/blamejs/examples/wiki/routes/pages.js +4 -1
  21. package/lib/vendor/blamejs/fuzz/guard-text.fuzz.js +20 -0
  22. package/lib/vendor/blamejs/index.js +2 -0
  23. package/lib/vendor/blamejs/lib/a2a-tasks.js +7 -23
  24. package/lib/vendor/blamejs/lib/acme.js +6 -5
  25. package/lib/vendor/blamejs/lib/agent-event-bus.js +5 -4
  26. package/lib/vendor/blamejs/lib/agent-idempotency.js +2 -5
  27. package/lib/vendor/blamejs/lib/agent-orchestrator.js +2 -5
  28. package/lib/vendor/blamejs/lib/agent-saga.js +3 -5
  29. package/lib/vendor/blamejs/lib/agent-tenant.js +2 -5
  30. package/lib/vendor/blamejs/lib/ai-adverse-decision.js +2 -15
  31. package/lib/vendor/blamejs/lib/ai-capability.js +1 -6
  32. package/lib/vendor/blamejs/lib/ai-dp.js +1 -5
  33. package/lib/vendor/blamejs/lib/ai-input.js +2 -2
  34. package/lib/vendor/blamejs/lib/ai-pref.js +3 -8
  35. package/lib/vendor/blamejs/lib/ai-quota.js +3 -14
  36. package/lib/vendor/blamejs/lib/api-key.js +37 -28
  37. package/lib/vendor/blamejs/lib/archive-adapters.js +2 -4
  38. package/lib/vendor/blamejs/lib/archive-entry-policy.js +32 -0
  39. package/lib/vendor/blamejs/lib/archive-read.js +5 -17
  40. package/lib/vendor/blamejs/lib/archive-tar-read.js +5 -16
  41. package/lib/vendor/blamejs/lib/archive.js +2 -10
  42. package/lib/vendor/blamejs/lib/arg-parser.js +7 -6
  43. package/lib/vendor/blamejs/lib/asyncapi-traits.js +2 -6
  44. package/lib/vendor/blamejs/lib/atomic-file.js +108 -31
  45. package/lib/vendor/blamejs/lib/audit-chain.js +133 -53
  46. package/lib/vendor/blamejs/lib/audit-daily-review.js +24 -14
  47. package/lib/vendor/blamejs/lib/audit-emit.js +82 -0
  48. package/lib/vendor/blamejs/lib/audit-sign.js +257 -0
  49. package/lib/vendor/blamejs/lib/audit.js +84 -0
  50. package/lib/vendor/blamejs/lib/auth/access-lock.js +5 -27
  51. package/lib/vendor/blamejs/lib/auth/dpop.js +23 -35
  52. package/lib/vendor/blamejs/lib/auth/fido-mds3.js +9 -15
  53. package/lib/vendor/blamejs/lib/auth/jwt-external.js +26 -8
  54. package/lib/vendor/blamejs/lib/auth/jwt.js +13 -15
  55. package/lib/vendor/blamejs/lib/auth/lockout.js +6 -25
  56. package/lib/vendor/blamejs/lib/auth/oauth.js +67 -45
  57. package/lib/vendor/blamejs/lib/auth/oid4vci.js +55 -32
  58. package/lib/vendor/blamejs/lib/auth/oid4vp.js +3 -2
  59. package/lib/vendor/blamejs/lib/auth/openid-federation.js +6 -6
  60. package/lib/vendor/blamejs/lib/auth/passkey.js +3 -3
  61. package/lib/vendor/blamejs/lib/auth/password.js +7 -1
  62. package/lib/vendor/blamejs/lib/auth/saml.js +37 -27
  63. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +2 -14
  64. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-issuer.js +2 -14
  65. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +1 -1
  66. package/lib/vendor/blamejs/lib/auth/status-list.js +7 -7
  67. package/lib/vendor/blamejs/lib/auth/step-up.js +6 -12
  68. package/lib/vendor/blamejs/lib/auth-bot-challenge.js +6 -37
  69. package/lib/vendor/blamejs/lib/backup/bundle.js +11 -18
  70. package/lib/vendor/blamejs/lib/backup/index.js +14 -47
  71. package/lib/vendor/blamejs/lib/bounded-map.js +112 -1
  72. package/lib/vendor/blamejs/lib/breach-deadline.js +1 -11
  73. package/lib/vendor/blamejs/lib/cache.js +7 -18
  74. package/lib/vendor/blamejs/lib/cbor.js +2 -1
  75. package/lib/vendor/blamejs/lib/cdn-cache-control.js +8 -9
  76. package/lib/vendor/blamejs/lib/cert.js +3 -10
  77. package/lib/vendor/blamejs/lib/chain-writer.js +162 -47
  78. package/lib/vendor/blamejs/lib/cli.js +5 -1
  79. package/lib/vendor/blamejs/lib/client-hints.js +7 -9
  80. package/lib/vendor/blamejs/lib/cloud-events.js +40 -31
  81. package/lib/vendor/blamejs/lib/cluster-storage.js +2 -38
  82. package/lib/vendor/blamejs/lib/cms-codec.js +2 -1
  83. package/lib/vendor/blamejs/lib/codepoint-class.js +67 -1
  84. package/lib/vendor/blamejs/lib/compliance-ai-act-logging.js +1 -6
  85. package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +2 -4
  86. package/lib/vendor/blamejs/lib/compliance-eaa.js +1 -11
  87. package/lib/vendor/blamejs/lib/compliance-sanctions-fetcher.js +21 -28
  88. package/lib/vendor/blamejs/lib/compliance-sanctions.js +2 -14
  89. package/lib/vendor/blamejs/lib/compliance.js +2 -9
  90. package/lib/vendor/blamejs/lib/config-drift.js +2 -12
  91. package/lib/vendor/blamejs/lib/content-digest.js +10 -8
  92. package/lib/vendor/blamejs/lib/cookies.js +5 -11
  93. package/lib/vendor/blamejs/lib/cose.js +19 -11
  94. package/lib/vendor/blamejs/lib/cra-report.js +1 -11
  95. package/lib/vendor/blamejs/lib/crypto-field.js +5 -10
  96. package/lib/vendor/blamejs/lib/crypto.js +120 -3
  97. package/lib/vendor/blamejs/lib/csp.js +235 -3
  98. package/lib/vendor/blamejs/lib/daemon.js +42 -41
  99. package/lib/vendor/blamejs/lib/data-act.js +19 -9
  100. package/lib/vendor/blamejs/lib/db-query.js +6 -40
  101. package/lib/vendor/blamejs/lib/db.js +34 -12
  102. package/lib/vendor/blamejs/lib/dbsc.js +5 -6
  103. package/lib/vendor/blamejs/lib/ddl-change-control.js +18 -14
  104. package/lib/vendor/blamejs/lib/deprecate.js +4 -5
  105. package/lib/vendor/blamejs/lib/did.js +6 -2
  106. package/lib/vendor/blamejs/lib/dsr.js +8 -12
  107. package/lib/vendor/blamejs/lib/external-db-migrate.js +21 -22
  108. package/lib/vendor/blamejs/lib/external-db.js +14 -26
  109. package/lib/vendor/blamejs/lib/fda-21cfr11.js +12 -8
  110. package/lib/vendor/blamejs/lib/fdx.js +22 -18
  111. package/lib/vendor/blamejs/lib/file-upload.js +80 -66
  112. package/lib/vendor/blamejs/lib/flag-evaluation-context.js +5 -8
  113. package/lib/vendor/blamejs/lib/framework-error.js +12 -0
  114. package/lib/vendor/blamejs/lib/framework-schema.js +19 -0
  115. package/lib/vendor/blamejs/lib/fsm.js +7 -12
  116. package/lib/vendor/blamejs/lib/gate-contract.js +869 -38
  117. package/lib/vendor/blamejs/lib/gdpr-ropa.js +4 -13
  118. package/lib/vendor/blamejs/lib/graphql-federation.js +1 -1
  119. package/lib/vendor/blamejs/lib/guard-agent-registry.js +2 -1
  120. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  121. package/lib/vendor/blamejs/lib/guard-archive.js +9 -30
  122. package/lib/vendor/blamejs/lib/guard-auth.js +23 -80
  123. package/lib/vendor/blamejs/lib/guard-cidr.js +20 -96
  124. package/lib/vendor/blamejs/lib/guard-csv.js +135 -196
  125. package/lib/vendor/blamejs/lib/guard-domain.js +23 -106
  126. package/lib/vendor/blamejs/lib/guard-dsn.js +16 -13
  127. package/lib/vendor/blamejs/lib/guard-email.js +46 -53
  128. package/lib/vendor/blamejs/lib/guard-envelope.js +1 -1
  129. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +2 -1
  130. package/lib/vendor/blamejs/lib/guard-filename.js +12 -60
  131. package/lib/vendor/blamejs/lib/guard-graphql.js +28 -75
  132. package/lib/vendor/blamejs/lib/guard-html-wcag.js +15 -2
  133. package/lib/vendor/blamejs/lib/guard-html.js +65 -117
  134. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +2 -1
  135. package/lib/vendor/blamejs/lib/guard-image.js +280 -77
  136. package/lib/vendor/blamejs/lib/guard-imap-command.js +8 -9
  137. package/lib/vendor/blamejs/lib/guard-json.js +87 -103
  138. package/lib/vendor/blamejs/lib/guard-jsonpath.js +20 -88
  139. package/lib/vendor/blamejs/lib/guard-jwt.js +32 -114
  140. package/lib/vendor/blamejs/lib/guard-list-id.js +2 -7
  141. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -7
  142. package/lib/vendor/blamejs/lib/guard-mail-compose.js +5 -6
  143. package/lib/vendor/blamejs/lib/guard-mail-move.js +2 -1
  144. package/lib/vendor/blamejs/lib/guard-mail-query.js +5 -3
  145. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +6 -7
  146. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +5 -4
  147. package/lib/vendor/blamejs/lib/guard-markdown.js +76 -110
  148. package/lib/vendor/blamejs/lib/guard-message-id.js +5 -6
  149. package/lib/vendor/blamejs/lib/guard-mime.js +20 -99
  150. package/lib/vendor/blamejs/lib/guard-oauth.js +19 -73
  151. package/lib/vendor/blamejs/lib/guard-pdf.js +65 -72
  152. package/lib/vendor/blamejs/lib/guard-pop3-command.js +12 -13
  153. package/lib/vendor/blamejs/lib/guard-posture-chain.js +2 -1
  154. package/lib/vendor/blamejs/lib/guard-regex.js +24 -99
  155. package/lib/vendor/blamejs/lib/guard-saga-config.js +2 -1
  156. package/lib/vendor/blamejs/lib/guard-shell.js +22 -87
  157. package/lib/vendor/blamejs/lib/guard-smtp-command.js +8 -11
  158. package/lib/vendor/blamejs/lib/guard-sql.js +15 -13
  159. package/lib/vendor/blamejs/lib/guard-stream-args.js +2 -1
  160. package/lib/vendor/blamejs/lib/guard-svg.js +95 -140
  161. package/lib/vendor/blamejs/lib/guard-template.js +23 -80
  162. package/lib/vendor/blamejs/lib/guard-tenant-id.js +2 -1
  163. package/lib/vendor/blamejs/lib/guard-text.js +592 -0
  164. package/lib/vendor/blamejs/lib/guard-time.js +27 -95
  165. package/lib/vendor/blamejs/lib/guard-uuid.js +21 -93
  166. package/lib/vendor/blamejs/lib/guard-xml.js +76 -106
  167. package/lib/vendor/blamejs/lib/guard-yaml.js +24 -60
  168. package/lib/vendor/blamejs/lib/http-client-cache.js +5 -12
  169. package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +2 -4
  170. package/lib/vendor/blamejs/lib/http-client.js +8 -21
  171. package/lib/vendor/blamejs/lib/http-message-signature.js +3 -2
  172. package/lib/vendor/blamejs/lib/i18n-messageformat.js +5 -7
  173. package/lib/vendor/blamejs/lib/i18n.js +83 -26
  174. package/lib/vendor/blamejs/lib/inbox.js +8 -8
  175. package/lib/vendor/blamejs/lib/incident-report.js +3 -21
  176. package/lib/vendor/blamejs/lib/ip-utils.js +49 -6
  177. package/lib/vendor/blamejs/lib/jobs.js +3 -2
  178. package/lib/vendor/blamejs/lib/keychain.js +6 -18
  179. package/lib/vendor/blamejs/lib/legal-hold.js +6 -15
  180. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +44 -112
  181. package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +17 -14
  182. package/lib/vendor/blamejs/lib/log-stream-otlp.js +16 -80
  183. package/lib/vendor/blamejs/lib/log-stream-webhook.js +20 -92
  184. package/lib/vendor/blamejs/lib/mail-arc-sign.js +8 -3
  185. package/lib/vendor/blamejs/lib/mail-arf.js +3 -2
  186. package/lib/vendor/blamejs/lib/mail-auth.js +40 -66
  187. package/lib/vendor/blamejs/lib/mail-bimi.js +19 -39
  188. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +3 -2
  189. package/lib/vendor/blamejs/lib/mail-dav.js +8 -35
  190. package/lib/vendor/blamejs/lib/mail-deploy.js +6 -9
  191. package/lib/vendor/blamejs/lib/mail-dkim.js +19 -38
  192. package/lib/vendor/blamejs/lib/mail-greylist.js +15 -26
  193. package/lib/vendor/blamejs/lib/mail-helo.js +2 -3
  194. package/lib/vendor/blamejs/lib/mail-journal.js +2 -1
  195. package/lib/vendor/blamejs/lib/mail-mdn.js +4 -3
  196. package/lib/vendor/blamejs/lib/mail-rbl.js +5 -8
  197. package/lib/vendor/blamejs/lib/mail-scan.js +2 -2
  198. package/lib/vendor/blamejs/lib/mail-send-deliver.js +33 -20
  199. package/lib/vendor/blamejs/lib/mail-server-imap.js +32 -87
  200. package/lib/vendor/blamejs/lib/mail-server-jmap.js +35 -51
  201. package/lib/vendor/blamejs/lib/mail-server-managesieve.js +29 -83
  202. package/lib/vendor/blamejs/lib/mail-server-mx.js +33 -74
  203. package/lib/vendor/blamejs/lib/mail-server-net.js +177 -0
  204. package/lib/vendor/blamejs/lib/mail-server-pop3.js +30 -83
  205. package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +30 -6
  206. package/lib/vendor/blamejs/lib/mail-server-submission.js +34 -73
  207. package/lib/vendor/blamejs/lib/mail-server-tls.js +89 -0
  208. package/lib/vendor/blamejs/lib/mail-spam-score.js +7 -8
  209. package/lib/vendor/blamejs/lib/mail-store.js +3 -2
  210. package/lib/vendor/blamejs/lib/mail.js +6 -11
  211. package/lib/vendor/blamejs/lib/markup-escape.js +31 -0
  212. package/lib/vendor/blamejs/lib/markup-tokenizer.js +54 -0
  213. package/lib/vendor/blamejs/lib/mcp-tool-registry.js +26 -23
  214. package/lib/vendor/blamejs/lib/mcp.js +1 -1
  215. package/lib/vendor/blamejs/lib/mdoc.js +11 -12
  216. package/lib/vendor/blamejs/lib/metrics.js +32 -30
  217. package/lib/vendor/blamejs/lib/middleware/age-gate.js +3 -23
  218. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +11 -22
  219. package/lib/vendor/blamejs/lib/middleware/assetlinks.js +2 -7
  220. package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +2 -1
  221. package/lib/vendor/blamejs/lib/middleware/body-parser.js +31 -48
  222. package/lib/vendor/blamejs/lib/middleware/bot-disclose.js +7 -10
  223. package/lib/vendor/blamejs/lib/middleware/bot-guard.js +2 -10
  224. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +13 -2
  225. package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +6 -26
  226. package/lib/vendor/blamejs/lib/middleware/deny-response.js +19 -1
  227. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +7 -0
  228. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +4 -20
  229. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +20 -28
  230. package/lib/vendor/blamejs/lib/middleware/scim-server.js +3 -2
  231. package/lib/vendor/blamejs/lib/middleware/security-headers.js +9 -1
  232. package/lib/vendor/blamejs/lib/middleware/security-txt.js +2 -6
  233. package/lib/vendor/blamejs/lib/middleware/tus-upload.js +12 -27
  234. package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +2 -7
  235. package/lib/vendor/blamejs/lib/migrations.js +4 -13
  236. package/lib/vendor/blamejs/lib/mime-parse.js +34 -17
  237. package/lib/vendor/blamejs/lib/module-loader.js +44 -0
  238. package/lib/vendor/blamejs/lib/money.js +105 -0
  239. package/lib/vendor/blamejs/lib/mtls-ca.js +116 -36
  240. package/lib/vendor/blamejs/lib/network-byte-quota.js +4 -18
  241. package/lib/vendor/blamejs/lib/network-dane.js +8 -6
  242. package/lib/vendor/blamejs/lib/network-dns-resolver.js +97 -6
  243. package/lib/vendor/blamejs/lib/network-dnssec.js +16 -16
  244. package/lib/vendor/blamejs/lib/network-heartbeat.js +3 -2
  245. package/lib/vendor/blamejs/lib/network-smtp-policy.js +29 -41
  246. package/lib/vendor/blamejs/lib/network-tls.js +40 -42
  247. package/lib/vendor/blamejs/lib/nis2-report.js +1 -11
  248. package/lib/vendor/blamejs/lib/nonce-store.js +81 -3
  249. package/lib/vendor/blamejs/lib/numeric-bounds.js +22 -8
  250. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +2 -6
  251. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +5 -45
  252. package/lib/vendor/blamejs/lib/object-store/gcs.js +8 -71
  253. package/lib/vendor/blamejs/lib/object-store/http-put.js +1 -5
  254. package/lib/vendor/blamejs/lib/object-store/http-request.js +128 -0
  255. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +2 -1
  256. package/lib/vendor/blamejs/lib/object-store/sigv4.js +9 -77
  257. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -25
  258. package/lib/vendor/blamejs/lib/observability.js +95 -0
  259. package/lib/vendor/blamejs/lib/openapi-paths-builder.js +7 -3
  260. package/lib/vendor/blamejs/lib/otel-export.js +7 -10
  261. package/lib/vendor/blamejs/lib/outbox.js +43 -37
  262. package/lib/vendor/blamejs/lib/pagination.js +11 -15
  263. package/lib/vendor/blamejs/lib/parsers/safe-env.js +5 -4
  264. package/lib/vendor/blamejs/lib/parsers/safe-ini.js +10 -4
  265. package/lib/vendor/blamejs/lib/parsers/safe-toml.js +11 -9
  266. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +2 -2
  267. package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +7 -6
  268. package/lib/vendor/blamejs/lib/pick.js +63 -5
  269. package/lib/vendor/blamejs/lib/pipl-cn.js +69 -59
  270. package/lib/vendor/blamejs/lib/privacy-pass.js +8 -6
  271. package/lib/vendor/blamejs/lib/problem-details.js +2 -5
  272. package/lib/vendor/blamejs/lib/pubsub.js +4 -8
  273. package/lib/vendor/blamejs/lib/redact.js +2 -1
  274. package/lib/vendor/blamejs/lib/render.js +4 -2
  275. package/lib/vendor/blamejs/lib/request-helpers.js +133 -6
  276. package/lib/vendor/blamejs/lib/restore.js +5 -22
  277. package/lib/vendor/blamejs/lib/retention.js +3 -5
  278. package/lib/vendor/blamejs/lib/safe-async.js +457 -0
  279. package/lib/vendor/blamejs/lib/safe-buffer.js +137 -6
  280. package/lib/vendor/blamejs/lib/safe-decompress.js +2 -1
  281. package/lib/vendor/blamejs/lib/safe-dns.js +2 -1
  282. package/lib/vendor/blamejs/lib/safe-ical.js +12 -26
  283. package/lib/vendor/blamejs/lib/safe-json.js +7 -8
  284. package/lib/vendor/blamejs/lib/safe-jsonpath.js +6 -5
  285. package/lib/vendor/blamejs/lib/safe-mime.js +25 -29
  286. package/lib/vendor/blamejs/lib/safe-redirect.js +2 -5
  287. package/lib/vendor/blamejs/lib/safe-schema.js +7 -6
  288. package/lib/vendor/blamejs/lib/safe-sql.js +126 -0
  289. package/lib/vendor/blamejs/lib/safe-vcard.js +12 -26
  290. package/lib/vendor/blamejs/lib/sandbox-worker.js +9 -2
  291. package/lib/vendor/blamejs/lib/sandbox.js +3 -2
  292. package/lib/vendor/blamejs/lib/scheduler.js +5 -12
  293. package/lib/vendor/blamejs/lib/sec-cyber.js +82 -37
  294. package/lib/vendor/blamejs/lib/seeders.js +4 -11
  295. package/lib/vendor/blamejs/lib/self-update.js +92 -69
  296. package/lib/vendor/blamejs/lib/session-device-binding.js +112 -66
  297. package/lib/vendor/blamejs/lib/session.js +194 -6
  298. package/lib/vendor/blamejs/lib/sql.js +225 -78
  299. package/lib/vendor/blamejs/lib/sse.js +6 -10
  300. package/lib/vendor/blamejs/lib/static.js +136 -98
  301. package/lib/vendor/blamejs/lib/storage.js +4 -2
  302. package/lib/vendor/blamejs/lib/structured-fields.js +275 -16
  303. package/lib/vendor/blamejs/lib/tenant-quota.js +2 -20
  304. package/lib/vendor/blamejs/lib/tsa.js +11 -15
  305. package/lib/vendor/blamejs/lib/validate-opts.js +154 -8
  306. package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +30 -48
  307. package/lib/vendor/blamejs/lib/vault-aad.js +3 -2
  308. package/lib/vendor/blamejs/lib/vc.js +2 -7
  309. package/lib/vendor/blamejs/lib/vex.js +35 -13
  310. package/lib/vendor/blamejs/lib/web-push-vapid.js +23 -20
  311. package/lib/vendor/blamejs/lib/webhook-dispatcher.js +706 -0
  312. package/lib/vendor/blamejs/lib/webhook.js +43 -18
  313. package/lib/vendor/blamejs/lib/websocket-channels.js +9 -7
  314. package/lib/vendor/blamejs/lib/websocket.js +7 -3
  315. package/lib/vendor/blamejs/lib/worm.js +2 -3
  316. package/lib/vendor/blamejs/lib/ws-client.js +8 -10
  317. package/lib/vendor/blamejs/package.json +1 -1
  318. package/lib/vendor/blamejs/release-notes/v0.15.13.json +81 -0
  319. package/lib/vendor/blamejs/scripts/check-changelog-extract.js +1 -1
  320. package/lib/vendor/blamejs/test/00-primitives.js +136 -7
  321. package/lib/vendor/blamejs/test/10-state.js +9 -3
  322. package/lib/vendor/blamejs/test/30-chain.js +40 -0
  323. package/lib/vendor/blamejs/test/helpers/check.js +18 -0
  324. package/lib/vendor/blamejs/test/helpers/db.js +4 -0
  325. package/lib/vendor/blamejs/test/helpers/index.js +1 -0
  326. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +2 -1
  327. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +2 -1
  328. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +8 -1
  329. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +1 -1
  330. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +2 -1
  331. package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +269 -0
  332. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-fd-read.test.js +149 -0
  333. package/lib/vendor/blamejs/test/layer-0-primitives/audit-framework-namespaces.test.js +51 -0
  334. package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +88 -0
  335. package/lib/vendor/blamejs/test/layer-0-primitives/audit-use-store.test.js +55 -1
  336. package/lib/vendor/blamejs/test/layer-0-primitives/backup-object-store-adapter.test.js +2 -2
  337. package/lib/vendor/blamejs/test/layer-0-primitives/bot-guard.test.js +1 -1
  338. package/lib/vendor/blamejs/test/layer-0-primitives/bounded-map.test.js +129 -0
  339. package/lib/vendor/blamejs/test/layer-0-primitives/chain-writer-multichain.test.js +107 -0
  340. package/lib/vendor/blamejs/test/layer-0-primitives/cli-api-key.test.js +12 -0
  341. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +2788 -3214
  342. package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +78 -0
  343. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-base64url.test.js +63 -0
  344. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +2 -1
  345. package/lib/vendor/blamejs/test/layer-0-primitives/csp-builder.test.js +50 -0
  346. package/lib/vendor/blamejs/test/layer-0-primitives/csp-nonce.test.js +1 -1
  347. package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +2 -1
  348. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-resolve-opts.test.js +76 -0
  349. package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +9 -0
  350. package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract-content-gate.test.js +290 -0
  351. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +84 -0
  352. package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +78 -0
  353. package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +23 -0
  354. package/lib/vendor/blamejs/test/layer-0-primitives/guard-gate-disposition-coverage.test.js +93 -0
  355. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +60 -0
  356. package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +184 -0
  357. package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
  358. package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +54 -0
  359. package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +10 -0
  360. package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +45 -0
  361. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +101 -0
  362. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +18 -0
  363. package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +76 -0
  364. package/lib/vendor/blamejs/test/layer-0-primitives/guard-text.test.js +365 -0
  365. package/lib/vendor/blamejs/test/layer-0-primitives/guard-xml.test.js +35 -0
  366. package/lib/vendor/blamejs/test/layer-0-primitives/guard-yaml.test.js +36 -0
  367. package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +20 -0
  368. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -0
  369. package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +83 -0
  370. package/lib/vendor/blamejs/test/layer-0-primitives/mail-arf.test.js +10 -0
  371. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +39 -0
  372. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +36 -0
  373. package/lib/vendor/blamejs/test/layer-0-primitives/mail-greylist.test.js +17 -0
  374. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-tls.test.js +39 -0
  375. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +17 -0
  376. package/lib/vendor/blamejs/test/layer-0-primitives/mime-parse.test.js +106 -0
  377. package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +17 -0
  378. package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-revocation.test.js +98 -0
  379. package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-release.test.js +77 -0
  380. package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +48 -0
  381. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-memory-getorinsert.test.js +100 -0
  382. package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +88 -0
  383. package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-loops.test.js +278 -0
  384. package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +37 -0
  385. package/lib/vendor/blamejs/test/layer-0-primitives/safe-jsonpath.test.js +14 -0
  386. package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +19 -0
  387. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +1 -1
  388. package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +2 -1
  389. package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +18 -0
  390. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +1 -1
  391. package/lib/vendor/blamejs/test/layer-0-primitives/session-valid-from.test.js +73 -0
  392. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +100 -0
  393. package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields.test.js +80 -0
  394. package/lib/vendor/blamejs/test/layer-0-primitives/validate-opts-shape.test.js +235 -0
  395. package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +374 -0
  396. package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +75 -8
  397. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +3 -3
  398. package/lib/winback-campaigns.js +202 -42
  399. package/package.json +1 -1
@@ -59,6 +59,10 @@ var numericChecks = require("./numeric-checks");
59
59
  var requestHelpers = require("./request-helpers");
60
60
  var validateOpts = require("./validate-opts");
61
61
  var { WebhookError } = require("./framework-error");
62
+ // b.webhook.dispatcher — durable signed-webhook delivery store. Lives in its
63
+ // own module (distinct domain: persistence) and lazyRequires this file back,
64
+ // so this top-level require is cycle-safe.
65
+ var webhookDispatcher = require("./webhook-dispatcher");
62
66
 
63
67
  var observability = lazyRequire(function () { return require("./observability"); });
64
68
 
@@ -832,16 +836,28 @@ function _coerceBodyString(body) {
832
836
  * HMAC-SHA-256 over the literal `<t>.<rawBody>` string using the
833
837
  * operator's `whsec_...` secret bytes verbatim (the prefix IS the key).
834
838
  * Refuses signatures older than the tolerance window (default 5 min,
835
- * minimum 30 s). When `nonceStore` is supplied the verifier records
836
- * the accepted v1 signature so a replay within the tolerance window
837
- * is refused. Constant-time compare via `b.crypto.timingSafeEqual`.
839
+ * minimum 30 s). When `nonceStore` is supplied the verifier atomically
840
+ * records the accepted v1 signature so a replay within the tolerance window
841
+ * is refused; the store speaks the same `checkAndInsert(nonce, expireAt) →
842
+ * bool` contract as `b.webhook.verifier` and `b.nonceStore.create`, so the
843
+ * framework's own replay store plugs in directly and concurrent redeliveries
844
+ * cannot race. Constant-time compare via `b.crypto.timingSafeEqual`.
845
+ *
846
+ * @opts
847
+ * alg: "hmac-sha256-stripe",
848
+ * secret: string | Buffer, // whsec_... bytes verbatim
849
+ * header: string, // Stripe-Signature value
850
+ * body: string | Buffer, // raw request body
851
+ * toleranceMs: number, // default 5 min, min 30 s
852
+ * nonceStore: { checkAndInsert(nonce, expireAt) }, // optional atomic replay defense
838
853
  *
839
854
  * @example
840
- * b.webhook.verify({
841
- * alg: "hmac-sha256-stripe",
842
- * secret: "whsec_abc...",
843
- * header: req.headers["stripe-signature"],
844
- * body: rawBodyBuffer,
855
+ * await b.webhook.verify({
856
+ * alg: "hmac-sha256-stripe",
857
+ * secret: "whsec_abc...",
858
+ * header: req.headers["stripe-signature"],
859
+ * body: rawBodyBuffer,
860
+ * nonceStore: b.nonceStore.create({ backend: "memory" }),
845
861
  * });
846
862
  * // → { ok: true, timestamp: 1700000000, scheme: "v1" }
847
863
  */
@@ -889,24 +905,30 @@ async function verify(input) {
889
905
  "verify: no v1 signature matched HMAC-SHA-256 of <t>.<body>");
890
906
  }
891
907
 
892
- // Optional replay defense — the nonceStore is operator-supplied
893
- // (e.g. `b.kv` or a Redis-shaped { has, set, expire }) so the
894
- // primitive doesn't own retention. `has` / `set` MAY return a
895
- // Promise we always `await` them so async backends (Redis, KV,
896
- // DynamoDB) work without falsely flagging a Promise as truthy.
908
+ // Optional replay defense — the nonceStore speaks the SAME atomic
909
+ // `checkAndInsert(nonce, expireAt) bool` contract as b.webhook.verifier
910
+ // and b.nonceStore.create, so the framework's own replay store drops in
911
+ // with no adapter. The check + insert is one atomic step: a non-atomic
912
+ // check-then-set would race two concurrent redeliveries of the same event
913
+ // (both observe "unseen", both proceed) — the exact failure replay defense
914
+ // exists to prevent. checkAndInsert MAY return a Promise (Redis / KV /
915
+ // cluster SQL); we always await it. `expireAt` is the absolute epoch-ms at
916
+ // which the signature itself falls outside the tolerance window, so a stored
917
+ // nonce is retained exactly as long as a replay of it could still be fresh.
897
918
  if (input.nonceStore) {
898
919
  var ns = input.nonceStore;
899
- if (typeof ns.has !== "function" || typeof ns.set !== "function") {
920
+ if (typeof ns.checkAndInsert !== "function") {
900
921
  throw new WebhookError("webhook/bad-nonce-store",
901
- "verify: nonceStore must expose { has(key), set(key, ttlMs) }");
922
+ "verify: nonceStore must expose checkAndInsert(nonce, expireAt) — the " +
923
+ "b.nonceStore contract shared with b.webhook.verifier");
902
924
  }
903
925
  var nonceKey = "stripe:" + parsed.timestamp + ":" + expectedHex;
904
- var seen = await ns.has(nonceKey);
905
- if (seen) {
926
+ var expireAt = C.TIME.seconds(parsed.timestamp) + tolerance;
927
+ var fresh = await ns.checkAndInsert(nonceKey, expireAt);
928
+ if (!fresh) {
906
929
  throw new WebhookError("webhook/replay",
907
930
  "verify: signature already seen within tolerance window (replay)");
908
931
  }
909
- await ns.set(nonceKey, tolerance);
910
932
  }
911
933
 
912
934
  return { ok: true, timestamp: parsed.timestamp, scheme: "v1" };
@@ -974,4 +996,7 @@ module.exports = {
974
996
  // v0.11.25 — Stripe-shaped inbound HMAC-SHA-256 verifier + signer.
975
997
  verify: verify,
976
998
  sign: sign,
999
+ // Durable signed-webhook delivery store (sign + persist + deliver + retry +
1000
+ // dead-letter + operator replay). Defined in lib/webhook-dispatcher.js.
1001
+ dispatcher: webhookDispatcher.dispatcher,
977
1002
  };
@@ -60,6 +60,7 @@
60
60
 
61
61
  var lazyRequire = require("./lazy-require");
62
62
  var pubsub = require("./pubsub");
63
+ var boundedMap = require("./bounded-map");
63
64
  var validateOpts = require("./validate-opts");
64
65
  var { defineClass } = require("./framework-error");
65
66
 
@@ -187,17 +188,18 @@ function create(opts) {
187
188
  if (typeof channel !== "string" || channel.length === 0) {
188
189
  throw _err("INVALID_CHANNEL", "subscribe: channel must be a non-empty string");
189
190
  }
190
- if (!connToChannels.has(conn)) {
191
+ boundedMap.requirePresent(connToChannels, conn, function () {
191
192
  throw _err("NOT_ATTACHED", "subscribe: connection must be attach()-ed first");
192
- }
193
- if (!channelToConns.has(channel)) {
194
- channelToConns.set(channel, new Set());
193
+ });
194
+ var subs = boundedMap.getOrInsert(channelToConns, channel, function () {
195
195
  // First local listener for this channel — open a pubsub
196
- // subscription so cross-node fan-out reaches us.
196
+ // subscription so cross-node fan-out reaches us, recording the
197
+ // token under the same key the new Set is stored at.
197
198
  var token = ps.subscribe(channel, _onPubsubMessage);
198
199
  channelToToken.set(channel, token);
199
- }
200
- channelToConns.get(channel).add(conn);
200
+ return new Set();
201
+ });
202
+ subs.add(conn);
201
203
  connToChannels.get(conn).add(channel);
202
204
  }
203
205
 
@@ -82,6 +82,7 @@ var C = require("./constants");
82
82
  var requestHelpers = require("./request-helpers");
83
83
  var safeAsync = require("./safe-async");
84
84
  var safeBuffer = require("./safe-buffer");
85
+ var pick = require("./pick");
85
86
  var structuredFields = require("./structured-fields");
86
87
  var { FrameworkError } = require("./framework-error");
87
88
  var { boot } = require("./log");
@@ -543,7 +544,7 @@ function _parseExtensionHeader(header) {
543
544
  var kv = parts[j].split("=");
544
545
  var k = kv[0].trim().toLowerCase();
545
546
  if (!k) continue;
546
- if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
547
+ if (pick.isPoisonedKey(k)) continue;
547
548
  var v = kv.length > 1 ? kv.slice(1).join("=").trim() : true;
548
549
  // Strip surrounding quotes per the token-or-quoted-string grammar.
549
550
  if (typeof v === "string") {
@@ -1128,7 +1129,7 @@ class WebSocketConnection extends EventEmitter {
1128
1129
  return this._abort(CLOSE_INVALID_PAYLOAD,
1129
1130
  "permessage-deflate inflate failed: " + ((e && e.message) || String(e)));
1130
1131
  }
1131
- if (data.length > this.maxMessageBytes) {
1132
+ if (safeBuffer.byteLengthOf(data) > this.maxMessageBytes) {
1132
1133
  return this._abort(CLOSE_MESSAGE_TOO_BIG,
1133
1134
  "decompressed message exceeds maxMessageBytes");
1134
1135
  }
@@ -1241,8 +1242,11 @@ class WebSocketConnection extends EventEmitter {
1241
1242
  } else if (Buffer.isBuffer(data)) {
1242
1243
  this._sendDataFrame(OPCODE_BINARY, data);
1243
1244
  } else {
1245
+ // WebSocketError's constructor is (code, message, closeCode) — code
1246
+ // first — so the (message, code) errorClass path would swap the two.
1247
+ // errorFactory hands toBuffer a (code, message) constructor instead.
1244
1248
  data = safeBuffer.toBuffer(data, {
1245
- errorClass: WebSocketError,
1249
+ errorFactory: function (code, message) { return new WebSocketError(code, message); },
1246
1250
  typeCode: "ws/invalid-payload",
1247
1251
  typeMessage: "send() requires Buffer, Uint8Array, or string",
1248
1252
  });
@@ -113,10 +113,9 @@ function create(opts) {
113
113
  throw new WormError("worm/bad-opt", "worm.create: defaultRetentionMs must be a non-negative finite number");
114
114
  }
115
115
 
116
+ var _baseAudit = audit().namespaced("worm");
116
117
  function _emit(action, outcome, id, metadata) {
117
- try {
118
- audit().safeEmit({ action: "worm." + action, actor: { type: "system" }, outcome: outcome, metadata: Object.assign({ id: id, mode: mode }, metadata || {}) });
119
- } catch (_e) { /* audit is drop-silent by design */ }
118
+ _baseAudit(action, outcome, Object.assign({ id: id, mode: mode }, metadata || {}), { actor: { type: "system" } });
120
119
  }
121
120
 
122
121
  function _resolveRetainUntil(now, putOpts) {
@@ -60,6 +60,7 @@ var audit = lazyRequire(function () { return require("./audit"); });
60
60
  var networkTls = lazyRequire(function () { return require("./network-tls"); });
61
61
  var safeJson = lazyRequire(function () { return require("./safe-json"); });
62
62
  var ssrfGuard = require("./ssrf-guard");
63
+ var structuredFields = require("./structured-fields");
63
64
  var C = require("./constants");
64
65
  var { defineClass } = require("./framework-error");
65
66
 
@@ -492,7 +493,7 @@ class WsClient extends EventEmitter {
492
493
  this._handshakeBuf = Buffer.concat([this._handshakeBuf, chunk]);
493
494
  var headerEnd = this._handshakeBuf.indexOf("\r\n\r\n");
494
495
  if (headerEnd === -1) {
495
- if (this._handshakeBuf.length > C.BYTES.kib(64)) {
496
+ if (safeBuffer.byteLengthOf(this._handshakeBuf) > C.BYTES.kib(64)) {
496
497
  this._handleSocketError(new WsClientError("ws-client/handshake-too-large",
497
498
  "handshake response exceeded 64 KiB before CRLFCRLF"));
498
499
  }
@@ -526,13 +527,10 @@ class WsClient extends EventEmitter {
526
527
  }
527
528
 
528
529
  var headers = Object.create(null);
529
- for (var i = 1; i < lines.length; i += 1) {
530
- var idx = lines[i].indexOf(":");
531
- if (idx === -1) continue;
532
- var hkey = lines[i].slice(0, idx).trim().toLowerCase();
533
- var hval = lines[i].slice(idx + 1).trim();
534
- headers[hkey] = hval;
535
- }
530
+ var hkvps = structuredFields.parseKeyValuePieces(lines, 1, ":");
531
+ structuredFields.forEachKeyValue(hkvps, function (key, value) {
532
+ headers[key] = value;
533
+ });
536
534
 
537
535
  if ((headers["upgrade"] || "").toLowerCase() !== "websocket" ||
538
536
  (headers["connection"] || "").toLowerCase().indexOf("upgrade") === -1) {
@@ -698,7 +696,7 @@ class WsClient extends EventEmitter {
698
696
  }
699
697
  if (frame.fin) {
700
698
  var fullPayload = Buffer.concat(this._fragmentChunks); // allow:handrolled-buffer-collect — bounded by maxMessageBytes below
701
- if (fullPayload.length > this._opts.maxMessageBytes) {
699
+ if (safeBuffer.byteLengthOf(fullPayload) > this._opts.maxMessageBytes) {
702
700
  this._handleSocketError(new WsClientError("ws-client/message-too-big",
703
701
  "incoming message exceeds maxMessageBytes (" + this._opts.maxMessageBytes + ")"));
704
702
  return;
@@ -797,7 +795,7 @@ class WsClient extends EventEmitter {
797
795
  } else {
798
796
  payload = Buffer.from(JSON.stringify(data), "utf8");
799
797
  }
800
- if (payload.length > this._opts.maxMessageBytes) {
798
+ if (safeBuffer.byteLengthOf(payload) > this._opts.maxMessageBytes) {
801
799
  throw new WsClientError("ws-client/payload-too-big",
802
800
  "send: payload exceeds maxMessageBytes (" + this._opts.maxMessageBytes + ")");
803
801
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.15.12",
3
+ "version": "0.15.13",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
@@ -0,0 +1,81 @@
1
+ {
2
+ "$schema": "../scripts/release-notes-schema.json",
3
+ "version": "0.15.13",
4
+ "date": "2026-06-19",
5
+ "headline": "Consolidates a large set of duplicated, security-sensitive code paths — injected-dependency validation, positive-integer option caps, TOCTOU-safe file reads, markup escaping, audit emission, structured-field parsing, and untrusted-JWK import — onto shared hardened primitives, so a fix or guard now applies everywhere at once; adds a TOCTOU-safe file reader; and makes several configuration errors throw typed framework errors instead of a bare Error",
6
+ "summary": "Across the framework, logic that had been hand-rolled in many places (and had quietly drifted between copies) is now single-sourced through shared primitives. The practical effect for operators is consistency: the strongest available guard is applied everywhere a given operation happens, rather than in whichever copy happened to have it. Injected dependencies (stores, backends, vaults, db handles, query objects) are validated through one contract; operator-tunable positive-integer options (pool sizes, byte caps, timeouts, stream limits) validate through one bounds primitive that forwards the caller's non-retryable / HTTP-status flags; the open-fd → fstat → read-fully pattern that several call sites used to defend file reads against a swap-after-stat race (CWE-367) is now the b.atomicFile.fdSafeReadSync primitive, with symlink refusal, inode-equality, a byte cap, and an integrity hash available as options and each caller's exact typed error preserved; and the XML/HTML markup escapers (including the b.guardHtml / b.guardSvg sanitizer output) route through one b.markupEscape so the base & < > \" chain can't drift between them. A few configuration-validation paths that threw a bare Error (httpClient.configurePool, b.db stream-limit validation) now throw the module's typed framework error. No security defaults change; this release tightens and unifies existing protections rather than adding opt-ins.",
7
+ "sections": [
8
+ {
9
+ "heading": "Added",
10
+ "items": [
11
+ {
12
+ "title": "b.atomicFile.fdSafeReadSync(filepath, opts?)",
13
+ "body": "A TOCTOU-safe synchronous file read (CWE-367 / js/file-system-race): it opens the path read-only, then binds the size, content, and integrity measurement to the inode the fd holds open, so a swap between stat and read can't change which bytes are returned. Optional guards layer on top — maxBytes (refuse an over-cap file), refuseSymlink + inodeCheck (refuse a symlink source and any inode swap, the strongest posture for operator-writable paths), expectedHash (SHA3-512), encoding (return a string), allowShortRead — and an errorFor(kind, detail) callback lets each caller keep its own typed error. The framework's own file reads (atomic-file, the TLS certificate-path loader, the sealed-PEM source reader, the backup bundle reader) all route through it."
14
+ },
15
+ {
16
+ "title": "Shared substrate primitives for previously-duplicated logic",
17
+ "body": "A set of shared primitives now back call sites that had each re-implemented the same logic: validateOpts.requireMethods (gains a `permanent` flag) for injected-dependency contract checks; numericBounds positive-finite-int validators (gain an errorOpts { permanent, statusCode }) for tunable integer options; b.markupEscape for the & < > \" markup chain; structured-field parsing helpers (parseTagList, forEachKeyValue, splitUnquoted, stripDoubleQuotes, unfoldHeaderContinuations); crypto.importPublicJwk and crypto.makeBase64UrlDecoder; safeSql.toPositional; safeBuffer.makeByteCoercer; nonceStore.enforceReplay; and the audit/observability namespaced emitters. These are mostly invisible substrate — the operator-visible benefit is that a guard or fix added to one of them now covers every caller."
18
+ }
19
+ ]
20
+ },
21
+ {
22
+ "heading": "Changed",
23
+ "items": [
24
+ {
25
+ "title": "Configuration errors throw typed framework errors",
26
+ "body": "Two configuration-validation paths that threw a bare `Error` now throw the module's typed framework error: httpClient.configurePool (bad maxSockets / maxFreeSockets / keepAliveMsecs → HttpClientError, code httpclient/bad-opts) and the b.db stream-limit validation (Query.stream → DbQueryError). The thrown error remains an Error subclass, so existing `instanceof Error` / try-catch handling is unaffected; code that matched the old free-text message should match the typed error's `code` instead. Several routed validators also report a slightly more precise message (\"positive finite integer\" instead of \"positive integer\")."
27
+ }
28
+ ]
29
+ },
30
+ {
31
+ "heading": "Security",
32
+ "items": [
33
+ {
34
+ "title": "TOCTOU-safe file reads applied uniformly",
35
+ "body": "The open-fd → fstat → read pattern that anchors a file read to one inode against a swap-after-stat race is now centralized in b.atomicFile.fdSafeReadSync, and every framework file read routes through it. The sealed-PEM source reader keeps the strongest posture (symlink refusal + inode-equality + byte cap), and those guards are now available as options to any caller — a re-introduced hand-rolled read-loop is flagged by a codebase-pattern detector."
36
+ },
37
+ {
38
+ "title": "Injected-dependency and integer-option validation can't silently drift",
39
+ "body": "Injected stores / backends / vaults / db handles / query objects are validated through one contract (validateOpts.requireMethods), and operator-tunable positive-integer options through one bounds primitive (numericBounds), both forwarding the caller's non-retryable / HTTP-status error flags so a config error stays permanent / carries its 4xx. Inverse detectors flag any re-introduced inline check, so the validation can no longer be partially copied or weakened in one place."
40
+ },
41
+ {
42
+ "title": "Single markup-escape chain for the HTML/SVG sanitizers",
43
+ "body": "The b.guardHtml and b.guardSvg sanitizer output escapers, the AI-Act transparency and mail HTML escapers, and the XML report escapers now share one b.markupEscape for the base & < > \" chain (apostrophe form as a parameter; each caller keeps its own input coercion and any extra escapes such as guardHtml.escapeAttr's backtick / = IE-attribute-injection hardening). Byte-for-byte parity with the prior escapers was verified across an XSS-vector corpus, so the consolidation cannot have introduced an escaping gap; a re-inlined & < > \" chain anywhere else is flagged by a detector."
44
+ },
45
+ {
46
+ "title": "One hardening point for untrusted public-JWK import",
47
+ "body": "The createPublicKey-from-JWK call that imports attacker-controlled key material (DID publicKeyJwk, DNSKEY, COSE_Key, DPoP / OAuth / OIDC / DBSC proof JWKs) is centralized in b.crypto.importPublicJwk, so the untrusted-key import has a single audited home; each caller keeps its own kty/crv pre-validation and typed error."
48
+ },
49
+ {
50
+ "title": "Linear-time Content-Security-Policy header parsing",
51
+ "body": "The CSP response-header parser split directives on a `/\\s*,\\s*/` regex that ran in O(n^2) on a long comma-less run of whitespace, letting a crafted header stall a worker (js/polynomial-redos). It now splits on the literal comma and trims each item, which is linear and byte-for-byte equivalent."
52
+ },
53
+ {
54
+ "title": "Parsed INI and OpenAPI path maps can't reach Object.prototype",
55
+ "body": "The INI parser already refuses a `__proto__` / `constructor` / `prototype` section or key (it throws before any write); as defense-in-depth, every parsed node — and the OpenAPI paths map keyed by URL pattern — is now a null-prototype object, so even a future gap could only ever set an own property, never mutate Object.prototype (CWE-1321)."
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "heading": "Fixed",
61
+ "items": [
62
+ {
63
+ "title": "Webhook deliveries retry transient transport failures instead of dead-lettering them",
64
+ "body": "A delivery that failed with a transient transport error (connection reset, timeout, 5xx) was recorded as permanently failed and dead-lettered rather than retried. The destination-safety check (SSRF / blocked-host guard) now runs in its own step and is the only permanent failure; a transport failure backs off and retries up to the configured limit."
65
+ },
66
+ {
67
+ "title": "Webhook deliveries table creates on MySQL",
68
+ "body": "The pending-delivery index used a partial-index (WHERE) clause that MySQL does not support, so the deliveries table failed to create on a MySQL backend. The index is now dialect-aware — partial on PostgreSQL / SQLite, plain on MySQL."
69
+ },
70
+ {
71
+ "title": "Inline webhook delivery can't be double-sent by concurrent retry processing",
72
+ "body": "An inline delivery is now claimed (status set to in-flight with a claim timestamp) before the POST, so a concurrent retry pass can't pick up and re-send the same delivery; a row left in-flight by a crash is reclaimed after the stale-claim window."
73
+ },
74
+ {
75
+ "title": "Audit checkpoint can never be anchored against a replaced database",
76
+ "body": "close() fires a best-effort final checkpoint as the database shuts down. In a narrow close-then-reopen window that checkpoint's write could resume after a fresh database had opened and anchor the prior database's chain tip into it. checkpoint() now binds to the database it read the tip from and refuses to write (returns null, fail-closed) if that handle has since been closed or replaced — so a checkpoint is only ever written against the database it measured."
77
+ }
78
+ ]
79
+ }
80
+ ]
81
+ }
@@ -88,7 +88,7 @@ function main() {
88
88
  // The first line must match the canonical shape — `- vX.Y.Z (date) — summary`.
89
89
  var firstLine = section[0];
90
90
  var canonical = new RegExp(
91
- "^- v" + version.replace(/\./g, "\\.") +
91
+ "^- v" + version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") +
92
92
  " \\(\\d{4}-\\d{2}-\\d{2}\\) — \\S.+$"
93
93
  );
94
94
  if (!canonical.test(firstLine)) {
@@ -91,6 +91,25 @@ function testSqlSafeIdentifierValidation() {
91
91
  try { b.safeSql.validateIdentifier("a".repeat(70)); }
92
92
  catch (e) { threwLong = e.code === "sql/too-long"; }
93
93
  check("safeSql rejects over-long identifier", threwLong);
94
+
95
+ // assertNoRawStringLiteral — the shared raw-SQL string-literal scanner.
96
+ check("assertNoRawStringLiteral: clean parameterized SQL passes",
97
+ b.safeSql.assertNoRawStringLiteral("WHERE id = ? AND name = ?", "x") === undefined);
98
+ var threwLit = false;
99
+ try { b.safeSql.assertNoRawStringLiteral("WHERE name = 'x'", "where"); }
100
+ catch (e) { threwLit = e.code === "sql/raw-literal"; }
101
+ check("assertNoRawStringLiteral: refuses a raw '...' literal", threwLit);
102
+ // A double-quoted identifier (even one containing an apostrophe) and -- / slash-star
103
+ // comments are skipped, not refused.
104
+ var skipOk = true;
105
+ try { b.safeSql.assertNoRawStringLiteral("SELECT \"o'col\" FROM t -- 'note'", "x"); }
106
+ catch (_e) { skipOk = false; }
107
+ check("assertNoRawStringLiteral: skips quoted identifiers + comments", skipOk);
108
+ // A caller-supplied makeError is honored (the per-caller error contract).
109
+ var customMsg = null;
110
+ try { b.safeSql.assertNoRawStringLiteral("a = 'b'", "build", function (w) { return new Error(w + ":custom"); }); }
111
+ catch (e) { customMsg = e.message; }
112
+ check("assertNoRawStringLiteral: honors caller makeError", customMsg === "build:custom");
94
113
  }
95
114
 
96
115
  function testSqlSafeQuoteIdentifier() {
@@ -7127,7 +7146,10 @@ function _spawnFakeIdpServer(routes) {
7127
7146
  req.body = Buffer.concat(bodyChunks).toString("utf8");
7128
7147
  try { handler(req, res, u); }
7129
7148
  catch (e) {
7130
- res.writeHead(500); res.end(String(e));
7149
+ // Fixed-shape, bounded diagnostic — never surface the raw exception
7150
+ // (its message/stack) verbatim into the response body.
7151
+ var emsg = (e && typeof e.message === "string") ? e.message : "error";
7152
+ res.writeHead(500); res.end("fake-idp handler error: " + emsg.slice(0, 120));
7131
7153
  }
7132
7154
  });
7133
7155
  });
@@ -9218,6 +9240,55 @@ function testSafeSchemaPrototypePollutionDefense() {
9218
9240
  check("discUnion rejects __proto__ discriminator", threw && threw.code === "safe-schema/poisoned-discriminator");
9219
9241
  }
9220
9242
 
9243
+ function testPickPoisonedKeyPrimitive() {
9244
+ // The single prototype-pollution key guard every parser/decoder/middleware
9245
+ // routes through (lib/pick.js): predicate, throw-guard, add-only extension.
9246
+ var pick = require("../lib/pick");
9247
+
9248
+ // isPoisonedKey — the core JS pollution vectors, nothing else.
9249
+ check("isPoisonedKey __proto__", pick.isPoisonedKey("__proto__") === true);
9250
+ check("isPoisonedKey constructor", pick.isPoisonedKey("constructor") === true);
9251
+ check("isPoisonedKey prototype", pick.isPoisonedKey("prototype") === true);
9252
+ check("isPoisonedKey safe key false", pick.isPoisonedKey("displayName") === false);
9253
+ check("isPoisonedKey non-string false", pick.isPoisonedKey(42) === false &&
9254
+ pick.isPoisonedKey(null) === false &&
9255
+ pick.isPoisonedKey(undefined) === false);
9256
+
9257
+ // POISONED_KEYS — the frozen core snapshot.
9258
+ check("POISONED_KEYS frozen core", Object.isFrozen(pick.POISONED_KEYS) &&
9259
+ pick.POISONED_KEYS.join(",") === "__proto__,constructor,prototype");
9260
+
9261
+ // assertSafeKey — throw-guard form; the caller throws its own typed error.
9262
+ var blocked = null;
9263
+ try { pick.assertSafeKey("__proto__", function () { throw new TypeError("BLOCKED"); }); }
9264
+ catch (e) { blocked = e.message; }
9265
+ check("assertSafeKey fires onPoisoned for poisoned key", blocked === "BLOCKED");
9266
+ var fired = false;
9267
+ pick.assertSafeKey("safe", function () { fired = true; });
9268
+ check("assertSafeKey silent for safe key", fired === false);
9269
+ var badCb = null;
9270
+ try { pick.assertSafeKey("x", 7); } catch (e) { badCb = e.constructor.name; }
9271
+ check("assertSafeKey rejects non-function onPoisoned", badCb === "TypeError");
9272
+
9273
+ // registerPoisonedKeys — add-only defense-in-depth extension. Use a unique
9274
+ // sentinel so registering it can't perturb any other test's data keys
9275
+ // (registration is process-global by design).
9276
+ var SENTINEL = "__blamejs_test_poison_sentinel__";
9277
+ check("sentinel not poisoned by default", pick.isPoisonedKey(SENTINEL) === false);
9278
+ pick.registerPoisonedKeys([SENTINEL]);
9279
+ check("registerPoisonedKeys adds extra key", pick.isPoisonedKey(SENTINEL) === true);
9280
+ check("core vectors still poisoned after extend", pick.isPoisonedKey("__proto__") === true);
9281
+ var badReg = null;
9282
+ try { pick.registerPoisonedKeys("not-array"); } catch (e) { badReg = e.constructor.name; }
9283
+ check("registerPoisonedKeys rejects non-array", badReg === "TypeError");
9284
+
9285
+ // pick() itself strips poisoned keys (built via Object.fromEntries so the
9286
+ // key is a real own property, not a prototype set).
9287
+ var stripped = pick(Object.fromEntries([["a", 1], ["__proto__", { x: 1 }]]), ["a", "__proto__"]);
9288
+ check("pick() strips __proto__ even if allowlisted", stripped.a === 1 &&
9289
+ !Object.prototype.hasOwnProperty.call(stripped, "__proto__"));
9290
+ }
9291
+
9221
9292
  function testSafeSchemaErrorIssues() {
9222
9293
  var s = b.safeSchema;
9223
9294
  var threw = null;
@@ -14359,6 +14430,20 @@ function testBufferSafeToBuffer() {
14359
14430
  try { bs.toBuffer(123); }
14360
14431
  catch (e) { threwType = e.code === "buffer/wrong-input-type"; }
14361
14432
  check("toBuffer: number rejected", threwType);
14433
+
14434
+ // byteLengthOf — the byte-cap measurement primitive: UTF-8 bytes for a
14435
+ // string, .length for a Buffer/Uint8Array (whose .length already IS bytes).
14436
+ check("byteLengthOf is a function", typeof bs.byteLengthOf === "function");
14437
+ check("byteLengthOf: ASCII string", bs.byteLengthOf("abc") === 3);
14438
+ var cjk = String.fromCharCode(0x4e2d); // one 3-byte UTF-8 char, 1 UTF-16 unit
14439
+ check("byteLengthOf: multibyte string measures UTF-8 bytes, not UTF-16 units",
14440
+ bs.byteLengthOf(cjk) === 3 && cjk.length === 1);
14441
+ check("byteLengthOf: Buffer .length is bytes", bs.byteLengthOf(Buffer.from([1, 2, 3, 4])) === 4);
14442
+ check("byteLengthOf: Uint8Array length is bytes", bs.byteLengthOf(new Uint8Array(5)) === 5);
14443
+ check("byteLengthOf: encoding honored (hex)", bs.byteLengthOf("ff00", "hex") === 2);
14444
+ var threwBL = false;
14445
+ try { bs.byteLengthOf({}); } catch (e) { threwBL = e instanceof TypeError; }
14446
+ check("byteLengthOf: non-string/non-bytes throws TypeError", threwBL);
14362
14447
  }
14363
14448
 
14364
14449
  function testBufferSafeBoundedChunkCollector() {
@@ -14657,9 +14742,9 @@ async function testHttpClientConfigurePool() {
14657
14742
  }
14658
14743
  rejects("non-object input", function () { b.httpClient.configurePool("nope"); }, /must be an object/);
14659
14744
  rejects("unknown key", function () { b.httpClient.configurePool({ bogus: 1 }); }, /unknown option/);
14660
- rejects("negative maxSockets", function () { b.httpClient.configurePool({ maxSockets: -1 }); }, /positive integer/);
14661
- rejects("non-integer keepAliveMsecs",function () { b.httpClient.configurePool({ keepAliveMsecs: 1.5 }); }, /positive integer/);
14662
- rejects("Infinity maxFreeSockets", function () { b.httpClient.configurePool({ maxFreeSockets: Infinity }); }, /positive integer/);
14745
+ rejects("negative maxSockets", function () { b.httpClient.configurePool({ maxSockets: -1 }); }, /positive finite integer/);
14746
+ rejects("non-integer keepAliveMsecs",function () { b.httpClient.configurePool({ keepAliveMsecs: 1.5 }); }, /positive finite integer/);
14747
+ rejects("Infinity maxFreeSockets", function () { b.httpClient.configurePool({ maxFreeSockets: Infinity }); }, /positive finite integer/);
14663
14748
  rejects("non-boolean keepAlive", function () { b.httpClient.configurePool({ keepAlive: "yes" }); }, /must be a boolean/);
14664
14749
  rejects("bad scheduling", function () { b.httpClient.configurePool({ scheduling: "rr" }); }, /lifo.*fifo/);
14665
14750
  }
@@ -15016,8 +15101,13 @@ async function testHttpClientRedirectFollow() {
15016
15101
 
15017
15102
  async function testHttpClientRedirectMaxHops() {
15018
15103
  var http = require("http");
15104
+ // Each hop redirects to a fresh, fixed-shape path (a per-request counter),
15105
+ // never one derived from the incoming request URL — so the location never
15106
+ // repeats and the client keeps following until it hits maxRedirects.
15107
+ var hop = 0;
15019
15108
  var server = http.createServer(function (req, res) {
15020
- res.writeHead(302, { "Location": req.url + "x" });
15109
+ hop += 1;
15110
+ res.writeHead(302, { "Location": "/loop-" + hop });
15021
15111
  res.end();
15022
15112
  });
15023
15113
  var port = await listenOnRandomPort(server);
@@ -16399,14 +16489,15 @@ async function testWebSocketConnection() {
16399
16489
  // Parse HTTP headers (very crude — enough for tests).
16400
16490
  var headerLines = headerBuffer.substring(0, idx).split("\r\n");
16401
16491
  var requestLine = headerLines[0].split(" ");
16402
- var headers = {};
16492
+ var headerPairs = [];
16403
16493
  for (var i = 1; i < headerLines.length; i++) {
16404
16494
  var p = headerLines[i].indexOf(":");
16405
16495
  if (p === -1) continue;
16406
16496
  var k = headerLines[i].substring(0, p).trim().toLowerCase();
16407
16497
  var v = headerLines[i].substring(p + 1).trim();
16408
- headers[k] = v; // lgtm[js/remote-property-injection] test fixture; not a runtime path
16498
+ headerPairs.push([k, v]);
16409
16499
  }
16500
+ var headers = Object.fromEntries(headerPairs);
16410
16501
  var req = { method: requestLine[0], url: requestLine[1], headers: headers };
16411
16502
  var head = Buffer.from(headerBuffer.substring(idx + 4), "binary");
16412
16503
  var conn = ws.handleUpgrade(req, socket, head, { closeGraceMs: 50 });
@@ -16490,6 +16581,42 @@ async function testWebSocketConnection() {
16490
16581
  }
16491
16582
  }
16492
16583
 
16584
+ async function testWebSocketSendBadPayloadErrorOrder() {
16585
+ var EE = require("node:events").EventEmitter;
16586
+ var ws = b.websocket;
16587
+
16588
+ // Minimal socket stand-in: WebSocketConnection only needs the
16589
+ // EventEmitter surface plus write/destroy. send() throws synchronously
16590
+ // on a non-byte payload BEFORE any frame is written, so write/destroy
16591
+ // are never reached on this path.
16592
+ var socket = new EE();
16593
+ socket.write = function () { return true; };
16594
+ socket.destroy = function () {};
16595
+
16596
+ var conn = new ws.WebSocketConnection(socket, { pingIntervalMs: 60000, pongTimeoutMs: 120000 });
16597
+ try {
16598
+ var threw = null;
16599
+ try {
16600
+ // Plain object is not Buffer / Uint8Array / string — toBuffer refuses.
16601
+ conn.send({ not: "bytes" });
16602
+ } catch (e) { threw = e; }
16603
+
16604
+ check("ws send bad payload throws WebSocketError",
16605
+ threw !== null && threw.isWebSocketError === true);
16606
+ // The bug was a swapped (message, code) construction: .code became the
16607
+ // human message and .message became the code string. Assert the correct
16608
+ // order — code is the dotted ws/* identifier, message is the prose.
16609
+ check("ws send bad payload: .code is the ws/* code",
16610
+ threw && threw.code === "ws/invalid-payload");
16611
+ check("ws send bad payload: .message is the prose",
16612
+ threw && threw.message === "send() requires Buffer, Uint8Array, or string");
16613
+ } finally {
16614
+ // Stop the connection's ping timer (it is unref'd, but tidy teardown
16615
+ // keeps the suite's open-handle count clean).
16616
+ socket.emit("close");
16617
+ }
16618
+ }
16619
+
16493
16620
  // Test helpers — local to the websocket suite. nodeCrypto for the
16494
16621
  // random key, _readUntil/_readNBytes/_readSome for incremental client
16495
16622
  // socket reads.
@@ -18399,6 +18526,7 @@ async function run() {
18399
18526
  testSafeSchemaRefineTransform();
18400
18527
  testSafeSchemaLazyAndPreprocess();
18401
18528
  testSafeSchemaPrototypePollutionDefense();
18529
+ testPickPoisonedKeyPrimitive();
18402
18530
  testSafeSchemaErrorIssues();
18403
18531
  testSafeSchemaImmutability();
18404
18532
  // events — moved to test/layer-0-primitives/events.test.js
@@ -18709,6 +18837,7 @@ async function run() {
18709
18837
  testWebSocketHandshake();
18710
18838
  testWebSocketFrames();
18711
18839
  await testWebSocketConnection();
18840
+ await testWebSocketSendBadPayloadErrorOrder();
18712
18841
  testRouterWsValidation();
18713
18842
  await testRouterSetsRoutePattern();
18714
18843
 
@@ -484,10 +484,16 @@ async function testFrameworkSchemaEnsure() {
484
484
  externalDbBackend: "ops",
485
485
  dialect: "sqlite",
486
486
  });
487
- check("ensureSchema returns 19 tables (added _blamejs_cache_tags in v0.6.21)",
488
- result.tables.length === 19);
487
+ check("ensureSchema returns 20 tables (_blamejs_session_valid_from is the 20th)",
488
+ result.tables.length === 20);
489
489
  check("ensureSchema includes _blamejs_cache_tags",
490
490
  result.tables.indexOf("_blamejs_cache_tags") !== -1);
491
+ check("ensureSchema includes _blamejs_session_valid_from",
492
+ result.tables.indexOf("_blamejs_session_valid_from") !== -1);
493
+ check("ensureSchema includes _blamejs_break_glass_policies",
494
+ result.tables.indexOf("_blamejs_break_glass_policies") !== -1);
495
+ check("ensureSchema includes _blamejs_break_glass_grants",
496
+ result.tables.indexOf("_blamejs_break_glass_grants") !== -1);
491
497
  check("ensureSchema includes _blamejs_audit_log",
492
498
  result.tables.indexOf("_blamejs_audit_log") !== -1);
493
499
  check("ensureSchema includes _blamejs_consent_log",
@@ -538,7 +544,7 @@ async function testFrameworkSchemaEnsure() {
538
544
  externalDbBackend: "ops",
539
545
  dialect: "sqlite",
540
546
  });
541
- check("ensureSchema is idempotent", second.tables.length === 19);
547
+ check("ensureSchema is idempotent", second.tables.length === 20);
542
548
 
543
549
  // Indexes exist
544
550
  var idxRow = await b.externalDb.query(