@blamejs/blamejs-shop 0.4.31 → 0.4.33

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 (343) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -1
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/vendor/MANIFEST.json +400 -282
  5. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  6. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  7. package/lib/vendor/blamejs/.gitignore +6 -0
  8. package/lib/vendor/blamejs/CHANGELOG.md +28 -0
  9. package/lib/vendor/blamejs/MIGRATING.md +55 -0
  10. package/lib/vendor/blamejs/README.md +8 -6
  11. package/lib/vendor/blamejs/SECURITY.md +19 -3
  12. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  13. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  14. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  15. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  16. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  17. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  18. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  19. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  20. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  21. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  22. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  23. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  24. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  25. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  26. package/lib/vendor/blamejs/index.js +4 -0
  27. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  28. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  29. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  30. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  31. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  32. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  33. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  34. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  35. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  36. package/lib/vendor/blamejs/lib/audit.js +259 -123
  37. package/lib/vendor/blamejs/lib/auth/elevation-grant.js +6 -2
  38. package/lib/vendor/blamejs/lib/auth/oauth.js +66 -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 +36 -7
  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 +210 -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/credential-hash.js +9 -0
  55. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  56. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  57. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  58. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  59. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  60. package/lib/vendor/blamejs/lib/db.js +249 -99
  61. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  62. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  63. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  64. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  65. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  66. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  67. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  68. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  69. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  70. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  71. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  72. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  73. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  74. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  75. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  76. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  77. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  78. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  79. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  80. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  81. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  82. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  83. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  84. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  85. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  86. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  87. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  88. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  89. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  90. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  91. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  92. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  93. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  94. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  95. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  96. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  97. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  98. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  99. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  100. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  101. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  102. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  103. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  104. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  105. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  106. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  107. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  108. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  109. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  110. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  111. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  112. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  113. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  114. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  115. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  116. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  117. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  118. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  119. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  120. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  121. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  122. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  123. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  124. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  125. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  126. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  127. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  128. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  129. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  131. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  132. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  133. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  134. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  135. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  136. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  137. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  138. package/lib/vendor/blamejs/lib/mail.js +8 -4
  139. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  140. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  141. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  142. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  143. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  144. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  145. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  146. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  147. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  148. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  149. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  150. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  151. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  152. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  153. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  154. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  155. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  156. package/lib/vendor/blamejs/lib/observability.js +124 -0
  157. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  158. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  159. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  160. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  161. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  162. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  163. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  164. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  165. package/lib/vendor/blamejs/lib/queue.js +7 -0
  166. package/lib/vendor/blamejs/lib/redact.js +68 -11
  167. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  168. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  169. package/lib/vendor/blamejs/lib/retention.js +117 -42
  170. package/lib/vendor/blamejs/lib/router.js +212 -5
  171. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  172. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  173. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  174. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  175. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  176. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  177. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  178. package/lib/vendor/blamejs/lib/scheduler.js +47 -12
  179. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  180. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  181. package/lib/vendor/blamejs/lib/session.js +175 -77
  182. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  183. package/lib/vendor/blamejs/lib/sse.js +26 -0
  184. package/lib/vendor/blamejs/lib/ssrf-guard.js +169 -4
  185. package/lib/vendor/blamejs/lib/static.js +177 -34
  186. package/lib/vendor/blamejs/lib/subject.js +96 -49
  187. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  188. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  189. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  190. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  191. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  192. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  193. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  194. package/lib/vendor/blamejs/package.json +2 -2
  195. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  200. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  201. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  202. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  203. package/lib/vendor/blamejs/release-notes/v0.15.7.json +43 -0
  204. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  205. package/lib/vendor/blamejs/scripts/gen-migrating.js +67 -0
  206. package/lib/vendor/blamejs/scripts/release.js +398 -38
  207. package/lib/vendor/blamejs/test/00-primitives.js +168 -0
  208. package/lib/vendor/blamejs/test/10-state.js +140 -14
  209. package/lib/vendor/blamejs/test/20-db.js +65 -2
  210. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  211. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  212. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  213. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  214. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  215. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  216. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  217. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  220. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  221. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  222. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  223. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  224. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  225. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  226. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  227. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  228. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  229. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  230. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  231. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  232. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  233. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  234. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  235. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  236. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  237. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  238. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  239. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  240. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  242. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  248. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  249. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  250. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  251. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  252. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1196 -14
  253. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  254. package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +18 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  256. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  258. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  259. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  267. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  271. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  272. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  273. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  275. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  276. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  277. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  278. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  282. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  283. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  285. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  286. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  287. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  288. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  296. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/retention-floor.test.js +59 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +362 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-watchdog-stale-settle.test.js +71 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  308. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  309. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  310. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  312. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  313. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  314. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  315. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  316. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  317. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  318. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  319. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  320. package/lib/vendor/blamejs/test/smoke.js +79 -21
  321. package/package.json +2 -2
  322. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  323. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  324. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  325. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  326. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  327. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  328. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  329. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  330. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  331. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  332. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  333. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  334. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  335. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  336. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  337. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  338. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  339. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  340. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  341. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  342. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  343. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -0,0 +1,362 @@
1
+ "use strict";
2
+ /**
3
+ * b.safeUrl.canonicalize — URL/host canonicalizer for safe comparison and
4
+ * SSRF-allowlist defense.
5
+ *
6
+ * Every obfuscated spelling of one destination must reduce to one string so a
7
+ * host allowlist / dedup key / SSRF pre-check compares equal. The adversarial
8
+ * cases below are the exact shapes an attacker reaches for to slip a naive
9
+ * `===` allowlist: IPv4 in octal / hex / decimal-int / shorthand, IPv4-mapped
10
+ * and zero-compressed IPv6, IDN homographs, trailing-dot and default-port and
11
+ * case variations, and over-encoded path bytes.
12
+ *
13
+ * Source-discipline: NO attack characters are typed as literals. The one
14
+ * mixed-script case is expressed as its already-encoded punycode A-label
15
+ * (`xn--ggle-55da.com`, same fixture the homograph suite uses) so this file
16
+ * stays pure ASCII.
17
+ */
18
+
19
+ var b = require("../..");
20
+ var check = require("../helpers/check").check;
21
+
22
+ // ---- Equivalence classes: obfuscated loopback collapses to one string ----
23
+
24
+ function testIpv4LoopbackEquivalenceClass() {
25
+ // Decimal-dotted, octal, hex-word, decimal-int, and shorthand spellings of
26
+ // 127.0.0.1 — in the OLD world (raw string compare, no canonicalizer) every
27
+ // pair below compared UNEQUAL, so an allowlist of "127.0.0.1" let them all
28
+ // through. canonicalize collapses them to one form.
29
+ var forms = [
30
+ "http://127.0.0.1/",
31
+ "http://0177.0.0.1/", // octal first octet
32
+ "http://0x7f.1/", // hex octet + shorthand
33
+ "http://0x7f000001/", // single hex dword
34
+ "http://2130706433/", // single decimal dword
35
+ "http://127.1/", // a.d shorthand
36
+ ];
37
+ var expected = "http://127.0.0.1/";
38
+ for (var i = 0; i < forms.length; i += 1) {
39
+ var got = b.safeUrl.canonicalize(forms[i]);
40
+ check("ipv4 loopback form '" + forms[i] + "' canonicalizes to " + expected,
41
+ got === expected);
42
+ // The pairwise equality the allowlist actually relies on.
43
+ check("ipv4 loopback '" + forms[i] + "' === canonical '127.0.0.1' form",
44
+ b.safeUrl.canonicalize(forms[i]) === b.safeUrl.canonicalize(forms[0]));
45
+ // Prove the test is meaningful: the RAW strings were NOT equal.
46
+ if (forms[i] !== forms[0]) {
47
+ check("raw '" + forms[i] + "' !== raw canonical (old-world bypass shape)",
48
+ forms[i] !== forms[0]);
49
+ }
50
+ }
51
+ }
52
+
53
+ function testIpv6MappedEquivalenceClass() {
54
+ // An IPv4-mapped IPv6 address (::ffff:a.b.c.d, the ::ffff:0:0/96 block) IS
55
+ // the IPv4 address a.b.c.d for routing / access-control: a dual-stack peer
56
+ // arriving on ::ffff:1.2.3.4 reaches the same host as 1.2.3.4, and the SSRF
57
+ // classifier already re-classifies it by the embedded v4. So the canonical
58
+ // form must FOLD it to the IPv4 dotted form — otherwise a dual-stack peer
59
+ // never unifies with an operator's IPv4 allowlist entry (the exact bypass).
60
+ var mappedForms = [
61
+ "http://[::ffff:127.0.0.1]/",
62
+ "http://[::ffff:7f00:1]/",
63
+ "http://[0:0:0:0:0:ffff:7f00:1]/",
64
+ "http://[0:0:0:0:0:FFFF:7F00:1]/", // mixed-case hex
65
+ ];
66
+ var bareV4 = b.safeUrl.canonicalize("http://127.0.0.1/");
67
+ check("plain IPv4 canonical is the dotted form", bareV4 === "http://127.0.0.1/");
68
+ for (var i = 0; i < mappedForms.length; i += 1) {
69
+ check("ipv4-mapped '" + mappedForms[i] + "' folds to the bare IPv4 form",
70
+ b.safeUrl.canonicalize(mappedForms[i]) === bareV4);
71
+ check("raw '" + mappedForms[i] + "' !== 'http://127.0.0.1/' (old-world unequal)",
72
+ mappedForms[i] !== "http://127.0.0.1/");
73
+ }
74
+ // canonicalizeHost folds the host-only form too (used for host allowlists).
75
+ check("canonicalizeHost folds ::ffff:1.2.3.4 to 1.2.3.4",
76
+ b.ssrfGuard.canonicalizeHost("::ffff:1.2.3.4") === "1.2.3.4");
77
+ check("canonicalizeHost folds the all-hex mapped spelling too",
78
+ b.ssrfGuard.canonicalizeHost("::ffff:102:304") === "1.2.3.4");
79
+ // A non-mapped IPv6 (no ::ffff:0:0/96 prefix) stays IPv6 — only the
80
+ // v4-mapped block is an IPv4 alias; an embedded-v4 in a documentation
81
+ // prefix is a distinct address.
82
+ check("a non-mapped IPv6 stays IPv6 (::1)",
83
+ b.ssrfGuard.canonicalizeHost("::1") === "::1");
84
+ check("2001:db8::1.2.3.4 (v4 suffix, NOT v4-mapped) stays IPv6",
85
+ b.ssrfGuard.canonicalizeHost("2001:db8::1.2.3.4").indexOf(".") === -1);
86
+ }
87
+
88
+ function testEmbeddedV4AndTrailingDotUnification() {
89
+ // The canonical form must never flip an SSRF classify() verdict from blocked
90
+ // to allowed. Only the IPv4-mapped block (::ffff:0:0/96) folds, because
91
+ // classify(::ffff:x) === classify(x) — its branch returns classify(mappedV4)
92
+ // with NO reserved fallback. NAT64 (64:ff9b::/96) and 6to4 (2002::/16) are
93
+ // NOT folded: classify treats a NAT64 literal as `classify(v4) || "reserved"`,
94
+ // so classify("64:ff9b::8.8.8.8") is "reserved" while classify("8.8.8.8") is
95
+ // null — folding would turn a blocked NAT64 address into an allowed public
96
+ // IPv4 verdict. The invariant below pins that: canonicalizing then classifying
97
+ // must agree with classifying the original.
98
+ var classify = b.ssrfGuard.classify;
99
+ function classifyAgrees(host) {
100
+ return classify(b.ssrfGuard.canonicalizeHost(host)) === classify(host);
101
+ }
102
+ check("NAT64 stays IPv6 (a public NAT64 literal must not become an allowed IPv4)",
103
+ b.ssrfGuard.canonicalizeHost("64:ff9b::8.8.8.8").indexOf(".") === -1);
104
+ check("canonicalize agrees with classify on a public NAT64 literal",
105
+ classifyAgrees("64:ff9b::8.8.8.8"));
106
+ check("canonicalize agrees with classify on a NAT64 loopback literal",
107
+ classifyAgrees("64:ff9b::127.0.0.1"));
108
+ check("canonicalize agrees with classify on a public IPv4-mapped literal",
109
+ classifyAgrees("::ffff:8.8.8.8"));
110
+ // 6to4 (2002::/16) is a /48 PREFIX, not a 1:1 alias — it must stay IPv6
111
+ // (folding it would collapse a whole subnet onto one IPv4).
112
+ check("6to4 2002:7f00:1:: stays IPv6 (not a 1:1 v4 alias)",
113
+ b.ssrfGuard.canonicalizeHost("2002:7f00:1::").indexOf(".") === -1);
114
+
115
+ // Trailing dots are not significant for host identity — every count must
116
+ // collapse to the bare name so host / host. / host.. all unify.
117
+ check("single trailing dot strips to the bare name",
118
+ b.ssrfGuard.canonicalizeHost("example.com.") === "example.com");
119
+ check("multiple trailing dots all strip to the bare name",
120
+ b.ssrfGuard.canonicalizeHost("example.com..") === "example.com");
121
+ check("canonicalize unifies a trailing-dot URL host with the bare host",
122
+ b.safeUrl.canonicalize("http://example.com./p") === b.safeUrl.canonicalize("http://example.com/p"));
123
+ }
124
+
125
+ function testIpv6ZeroCompressionEquivalenceClass() {
126
+ // ::1 in compressed, fully-expanded, and zero-padded spellings.
127
+ var forms = [
128
+ "http://[::1]/",
129
+ "http://[0:0:0:0:0:0:0:1]/",
130
+ "http://[0000:0000:0000:0000:0000:0000:0000:0001]/",
131
+ ];
132
+ var first = b.safeUrl.canonicalize(forms[0]);
133
+ check("IPv6 loopback canonical is [::1]", first === "http://[::1]/");
134
+ for (var i = 1; i < forms.length; i += 1) {
135
+ check("IPv6 loopback form '" + forms[i] + "' === [::1] canonical",
136
+ b.safeUrl.canonicalize(forms[i]) === first);
137
+ check("raw '" + forms[i] + "' !== raw '[::1]' (old-world unequal)",
138
+ forms[i] !== forms[0]);
139
+ }
140
+ // A documentation address with an INTERIOR zero run picks the longest run
141
+ // (RFC 5952 §4.2.3) and stays lower-hex.
142
+ check("interior zero-run compresses correctly",
143
+ b.safeUrl.canonicalize("https://[2001:0DB8:0:0:0:0:0:1]/") ===
144
+ "https://[2001:db8::1]/");
145
+ }
146
+
147
+ // ---- IP-literal cross-check against the SSRF classifier's byte parser ----
148
+
149
+ function testIpCanonicalAgreesWithClassifier() {
150
+ // The canonical host string and the SSRF verdict are derived from the SAME
151
+ // bytes — so the canonical loopback string classifies as loopback and the
152
+ // canonical mapped form re-classifies its embedded v4 as loopback too.
153
+ check("canonical 127.0.0.1 classifies loopback",
154
+ b.ssrfGuard.classify("127.0.0.1") === "loopback");
155
+ // canonicalizeHost is the IP-byte + case layer: it byte-canonicalizes
156
+ // net.isIP-recognized literals (the numeric-base decode of 0x.../octal/dword
157
+ // is the WHATWG-URL-parser layer that safeUrl.canonicalize runs first).
158
+ check("ssrfGuard.canonicalizeHost passes an already-canonical dotted-quad",
159
+ b.ssrfGuard.canonicalizeHost("127.0.0.1") === "127.0.0.1");
160
+ check("ssrfGuard.canonicalizeHost collapses expanded v6 to ::1",
161
+ b.ssrfGuard.canonicalizeHost("[0:0:0:0:0:0:0:1]") === "::1");
162
+ check("ssrfGuard.canonicalizeHost compresses + lower-hexes a mixed-case v6",
163
+ b.ssrfGuard.canonicalizeHost("2001:0DB8:0:0:0:0:0:1") === "2001:db8::1");
164
+ check("ssrfGuard.canonicalizeHost lowercases + strips trailing dot",
165
+ b.ssrfGuard.canonicalizeHost("Example.COM.") === "example.com");
166
+ }
167
+
168
+ // ---- IDN: A-label emission + homograph refusal ----
169
+
170
+ function testIdnEmittedAsPunycode() {
171
+ // A pure-Cyrillic+Latin-tld host parses (single script per label) and is
172
+ // emitted as its xn-- A-label, never the Unicode form.
173
+ var got = b.safeUrl.canonicalize("https://xn--80akhbyknj4f.com/");
174
+ check("IDN host emitted as xn-- A-label",
175
+ got === "https://xn--80akhbyknj4f.com/");
176
+ }
177
+
178
+ function testConfusableHostThrows() {
179
+ // xn--ggle-55da.com decodes to a label mixing Cyrillic + Latin (the
180
+ // gооgle.com homograph). canonicalize must THROW, never silently pass it.
181
+ var threw = false;
182
+ var code = null;
183
+ try { b.safeUrl.canonicalize("https://xn--ggle-55da.com/"); }
184
+ catch (e) { threw = true; code = e.code; }
185
+ check("confusable / mixed-script host throws", threw === true);
186
+ check("confusable host throw carries safe-url/idn-homograph",
187
+ code === "safe-url/idn-homograph");
188
+ }
189
+
190
+ function testMixedScriptOptInCanonicalizes() {
191
+ // Operators with a legitimate non-Latin host opt in; the A-label is then
192
+ // canonicalized rather than refused.
193
+ var got = b.safeUrl.canonicalize("https://xn--ggle-55da.com/", {
194
+ allowMixedScript: true,
195
+ });
196
+ check("allowMixedScript:true canonicalizes the mixed-script host",
197
+ got === "https://xn--ggle-55da.com/");
198
+ }
199
+
200
+ // ---- scheme / host case, default port, trailing dot, path ----
201
+
202
+ function testEndToEndNormalization() {
203
+ // The headline end-to-end case from the spec: case-folded host, stripped
204
+ // default port, resolved `..`, all in one.
205
+ check("https://Example.COM:443/a/../b -> https://example.com/b",
206
+ b.safeUrl.canonicalize("https://Example.COM:443/a/../b") ===
207
+ "https://example.com/b");
208
+ check("http default :80 stripped + host lowercased",
209
+ b.safeUrl.canonicalize("http://Example.COM:80/") ===
210
+ "http://example.com/");
211
+ check("trailing-dot host removed",
212
+ b.safeUrl.canonicalize("https://example.com./path") ===
213
+ "https://example.com/path");
214
+ check("non-default port preserved",
215
+ b.safeUrl.canonicalize("https://example.com:8443/") ===
216
+ "https://example.com:8443/");
217
+ }
218
+
219
+ function testPathPercentNormalization() {
220
+ // RFC 3986 §6.2.2: escapes of unreserved chars are decoded, hex digits
221
+ // uppercased — in the PATH only.
222
+ check("path %7E decodes to ~ and %2D to -",
223
+ b.safeUrl.canonicalize("https://example.com/%7Euser/a%2Db") ===
224
+ "https://example.com/~user/a-b");
225
+ // A reserved char (%2F = '/') is NOT decoded (would change structure); its
226
+ // hex is uppercased.
227
+ check("reserved %2f path escape uppercased, NOT decoded",
228
+ b.safeUrl.canonicalize("https://example.com/a%2fb") ===
229
+ "https://example.com/a%2Fb");
230
+ // Query semantics are conservative: a %2D in the QUERY is left verbatim
231
+ // (lowercase, undecoded) so value semantics are never altered.
232
+ check("query percent-escape left byte-for-byte",
233
+ b.safeUrl.canonicalize("https://example.com/?x=%2d") ===
234
+ "https://example.com/?x=%2d");
235
+ }
236
+
237
+ // ---- throw paths: disallowed scheme + unparseable + scheme allowlist ----
238
+
239
+ function testDisallowedSchemeThrows() {
240
+ var threw = false;
241
+ var code = null;
242
+ try { b.safeUrl.canonicalize("ftp://example.com/file"); }
243
+ catch (e) { threw = true; code = e.code; }
244
+ check("ftp scheme throws", threw === true);
245
+ check("disallowed-scheme code is safe-url/protocol-disallowed",
246
+ code === "safe-url/protocol-disallowed");
247
+ }
248
+
249
+ function testUnparseableThrows() {
250
+ var threw = false;
251
+ var code = null;
252
+ try { b.safeUrl.canonicalize("this is not a url"); }
253
+ catch (e) { threw = true; code = e.code; }
254
+ check("unparseable input throws", threw === true);
255
+ check("unparseable code is safe-url/malformed", code === "safe-url/malformed");
256
+ }
257
+
258
+ function testMissingInputThrows() {
259
+ var threw = false;
260
+ var code = null;
261
+ try { b.safeUrl.canonicalize(""); }
262
+ catch (e) { threw = true; code = e.code; }
263
+ check("empty input throws safe-url/missing", threw === true && code === "safe-url/missing");
264
+ }
265
+
266
+ function testCustomSchemeAllowlist() {
267
+ // Narrow the allowlist: https-only refuses an http URL.
268
+ var threw = false;
269
+ var code = null;
270
+ try {
271
+ b.safeUrl.canonicalize("http://example.com/", {
272
+ allowedSchemes: b.safeUrl.ALLOW_HTTP_TLS,
273
+ });
274
+ } catch (e) { threw = true; code = e.code; }
275
+ check("custom allowedSchemes (https-only) refuses http",
276
+ threw === true && code === "safe-url/protocol-disallowed");
277
+ // ws canonicalizes under the default ALLOW_ANY.
278
+ check("ws scheme canonicalizes under default allowlist",
279
+ b.safeUrl.canonicalize("ws://Example.COM:80/s") === "ws://example.com/s");
280
+ }
281
+
282
+ function testIdempotence() {
283
+ // canonicalize(canonicalize(x)) === canonicalize(x) for the whole adversarial
284
+ // set — a canonical form must be a fixed point.
285
+ var inputs = [
286
+ "http://0177.0.0.1/",
287
+ "http://[0:0:0:0:0:ffff:7f00:1]/",
288
+ "https://Example.COM:443/a/../b",
289
+ "https://example.com/%7Euser",
290
+ "https://xn--80akhbyknj4f.com/",
291
+ ];
292
+ for (var i = 0; i < inputs.length; i += 1) {
293
+ var once = b.safeUrl.canonicalize(inputs[i]);
294
+ var twice = b.safeUrl.canonicalize(once);
295
+ check("canonicalize is idempotent for '" + inputs[i] + "'", once === twice);
296
+ }
297
+ }
298
+
299
+ function testUncanonicalizableCodeIsRegistered() {
300
+ // The post-parse fallback code is reachable as a documented SafeUrlError
301
+ // code — constructing it directly proves the class accepts it.
302
+ var e = new b.safeUrl.SafeUrlError("safe-url/uncanonicalizable", "x");
303
+ check("safe-url/uncanonicalizable is a valid SafeUrlError code",
304
+ e.code === "safe-url/uncanonicalizable" && e.isSafeUrlError === true);
305
+ }
306
+
307
+ // ---- Credentials are never carried into the canonical form ----
308
+
309
+ function testUserinfoDroppedFromCanonicalForm() {
310
+ // The canonical string is built to be compared, used as a dedup / cache key,
311
+ // or logged — it must never carry user:pass credentials, and the creds are
312
+ // not part of the target identity for an allowlist / SSRF decision. parse()
313
+ // refuses userinfo by default; even opted-in, the canonical output omits it.
314
+ var user = "alice";
315
+ var token = "s3cr" + "et-pw-9f3a"; // split so no secret-shaped literal sits in source
316
+ var withCreds = "https://" + user + ":" + token + "@host.example.com/p";
317
+
318
+ var deniedCode = null;
319
+ try { b.safeUrl.canonicalize(withCreds); }
320
+ catch (e) { deniedCode = e && e.code; }
321
+ check("canonicalize refuses userinfo by default",
322
+ deniedCode === "safe-url/userinfo-disallowed");
323
+
324
+ var canon = b.safeUrl.canonicalize(withCreds, { allowUserinfo: true });
325
+ check("canonicalize drops the userinfo delimiter from the canonical form",
326
+ canon.indexOf("@") === -1);
327
+ check("canonicalize does not carry the password into the canonical form",
328
+ canon.indexOf(token) === -1);
329
+ check("canonicalize does not carry the username into the canonical form",
330
+ canon.indexOf("//" + user) === -1);
331
+ check("the credential-stripped canonical form is the bare target",
332
+ canon === "https://host.example.com/p");
333
+ check("URLs differing only in credentials canonicalize equal",
334
+ b.safeUrl.canonicalize("https://x:y@host.example.com/p", { allowUserinfo: true }) === canon);
335
+ }
336
+
337
+ async function run() {
338
+ testUserinfoDroppedFromCanonicalForm();
339
+ testEmbeddedV4AndTrailingDotUnification();
340
+ testIpv4LoopbackEquivalenceClass();
341
+ testIpv6MappedEquivalenceClass();
342
+ testIpv6ZeroCompressionEquivalenceClass();
343
+ testIpCanonicalAgreesWithClassifier();
344
+ testIdnEmittedAsPunycode();
345
+ testConfusableHostThrows();
346
+ testMixedScriptOptInCanonicalizes();
347
+ testEndToEndNormalization();
348
+ testPathPercentNormalization();
349
+ testDisallowedSchemeThrows();
350
+ testUnparseableThrows();
351
+ testMissingInputThrows();
352
+ testCustomSchemeAllowlist();
353
+ testIdempotence();
354
+ testUncanonicalizableCodeIsRegistered();
355
+ }
356
+
357
+ module.exports = { run: run };
358
+
359
+ if (require.main === module) {
360
+ run().then(function () { console.log("OK"); })
361
+ .catch(function (e) { console.error(e.stack || e); process.exit(1); });
362
+ }
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * b.parsers.xml.parse — security-focused XML parser.
4
+ *
5
+ * Covers the structural defaults (XXE / DOCTYPE / billion-laughs / depth
6
+ * + element + attribute caps) and the prototype-pollution posture: the
7
+ * result tree and every nested object it contains carry a null prototype,
8
+ * and element / attribute names equal to __proto__ / constructor /
9
+ * prototype are rejected with xml/forbidden-name (CWE-1321 / OWASP
10
+ * prototype-pollution).
11
+ *
12
+ * Run standalone: `node test/layer-0-primitives/safe-xml.test.js`
13
+ */
14
+
15
+ var helpers = require("../helpers");
16
+ var b = helpers.b;
17
+ var check = helpers.check;
18
+
19
+ var xml = b.parsers.xml;
20
+
21
+ // ---- Baseline parse + shape ----
22
+
23
+ function testParsesAttributedElement() {
24
+ var result = xml.parse('<root id="x"><child>text</child></root>');
25
+ check("xml.parse returns the root key",
26
+ result && result.root && result.root["@attrs"] && result.root["@attrs"].id === "x");
27
+ check("xml.parse groups child element",
28
+ result.root.child === "text");
29
+ }
30
+
31
+ function testRejectsDoctype() {
32
+ var threw = null;
33
+ try { xml.parse('<!DOCTYPE x SYSTEM "file:///etc/passwd"><root/>'); }
34
+ catch (e) { threw = e; }
35
+ check("xml.parse rejects DOCTYPE (XXE defense)",
36
+ threw && threw.code === "xml/doctype");
37
+ }
38
+
39
+ function testRejectsCustomEntity() {
40
+ var threw = null;
41
+ try { xml.parse("<root>&xxe;</root>"); }
42
+ catch (e) { threw = e; }
43
+ check("xml.parse rejects custom entity (XXE defense)",
44
+ threw && threw.code === "xml/external-entity");
45
+ }
46
+
47
+ // ---- Prototype-pollution posture (CWE-1321) ----
48
+
49
+ function testResultTreeHasNullPrototype() {
50
+ // A document that exercises every keyed accumulator: attributes (attrs),
51
+ // grouped child elements (grouped / obj), and the element-name wrapper
52
+ // (out). Each must carry a null prototype so a consumer reading an
53
+ // absent key sees undefined, never an inherited Object member.
54
+ var result = xml.parse(
55
+ '<root attr="v"><a>1</a><a>2</a><b><c>deep</c></b></root>'
56
+ );
57
+
58
+ check("result wrapper has null prototype",
59
+ Object.getPrototypeOf(result) === null);
60
+ check("element object has null prototype",
61
+ Object.getPrototypeOf(result.root) === null);
62
+ check("@attrs object has null prototype",
63
+ Object.getPrototypeOf(result.root["@attrs"]) === null);
64
+ check("nested element object has null prototype",
65
+ Object.getPrototypeOf(result.root.b) === null);
66
+ check("repeated child became an array",
67
+ Array.isArray(result.root.a) && result.root.a.length === 2);
68
+
69
+ // No inherited Object.prototype members leak through any accumulator —
70
+ // a plain {} would surface a function here.
71
+ check("no inherited toString on result",
72
+ result.toString === undefined);
73
+ check("no inherited hasOwnProperty on element object",
74
+ result.root.hasOwnProperty === undefined);
75
+ check("no inherited constructor on @attrs object",
76
+ result.root["@attrs"].constructor === undefined);
77
+ }
78
+
79
+ function testRejectsProtoAttributeName() {
80
+ var threw = null;
81
+ try { xml.parse('<root __proto__="polluted"/>'); }
82
+ catch (e) { threw = e; }
83
+ check("xml.parse rejects __proto__ attribute name",
84
+ threw && threw.code === "xml/forbidden-name");
85
+ // The global Object.prototype was not mutated either way.
86
+ check("Object.prototype not polluted by __proto__ attribute",
87
+ ({}).polluted === undefined);
88
+ }
89
+
90
+ function testRejectsProtoElementName() {
91
+ var threw = null;
92
+ try { xml.parse("<__proto__><x>y</x></__proto__>"); }
93
+ catch (e) { threw = e; }
94
+ check("xml.parse rejects __proto__ element name",
95
+ threw && threw.code === "xml/forbidden-name");
96
+ }
97
+
98
+ function testRejectsConstructorElementName() {
99
+ var threw = null;
100
+ try { xml.parse("<root><constructor>x</constructor></root>"); }
101
+ catch (e) { threw = e; }
102
+ check("xml.parse rejects constructor child element name",
103
+ threw && threw.code === "xml/forbidden-name");
104
+ }
105
+
106
+ function testRejectsPrototypeElementName() {
107
+ var threw = null;
108
+ try { xml.parse("<prototype/>"); }
109
+ catch (e) { threw = e; }
110
+ check("xml.parse rejects prototype element name",
111
+ threw && threw.code === "xml/forbidden-name");
112
+ }
113
+
114
+ function testConstructorAttributeNoFalseDuplicate() {
115
+ // With a plain-object accumulator, attrs["constructor"] would read the
116
+ // inherited Object constructor (not undefined) and a single occurrence
117
+ // would falsely trip the duplicate-attribute guard. The forbidden-name
118
+ // rejection now fires first; assert the code is forbidden-name, not
119
+ // duplicate-attr, so the diagnostic points at the real cause.
120
+ var threw = null;
121
+ try { xml.parse('<root constructor="once"/>'); }
122
+ catch (e) { threw = e; }
123
+ check("constructor attribute → forbidden-name (not false duplicate-attr)",
124
+ threw && threw.code === "xml/forbidden-name");
125
+ }
126
+
127
+ (function run() {
128
+ try {
129
+ testParsesAttributedElement();
130
+ testRejectsDoctype();
131
+ testRejectsCustomEntity();
132
+ testResultTreeHasNullPrototype();
133
+ testRejectsProtoAttributeName();
134
+ testRejectsProtoElementName();
135
+ testRejectsConstructorElementName();
136
+ testRejectsPrototypeElementName();
137
+ testConstructorAttributeNoFalseDuplicate();
138
+ } catch (e) {
139
+ console.error(e);
140
+ process.exit(1);
141
+ }
142
+ console.log("OK — safe-xml tests");
143
+ })();