@blamejs/blamejs-shop 0.4.30 → 0.4.32

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 (338) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/checkout.js +8 -0
  4. package/lib/order.js +71 -11
  5. package/lib/vendor/MANIFEST.json +392 -278
  6. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  7. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  8. package/lib/vendor/blamejs/.gitignore +6 -0
  9. package/lib/vendor/blamejs/CHANGELOG.md +26 -0
  10. package/lib/vendor/blamejs/MIGRATING.md +43 -0
  11. package/lib/vendor/blamejs/README.md +8 -6
  12. package/lib/vendor/blamejs/SECURITY.md +19 -3
  13. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  14. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  15. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  16. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  17. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  18. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  19. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  20. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  21. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  22. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  23. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  24. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  25. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  26. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  27. package/lib/vendor/blamejs/index.js +4 -0
  28. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  29. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  30. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  31. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  32. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  33. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  34. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  35. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  36. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  37. package/lib/vendor/blamejs/lib/audit.js +259 -123
  38. package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
  39. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  40. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  41. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
  42. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  43. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  44. package/lib/vendor/blamejs/lib/cache.js +174 -105
  45. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  46. package/lib/vendor/blamejs/lib/cli.js +19 -14
  47. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  48. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  49. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  50. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  51. package/lib/vendor/blamejs/lib/compliance.js +206 -4
  52. package/lib/vendor/blamejs/lib/consent.js +82 -29
  53. package/lib/vendor/blamejs/lib/constants.js +27 -11
  54. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  55. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  56. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  57. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  58. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  59. package/lib/vendor/blamejs/lib/db.js +249 -99
  60. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  61. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  62. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  63. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  64. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  65. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  66. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  67. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  68. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  69. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  70. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  71. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  72. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  73. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  74. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  75. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  76. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  77. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  78. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  79. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  80. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  81. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  82. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  83. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  84. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  85. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  86. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  87. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  88. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  89. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  90. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  91. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  92. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  93. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  94. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  95. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  96. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  97. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  98. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  99. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  100. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  101. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  102. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  103. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  104. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  105. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  106. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  107. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  108. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  109. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  110. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  111. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  112. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  113. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  114. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  115. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  116. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  117. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  118. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  119. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  120. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  121. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  122. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  123. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  124. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  125. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  126. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  127. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  128. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  129. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  131. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  132. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  133. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  134. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  135. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  136. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  137. package/lib/vendor/blamejs/lib/mail.js +8 -4
  138. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  139. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  140. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  141. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  142. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  143. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  144. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  145. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  146. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  147. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  148. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  149. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  150. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  151. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  152. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  153. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  154. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  155. package/lib/vendor/blamejs/lib/observability.js +124 -0
  156. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  157. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  158. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  159. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  160. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  161. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  162. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  163. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  164. package/lib/vendor/blamejs/lib/queue.js +7 -0
  165. package/lib/vendor/blamejs/lib/redact.js +68 -11
  166. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  167. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  168. package/lib/vendor/blamejs/lib/retention.js +101 -40
  169. package/lib/vendor/blamejs/lib/router.js +212 -5
  170. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  171. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  172. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  173. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  174. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  175. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  176. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  177. package/lib/vendor/blamejs/lib/scheduler.js +35 -12
  178. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  179. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  180. package/lib/vendor/blamejs/lib/session.js +175 -77
  181. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  182. package/lib/vendor/blamejs/lib/sse.js +26 -0
  183. package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
  184. package/lib/vendor/blamejs/lib/static.js +177 -34
  185. package/lib/vendor/blamejs/lib/subject.js +96 -49
  186. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  187. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  188. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  189. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  190. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  191. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  192. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  193. package/lib/vendor/blamejs/package.json +2 -2
  194. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  195. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  200. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  201. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  202. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  203. package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
  204. package/lib/vendor/blamejs/scripts/release.js +398 -38
  205. package/lib/vendor/blamejs/test/00-primitives.js +117 -0
  206. package/lib/vendor/blamejs/test/10-state.js +140 -14
  207. package/lib/vendor/blamejs/test/20-db.js +65 -2
  208. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  209. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  210. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  211. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  212. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  213. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  214. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  215. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  216. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  217. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  220. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  221. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  222. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  223. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  224. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  225. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  226. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  227. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  228. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  229. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  230. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  231. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  232. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  233. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  234. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  235. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  236. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  237. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  238. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  240. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  248. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  250. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
  251. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  253. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  256. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  259. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  260. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  261. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  271. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  272. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  273. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  274. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  275. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  277. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  278. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  279. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  280. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  282. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  283. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  284. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  285. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  293. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  304. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  308. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  310. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  312. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  313. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  314. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  315. package/lib/vendor/blamejs/test/smoke.js +79 -21
  316. package/package.json +1 -1
  317. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  318. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  319. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  320. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  321. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  322. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  323. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  324. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  325. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  326. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  327. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  328. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  329. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  330. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  331. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  332. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  333. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  334. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  335. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  336. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  337. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  338. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -77,6 +77,7 @@
77
77
 
78
78
  var C = require("./constants");
79
79
  var { defineClass } = require("./framework-error");
80
+ var gateContract = require("./gate-contract");
80
81
 
81
82
  var SafeIcapError = defineClass("SafeIcapError", { alwaysPermanent: true });
82
83
 
@@ -131,11 +132,15 @@ var PROFILES = Object.freeze({
131
132
  },
132
133
  });
133
134
 
134
- var COMPLIANCE_POSTURES = Object.freeze({
135
- hipaa: "strict",
136
- "pci-dss": "strict",
137
- gdpr: "strict",
138
- soc2: "strict",
135
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
136
+
137
+ var _resolveProfile = gateContract.makeProfileResolver({
138
+ profiles: PROFILES,
139
+ postures: COMPLIANCE_POSTURES,
140
+ defaults: DEFAULT_PROFILE,
141
+ errorClass: SafeIcapError,
142
+ codePrefix: "safe-icap",
143
+ byObject: true,
139
144
  });
140
145
 
141
146
  /**
@@ -257,22 +262,6 @@ function parse(buf, opts) {
257
262
  };
258
263
  }
259
264
 
260
- /**
261
- * @primitive b.safeIcap.compliancePosture
262
- * @signature b.safeIcap.compliancePosture(posture)
263
- * @since 0.9.81
264
- * @status stable
265
- *
266
- * Return the effective profile name for a compliance posture, or
267
- * `null` for unknown posture names.
268
- *
269
- * @example
270
- * b.safeIcap.compliancePosture("hipaa"); // → "strict"
271
- */
272
- function compliancePosture(posture) {
273
- return COMPLIANCE_POSTURES[posture] || null;
274
- }
275
-
276
265
  // ---- internals ----
277
266
 
278
267
  function _findHeaderEnd(buf, maxHeaderBytes) {
@@ -479,25 +468,20 @@ function _detectThreat(statusCode, headers) {
479
468
  return { found: found, name: name };
480
469
  }
481
470
 
482
- function _resolveProfile(opts) {
483
- if (opts.posture && COMPLIANCE_POSTURES[opts.posture]) {
484
- return PROFILES[COMPLIANCE_POSTURES[opts.posture]];
485
- }
486
- var p = opts.profile || DEFAULT_PROFILE;
487
- if (!PROFILES[p]) {
488
- throw new SafeIcapError("safe-icap/bad-profile",
489
- "safeIcap: unknown profile '" + p + "' (valid: strict / balanced / permissive)");
490
- }
491
- return PROFILES[p];
492
- }
493
-
494
- module.exports = {
495
- parse: parse,
496
- compliancePosture: compliancePosture,
497
- PROFILES: PROFILES,
498
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
499
- ALLOWED_STATUS: ALLOWED_STATUS,
500
- SafeIcapError: SafeIcapError,
501
- NAME: "icap",
502
- KIND: "icap-response",
503
- };
471
+ // compliancePosture is assembled by gateContract.defineParser below; its
472
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
473
+ // block in gate-contract.js, instantiated for this guard by the page
474
+ // generator.
475
+ module.exports = gateContract.defineParser({
476
+ name: "icap",
477
+ entry: parse,
478
+ entryName: "parse",
479
+ errorClass: SafeIcapError,
480
+ profiles: PROFILES,
481
+ postures: COMPLIANCE_POSTURES,
482
+ extra: {
483
+ ALLOWED_STATUS: ALLOWED_STATUS,
484
+ NAME: "icap",
485
+ KIND: "icap-response",
486
+ },
487
+ });
@@ -49,6 +49,7 @@
49
49
  */
50
50
 
51
51
  var { defineClass } = require("./framework-error");
52
+ var gateContract = require("./gate-contract");
52
53
 
53
54
  var SafeSieveError = defineClass("SafeSieveError", { alwaysPermanent: true });
54
55
 
@@ -82,12 +83,7 @@ var PROFILES = Object.freeze({
82
83
  }),
83
84
  });
84
85
 
85
- var COMPLIANCE_POSTURES = Object.freeze({
86
- hipaa: "strict",
87
- "pci-dss": "strict",
88
- gdpr: "strict",
89
- soc2: "strict",
90
- });
86
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
91
87
 
92
88
  // RFC 5228 §1.2 capability identifiers. Each entry lists whether the
93
89
  // framework's v0.9.55 interpreter implements the capability. Unknown
@@ -648,37 +644,22 @@ function validate(script, opts) {
648
644
  }
649
645
  }
650
646
 
651
- /**
652
- * @primitive b.safeSieve.compliancePosture
653
- * @signature b.safeSieve.compliancePosture(name)
654
- * @since 0.9.55
655
- * @status stable
656
- * @related b.safeSieve.parse, b.safeSieve.validate
657
- *
658
- * Look up the recommended profile name for a compliance posture
659
- * (`hipaa` / `pci-dss` / `gdpr` / `soc2`). Returns `"strict"` for any
660
- * known posture, `null` for unknown names. Operator-facing primitives
661
- * that thread `compliancePosture` opt through to safeSieve compose
662
- * this for the explicit-cast pattern when they need the name string
663
- * (rather than relying on `_resolveOpts` to do the lookup).
664
- *
665
- * @example
666
- * b.safeSieve.compliancePosture("hipaa"); // → "strict"
667
- * b.safeSieve.compliancePosture("loose"); // → null
668
- */
669
- function compliancePosture(name) {
670
- return COMPLIANCE_POSTURES[name] || null;
671
- }
672
-
673
- module.exports = {
674
- parse: parse,
675
- validate: validate,
676
- compliancePosture: compliancePosture,
677
- KNOWN_CAPABILITIES: KNOWN_CAPABILITIES,
678
- PROFILES: PROFILES,
679
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
680
- SafeSieveError: SafeSieveError,
681
- // Internal exports for the interpreter at lib/mail-sieve.js.
682
- _tokenize: _tokenize,
683
- _resolveCaps: _resolveCaps,
684
- };
647
+ // compliancePosture is assembled by gateContract.defineParser below; its
648
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
649
+ // block in gate-contract.js, instantiated for this guard by the page
650
+ // generator.
651
+ module.exports = gateContract.defineParser({
652
+ name: "sieve",
653
+ entry: parse,
654
+ entryName: "parse",
655
+ errorClass: SafeSieveError,
656
+ profiles: PROFILES,
657
+ postures: COMPLIANCE_POSTURES,
658
+ extra: {
659
+ validate: validate,
660
+ KNOWN_CAPABILITIES: KNOWN_CAPABILITIES,
661
+ // Internal exports for the interpreter at lib/mail-sieve.js.
662
+ _tokenize: _tokenize,
663
+ _resolveCaps: _resolveCaps,
664
+ },
665
+ });
@@ -175,7 +175,7 @@ function validateIdentifier(name, opts) {
175
175
 
176
176
  /**
177
177
  * @primitive b.safeSql.quoteIdentifier
178
- * @signature b.safeSql.quoteIdentifier(name, dialect?)
178
+ * @signature b.safeSql.quoteIdentifier(name, dialect?, opts?)
179
179
  * @since 0.1.0
180
180
  * @status stable
181
181
  * @related b.safeSql.validateIdentifier, b.safeSql.quoteQualified
@@ -185,6 +185,17 @@ function validateIdentifier(name, opts) {
185
185
  * MySQL. Default dialect is `"sqlite"`. Throws `SafeSqlError` if the
186
186
  * identifier fails `validateIdentifier`.
187
187
  *
188
+ * `opts` is forwarded to `validateIdentifier` — pass
189
+ * `{ allowReserved: true }` to quote a name that collides with a SQL
190
+ * keyword (a column literally named `from` / `select`). Quoting is
191
+ * exactly what makes a reserved word safe in identifier position, so the
192
+ * query builder (`b.sql`) routes every identifier through here with
193
+ * `allowReserved` on; the default still rejects reserved words so a bare
194
+ * caller catches the likely typo.
195
+ *
196
+ * @opts
197
+ * allowReserved: boolean, // default: false — permit SQL-keyword names (safe once quoted)
198
+ *
188
199
  * @example
189
200
  * var b = require("blamejs");
190
201
  * b.safeSql.quoteIdentifier("users");
@@ -193,11 +204,14 @@ function validateIdentifier(name, opts) {
193
204
  * b.safeSql.quoteIdentifier("Order", "postgres");
194
205
  * // → '"Order"'
195
206
  *
207
+ * b.safeSql.quoteIdentifier("from", "postgres", { allowReserved: true });
208
+ * // → '"from"'
209
+ *
196
210
  * b.safeSql.quoteIdentifier("users", "mysql");
197
211
  * // → "`users`"
198
212
  */
199
- function quoteIdentifier(name, dialect) {
200
- validateIdentifier(name);
213
+ function quoteIdentifier(name, dialect, opts) {
214
+ validateIdentifier(name, opts);
201
215
  dialect = (dialect || "sqlite").toLowerCase();
202
216
  if (dialect === "mysql") return "`" + name + "`";
203
217
  // sqlite + postgres both use double-quote per SQL standard
@@ -258,6 +272,53 @@ function quoteQualified(parts, dialect) {
258
272
  return quoted.join(".");
259
273
  }
260
274
 
275
+ /**
276
+ * @primitive b.safeSql.quoteList
277
+ * @signature b.safeSql.quoteList(names, dialect?, opts?)
278
+ * @since 0.15.0
279
+ * @status stable
280
+ * @related b.safeSql.quoteIdentifier, b.safeSql.quoteQualified, b.sql
281
+ *
282
+ * Quote a list of identifiers into a comma-joined fragment — each name
283
+ * validated + quoted via `quoteIdentifier`. The "many" companion to
284
+ * `quoteIdentifier` (one) and `quoteQualified` (a dotted name): use it for
285
+ * SELECT projections and INSERT column lists so the recurring
286
+ * `cols.map(quoteIdentifier).join(", ")` shape is composed, not hand-rolled.
287
+ *
288
+ * There is deliberately NO value/string-literal quoter in this module:
289
+ * values flow as bound placeholders (`?` / `$N`), never interpolated, which
290
+ * is what makes the injection class structurally impossible. Quoting a
291
+ * literal would reopen it — use the query builder's parameter binding.
292
+ *
293
+ * `opts` is forwarded to each `quoteIdentifier` (e.g.
294
+ * `{ allowReserved: true }` for column lists that may contain SQL-keyword
295
+ * names, as `b.sql` does).
296
+ *
297
+ * Throws `SafeSqlError` (`sql/empty`) on an empty array and (per
298
+ * `quoteIdentifier`) on any invalid identifier.
299
+ *
300
+ * @opts
301
+ * allowReserved: boolean, // default: false — forwarded to quoteIdentifier
302
+ *
303
+ * @example
304
+ * var b = require("blamejs");
305
+ * b.safeSql.quoteList(["id", "createdAt"], "postgres");
306
+ * // → '"id", "createdAt"'
307
+ *
308
+ * b.safeSql.quoteList(["queueName", "status"], "mysql");
309
+ * // → "`queueName`, `status`"
310
+ */
311
+ function quoteList(names, dialect, opts) {
312
+ if (!Array.isArray(names) || names.length === 0) {
313
+ throw new SafeSqlError("quoteList requires a non-empty array of identifiers", "sql/empty");
314
+ }
315
+ var out = [];
316
+ for (var i = 0; i < names.length; i++) {
317
+ out.push(quoteIdentifier(names[i], dialect, opts));
318
+ }
319
+ return out.join(", ");
320
+ }
321
+
261
322
  /**
262
323
  * @primitive b.safeSql.assertOneOf
263
324
  * @signature b.safeSql.assertOneOf(name, allowlist)
@@ -310,6 +371,64 @@ function assertOneOf(name, allowlist) {
310
371
  return name;
311
372
  }
312
373
 
374
+ /**
375
+ * @primitive b.safeSql.countPlaceholders
376
+ * @signature b.safeSql.countPlaceholders(sql)
377
+ * @since 0.14.29
378
+ * @status stable
379
+ * @related b.safeSql.quoteIdentifier, b.safeSql.validateIdentifier
380
+ *
381
+ * Count the bound `?` placeholders in a SQL string, skipping any `?`
382
+ * that appears inside a string literal (`'...'` / `"..."`, doubled-quote
383
+ * escape aware) or inside a line or block comment. The canonical quote-
384
+ * and comment-aware scanner the query builder uses to check placeholder /
385
+ * param parity and the residency write-gate uses to align bound values;
386
+ * both compose this so the skip rules live in one place.
387
+ *
388
+ * @example
389
+ * var b = require("blamejs");
390
+ * b.safeSql.countPlaceholders("a = ? AND b = ?");
391
+ * // → 2
392
+ *
393
+ * b.safeSql.countPlaceholders("note = 'is ? literal' AND id = ?");
394
+ * // → 1
395
+ */
396
+ function countPlaceholders(sql) {
397
+ var count = 0;
398
+ var i = 0;
399
+ var len = sql.length;
400
+ while (i < len) {
401
+ var ch = sql.charAt(i);
402
+ var next = i + 1 < len ? sql.charAt(i + 1) : "";
403
+ if (ch === "'" || ch === '"') {
404
+ var quote = ch;
405
+ i += 1;
406
+ while (i < len) {
407
+ if (sql.charAt(i) === quote) {
408
+ // SQL doubles the quote char to escape it within a literal.
409
+ if (sql.charAt(i + 1) === quote) { i += 2; continue; }
410
+ i += 1; break;
411
+ }
412
+ i += 1;
413
+ }
414
+ continue;
415
+ }
416
+ if (ch === "-" && next === "-") {
417
+ while (i < len && sql.charAt(i) !== "\n") i += 1;
418
+ continue;
419
+ }
420
+ if (ch === "/" && next === "*") {
421
+ i += 2;
422
+ while (i < len && !(sql.charAt(i) === "*" && sql.charAt(i + 1) === "/")) i += 1;
423
+ i += 2;
424
+ continue;
425
+ }
426
+ if (ch === "?") count += 1;
427
+ i += 1;
428
+ }
429
+ return count;
430
+ }
431
+
313
432
  /**
314
433
  * @primitive b.safeSql.DEFAULT_IDENTIFIER_RE
315
434
  * @signature b.safeSql.DEFAULT_IDENTIFIER_RE
@@ -351,11 +470,101 @@ function assertOneOf(name, allowlist) {
351
470
  * // → 63
352
471
  */
353
472
 
473
+ /**
474
+ * @primitive b.safeSql.assertSingleStatement
475
+ * @signature b.safeSql.assertSingleStatement(sql, opts?)
476
+ * @since 0.15.4
477
+ * @status stable
478
+ * @related b.safeSql.quoteIdentifier, b.safeSql.countPlaceholders, b.sql
479
+ *
480
+ * The one quote/comment-aware single-statement gate for any FINISHED SQL
481
+ * string that reaches a driver. Refuses a NUL, a lone surrogate, a
482
+ * top-level ';' (stacked statement), an unterminated quote, and unbalanced
483
+ * parentheses - while CORRECTLY allowing those characters inside a balanced
484
+ * quoted label (e.g. a MySQL ENUM('a;b')). Hand-rolled DDL (schema
485
+ * reconcile, the DSR store, migrations) and the b.sql builder's own output
486
+ * gates route through this single scan so the injection backstop cannot
487
+ * drift between the structured builder and the raw-DDL paths. Returns the
488
+ * input string so a caller can wrap inline:
489
+ * runSql(db, safeSql.assertSingleStatement(ddl, { label: "schema" }));
490
+ *
491
+ * @opts
492
+ * label: string, // message prefix (default: "sql")
493
+ * makeError: function, // (message, codeSuffix) => Error (default: SafeSqlError "sql/<suffix>")
494
+ *
495
+ * @example
496
+ * var ddl = b.safeSql.assertSingleStatement("CREATE TABLE t (id INTEGER)", { label: "schema" });
497
+ * // returns the input string; throws sql/stacked-statement on a stacked DDL
498
+ */
499
+ function assertSingleStatement(sql, opts) {
500
+ opts = opts || {};
501
+ var label = typeof opts.label === "string" ? opts.label : "sql";
502
+ var mkErr = typeof opts.makeError === "function"
503
+ ? opts.makeError
504
+ : function (msg, suffix) { return new SafeSqlError(msg, "sql/" + suffix); };
505
+ // Backtick written via its code point so no NUL byte can reach this source.
506
+ var BACKTICK = String.fromCharCode(96);
507
+ if (typeof sql !== "string" || sql.length === 0) {
508
+ throw mkErr(label + ": SQL must be a non-empty string", "empty-sql");
509
+ }
510
+ if (sql.indexOf(String.fromCharCode(0)) !== -1) {
511
+ throw mkErr(label + ": SQL contains a NUL byte - rejected", "null-byte-sql");
512
+ }
513
+ if (typeof sql.isWellFormed === "function" && !sql.isWellFormed()) {
514
+ throw mkErr(label + ": SQL contains invalid Unicode (lone surrogates) - rejected",
515
+ "invalid-encoding-sql");
516
+ }
517
+ var i = 0;
518
+ var len = sql.length;
519
+ var depth = 0;
520
+ while (i < len) {
521
+ var ch = sql.charAt(i);
522
+ var next = i + 1 < len ? sql.charAt(i + 1) : "";
523
+ if (ch === "'" || ch === '"' || ch === BACKTICK) {
524
+ var qch = ch;
525
+ var closed = false;
526
+ i += 1;
527
+ while (i < len) {
528
+ if (sql.charAt(i) === qch) {
529
+ if (sql.charAt(i + 1) === qch) { i += 2; continue; } // doubled quote = escaped literal
530
+ i += 1; closed = true; break;
531
+ }
532
+ i += 1;
533
+ }
534
+ if (!closed) {
535
+ throw mkErr(label + ": unterminated quote in SQL (quote-jump / breakout risk)",
536
+ "unterminated-quote");
537
+ }
538
+ continue;
539
+ }
540
+ if (ch === "-" && next === "-") { while (i < len && sql.charAt(i) !== "\n") i += 1; continue; }
541
+ if (ch === "/" && next === "*") {
542
+ i += 2;
543
+ while (i < len && !(sql.charAt(i) === "*" && sql.charAt(i + 1) === "/")) i += 1;
544
+ i += 2;
545
+ continue;
546
+ }
547
+ if (ch === "(") { depth += 1; }
548
+ else if (ch === ")") { depth -= 1; }
549
+ else if (ch === ";") {
550
+ throw mkErr(label + ": emitted a top-level ';' - exactly one statement", "stacked-statement");
551
+ }
552
+ i += 1;
553
+ }
554
+ if (depth !== 0) {
555
+ throw mkErr(label + ": unbalanced parentheses in SQL", "unbalanced");
556
+ }
557
+ return sql;
558
+ }
559
+
354
560
  module.exports = {
355
561
  validateIdentifier: validateIdentifier,
562
+ assertSingleStatement: assertSingleStatement,
356
563
  quoteIdentifier: quoteIdentifier,
357
564
  quoteQualified: quoteQualified,
565
+ quoteList: quoteList,
358
566
  assertOneOf: assertOneOf,
567
+ countPlaceholders: countPlaceholders,
359
568
  SafeSqlError: SafeSqlError,
360
569
  // Exposed so consumers can compose their own validators
361
570
  DEFAULT_IDENTIFIER_RE: DEFAULT_IDENTIFIER_RE,
@@ -53,6 +53,11 @@ var nodeUrl = require("node:url");
53
53
  var { URL } = require("node:url");
54
54
 
55
55
  var audit = lazyRequire(function () { return require("./audit"); });
56
+ // ssrf-guard requires safe-url at top of file (the SSRF gate parses through
57
+ // the framework's defensive URL parser); lazyRequire breaks that cycle so the
58
+ // canonicalizer can reuse ssrf-guard's IP-literal canonicalization without an
59
+ // at-load circular require.
60
+ var ssrfGuard = lazyRequire(function () { return require("./ssrf-guard"); });
56
61
 
57
62
  /**
58
63
  * @primitive b.safeUrl.ALLOW_HTTP_TLS
@@ -168,9 +173,9 @@ var ALLOW_ANY = Object.freeze(["http:", "https:", "ws:", "wss:"]);
168
173
  * Extends `FrameworkError`. Carries a stable `.code`:
169
174
  * `safe-url/missing` / `safe-url/too-long` / `safe-url/malformed` /
170
175
  * `safe-url/protocol-disallowed` / `safe-url/userinfo-disallowed` /
171
- * `safe-url/idn-homograph` / `safe-url/bad-opt`. HTTP middleware
172
- * inspects `.code` to translate the throw into a 400 without
173
- * leaking parser internals.
176
+ * `safe-url/idn-homograph` / `safe-url/uncanonicalizable` /
177
+ * `safe-url/bad-opt`. HTTP middleware inspects `.code` to translate
178
+ * the throw into a 400 without leaking parser internals.
174
179
  *
175
180
  * @example
176
181
  * var b = require("blamejs");
@@ -205,6 +210,38 @@ function _makeError(errorClass, code, message) {
205
210
  // payloads) override via opts.maxUrlLength.
206
211
  var DEFAULT_MAX_URL_LENGTH = C.BYTES.kib(8);
207
212
 
213
+ // RFC 3986 §6.2.2 percent-encoding normalization applied CONSERVATIVELY to a
214
+ // path segment string: uppercase the two hex digits of every valid escape
215
+ // (§6.2.2.2) and decode an escape of an unreserved character to its literal
216
+ // (§6.2.2.3). A malformed escape (`%`, `%g`, `%4`) is passed through verbatim
217
+ // so no information is invented. Query and fragment are NOT touched —
218
+ // reordering / re-decoding there can change application semantics (a `%26`
219
+ // inside a value is NOT the same as a literal `&`).
220
+ function _normalizePctPath(path) {
221
+ if (typeof path !== "string" || path.indexOf("%") === -1) return path;
222
+ var out = "";
223
+ for (var i = 0; i < path.length; i += 1) {
224
+ var ch = path.charAt(i);
225
+ if (ch === "%") {
226
+ // The escape's two hex digits — sliced to a fixed two-char window so
227
+ // the regex test runs on a length-bounded string (no ReDoS surface).
228
+ var pair = path.slice(i + 1, i + 3);
229
+ if (pair.length === 2 && codepointClass.HEX_PAIR_RE.test(pair)) {
230
+ var cc = parseInt(pair, 16);
231
+ if (codepointClass.isUnreserved(cc)) {
232
+ out += String.fromCharCode(cc);
233
+ } else {
234
+ out += "%" + pair.toUpperCase();
235
+ }
236
+ i += 2;
237
+ continue;
238
+ }
239
+ }
240
+ out += ch;
241
+ }
242
+ return out;
243
+ }
244
+
208
245
  /**
209
246
  * @primitive b.safeUrl.parse
210
247
  * @signature b.safeUrl.parse(url, opts?)
@@ -416,9 +453,139 @@ function format(url) {
416
453
  }
417
454
  }
418
455
 
456
+ /**
457
+ * @primitive b.safeUrl.canonicalize
458
+ * @signature b.safeUrl.canonicalize(input, opts?)
459
+ * @since 0.15.6
460
+ * @status stable
461
+ * @related b.safeUrl.parse, b.ssrfGuard.canonicalizeHost, b.ssrfGuard.checkUrl
462
+ *
463
+ * Return the single canonical, comparable form of a URL so two
464
+ * spellings of the same destination compare equal as strings. The use
465
+ * cases are host allowlists, dedup / cache keys, and SSRF pre-checks —
466
+ * exactly the places an attacker reaches for an obfuscated host
467
+ * (`http://0177.0.0.1/`, `http://2130706433/`, `http://[::ffff:7f00:1]/`,
468
+ * an IDN homograph, a trailing-dot or default-port variation) to slip
469
+ * past a naive `===` allowlist. Routing every comparison through one
470
+ * audited canonicalizer closes that class instead of leaving each
471
+ * caller to re-derive normalization (which is how the bypasses happen).
472
+ *
473
+ * The canonical form is built from `parse`'s defensive gates plus the
474
+ * security-relevant normalization set:
475
+ *
476
+ * - Scheme and host lowercased (the WHATWG URL parser does this).
477
+ * - Host IDN labels emitted as their punycode `xn--` A-label; a
478
+ * mixed-script / confusable host label THROWS exactly as
479
+ * `parse` does (a homograph is never silently passed) unless the
480
+ * caller opts in via `allowMixedScript` / `allowedScripts`.
481
+ * - A trailing dot on the host is removed (`example.com.` →
482
+ * `example.com`) — DNS-equivalent but breaks string comparison.
483
+ * - An IP-literal host in ANY notation collapses to one canonical
484
+ * string via `b.ssrfGuard.canonicalizeHost` (the SAME byte parser
485
+ * the SSRF classifier matches on): IPv4 decimal / octal / hex /
486
+ * shorthand → dotted-quad; IPv6 (incl. IPv4-mapped + any
487
+ * zero-compression) → RFC 5952 lower-hex, bracketed.
488
+ * - The default port for the scheme is stripped (`:80` http/ws,
489
+ * `:443` https/wss — the parser does this).
490
+ * - Path `.` / `..` segments resolved (WHATWG), then RFC 3986 §6.2.2
491
+ * percent-normalization applied to the path: hex digits uppercased,
492
+ * escapes of unreserved characters decoded. Query and fragment are
493
+ * left BYTE-FOR-BYTE as parsed — reordering or re-decoding there can
494
+ * change application semantics.
495
+ *
496
+ * Throws `SafeUrlError` (or `opts.errorClass`): the `parse` codes for a
497
+ * missing / too-long / malformed / disallowed-scheme / userinfo /
498
+ * homograph input, plus `safe-url/uncanonicalizable` when a parsed URL
499
+ * cannot be reduced to a safe canonical form. This is a config /
500
+ * entry-point validator — it THROWS on bad input, it does NOT return a
501
+ * best-effort string.
502
+ *
503
+ * @opts
504
+ * allowedSchemes: string[], // default ALLOW_ANY (http/https/ws/wss); canonicalize is a compare tool, not a fetch gate
505
+ * allowUserinfo: boolean, // default false; opt-in to keep user:pass@ (still discouraged)
506
+ * allowMixedScript: boolean, // default false; opt-in to mixed-script host labels
507
+ * allowedScripts: string[], // narrow mixed-script allowlist (e.g. ["latin","cyrillic"])
508
+ * maxUrlLength: number, // default 8192 (RFC 7230 §3.1.1)
509
+ * errorClass: Function, // throw this instead of SafeUrlError
510
+ *
511
+ * @example
512
+ * var b = require("blamejs");
513
+ *
514
+ * // Every obfuscated loopback spelling collapses to one string.
515
+ * b.safeUrl.canonicalize("http://0177.0.0.1/"); // → "http://127.0.0.1/"
516
+ * b.safeUrl.canonicalize("http://2130706433/"); // → "http://127.0.0.1/"
517
+ * b.safeUrl.canonicalize("http://127.1/"); // → "http://127.0.0.1/"
518
+ *
519
+ * // Case, default port, trailing dot, and `..` all normalize.
520
+ * b.safeUrl.canonicalize("https://Example.COM:443/a/../b");
521
+ * // → "https://example.com/b"
522
+ *
523
+ * // A disallowed scheme throws SafeUrlError.
524
+ * try { b.safeUrl.canonicalize("ftp://example.com/"); }
525
+ * catch (e) { e.code; } // → "safe-url/protocol-disallowed"
526
+ */
527
+ function canonicalize(input, opts) {
528
+ opts = opts || {};
529
+ var allowedSchemes = Array.isArray(opts.allowedSchemes) && opts.allowedSchemes.length > 0
530
+ ? opts.allowedSchemes
531
+ : ALLOW_ANY;
532
+
533
+ // Route through parse — it owns the length cap, the scheme allowlist,
534
+ // userinfo refusal, and the IDN-homograph defense. A parse throw (with
535
+ // its stable .code) propagates unchanged so callers branch on the same
536
+ // codes parse documents.
537
+ var parsed = parse(input, {
538
+ allowedProtocols: allowedSchemes,
539
+ maxUrlLength: opts.maxUrlLength,
540
+ allowUserinfo: opts.allowUserinfo,
541
+ allowMixedScript: opts.allowMixedScript,
542
+ allowedScripts: opts.allowedScripts,
543
+ errorClass: opts.errorClass,
544
+ });
545
+
546
+ try {
547
+ // The parser already lowercased the scheme + host, stripped the
548
+ // default port, emitted IDN as punycode, and resolved `.`/`..`. The
549
+ // canonicalizer adds: IP-literal collapse (shared with the SSRF
550
+ // classifier), trailing-dot removal, and path percent-normalization.
551
+ var scheme = parsed.protocol; // e.g. "https:"
552
+ var rawHost = parsed.hostname; // already lowercase / punycode / default-port-free
553
+ var canonHost = ssrfGuard().canonicalizeHost(rawHost);
554
+ // canonicalizeHost returns IPv6 UNbracketed; a URL authority needs the
555
+ // brackets back. net.isIP via the colon is a sufficient discriminator
556
+ // (a DNS label can't contain a colon).
557
+ var host = canonHost.indexOf(":") !== -1 ? "[" + canonHost + "]" : canonHost;
558
+
559
+ // Userinfo (`user:pass@`) is deliberately DROPPED from the canonical form.
560
+ // The canonical string is built to be compared, used as a dedup / cache
561
+ // key, or logged; carrying a credential into any of those leaks it, and
562
+ // the username/password are not part of the resource's target identity for
563
+ // an allowlist / SSRF decision. parse() already refuses userinfo unless
564
+ // allowUserinfo:true; even then, the canonical output omits it (never reads
565
+ // parsed.password), so two URLs that differ only in credentials canonicalize
566
+ // equal.
567
+
568
+ var port = parsed.port !== "" ? ":" + parsed.port : "";
569
+ var path = _normalizePctPath(parsed.pathname);
570
+ // Query + fragment: byte-for-byte as the parser emitted them. Reordering
571
+ // a query or re-decoding a value changes semantics — out of scope for a
572
+ // safe canonicalizer.
573
+ var query = parsed.search;
574
+ var fragment = parsed.hash;
575
+
576
+ return scheme + "//" + host + port + path + query + fragment;
577
+ } catch (e) {
578
+ if (e && e.isSafeUrlError) throw e;
579
+ throw _makeError(opts.errorClass, "safe-url/uncanonicalizable",
580
+ "safeUrl.canonicalize could not reduce the URL to a canonical form: " +
581
+ ((e && e.message) || String(e)));
582
+ }
583
+ }
584
+
419
585
  module.exports = {
420
586
  parse: parse,
421
587
  format: format,
588
+ canonicalize: canonicalize,
422
589
  SafeUrlError: SafeUrlError,
423
590
  ALLOW_HTTP_TLS: ALLOW_HTTP_TLS,
424
591
  ALLOW_HTTP_ALL: ALLOW_HTTP_ALL,