@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
@@ -146,10 +146,14 @@ function testFederationParseAndPolicy() {
146
146
  check("federation: parse returns header.typ", parsed.header.typ === "entity-statement+jwt");
147
147
  check("federation: parse returns claims.iss", parsed.claims.iss === "https://leaf.example");
148
148
 
149
- // metadata_policy operators — value/default/one_of/subset_of/superset_of
149
+ // metadata_policy operators — value/default/one_of/subset_of/superset_of.
150
+ // Per OIDF §6.2 the policy comes from the SUPERIOR-SIGNED subordinate
151
+ // statement about the entity (`chain[i].subordinate.metadata_policy`),
152
+ // NOT the entity's own self-config — so each chain node carries the
153
+ // policy under `.subordinate`.
150
154
  var meta = { application_type: "web", redirect_uris: ["https://leaf/cb"] };
151
155
  var chain = [
152
- { claims: { metadata_policy: { openid_relying_party: {
156
+ { subordinate: { metadata_policy: { openid_relying_party: {
153
157
  application_type: { one_of: ["web", "native"] },
154
158
  grant_types: { default: ["authorization_code"] },
155
159
  } } } },
@@ -162,7 +166,7 @@ function testFederationParseAndPolicy() {
162
166
  var threw = false;
163
167
  try {
164
168
  b.auth.openidFederation.applyMetadataPolicy({ application_type: "bad" }, [
165
- { claims: { metadata_policy: { openid_relying_party: { application_type: { one_of: ["web", "native"] } } } } },
169
+ { subordinate: { metadata_policy: { openid_relying_party: { application_type: { one_of: ["web", "native"] } } } } },
166
170
  ], "openid_relying_party");
167
171
  } catch (e) { threw = /not in/.test(e.message); }
168
172
  check("federation: one_of rejects out-of-set value", threw);
@@ -171,10 +175,230 @@ function testFederationParseAndPolicy() {
171
175
  threw = false;
172
176
  try {
173
177
  b.auth.openidFederation.applyMetadataPolicy({ x: 1 }, [
174
- { claims: { metadata_policy: { openid_relying_party: { x: { bogus: 1 } } } } },
178
+ { subordinate: { metadata_policy: { openid_relying_party: { x: { bogus: 1 } } } } },
175
179
  ], "openid_relying_party");
176
180
  } catch (e) { threw = /unknown operator/.test(e.message); }
177
181
  check("federation: unknown policy op refused", threw);
182
+
183
+ // M2: a leaf's OWN self-config metadata_policy is IGNORED — only the
184
+ // superior-signed subordinate statement constrains the entity. A leaf
185
+ // that self-declares a widening policy can't drop the anchor's rules.
186
+ var ignored = b.auth.openidFederation.applyMetadataPolicy(
187
+ { application_type: "web" },
188
+ [{ claims: { metadata_policy: { openid_relying_party: {
189
+ application_type: { value: "native" } } } } }],
190
+ "openid_relying_party");
191
+ check("federation M2: leaf self-config metadata_policy ignored",
192
+ ignored.application_type === "web");
193
+ }
194
+
195
+ // ---- OpenID Federation trust-chain (multi-element, real signatures) ---
196
+
197
+ // Mint an EC P-256 entity: keypair + a single-key JWKS carrying `kid`.
198
+ function _fedEntity(kid) {
199
+ var kp = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
200
+ var jwk = kp.publicKey.export({ format: "jwk" });
201
+ jwk.kid = kid;
202
+ jwk.use = "sig";
203
+ jwk.alg = "ES256";
204
+ return { kp: kp, kid: kid, jwks: { keys: [jwk] } };
205
+ }
206
+
207
+ // Sign an entity-statement+jwt (ES256) with the given signing entity.
208
+ function _signEntityStatement(signer, claims) {
209
+ var header = { typ: "entity-statement+jwt", alg: "ES256", kid: signer.kid };
210
+ var iat = Math.floor(Date.now() / 1000);
211
+ var full = Object.assign({ iat: iat, exp: iat + 3600 }, claims);
212
+ var input = _b64url(JSON.stringify(header)) + "." + _b64url(JSON.stringify(full));
213
+ var sig = nodeCrypto.sign("sha256", Buffer.from(input, "ascii"),
214
+ { key: signer.kp.privateKey, dsaEncoding: "ieee-p1363" });
215
+ return input + "." + _b64url(sig);
216
+ }
217
+
218
+ async function testFederationTrustChainMultiElement() {
219
+ var ANCHOR = "https://anchor.example";
220
+ var INT = "https://intermediate.example";
221
+ var LEAF = "https://leaf.example";
222
+
223
+ var anchor = _fedEntity("anchor-1");
224
+ var inter = _fedEntity("inter-1");
225
+ var leaf = _fedEntity("leaf-1");
226
+
227
+ // Self-configs (each self-signed over its own jwks).
228
+ var anchorCfg = _signEntityStatement(anchor, { iss: ANCHOR, sub: ANCHOR, jwks: anchor.jwks });
229
+ var interCfg = _signEntityStatement(inter, { iss: INT, sub: INT, jwks: inter.jwks, authority_hints: [ANCHOR] });
230
+ // Leaf self-declares a wide scope ("openid profile email") + a single
231
+ // redirect_uri; the superior policy will force the scope down and
232
+ // confirm the redirect_uri is within the allowed subset.
233
+ var leafMeta = { openid_relying_party: { redirect_uris: ["https://leaf.example/cb"],
234
+ scope: "openid profile email" } };
235
+ var leafCfg = _signEntityStatement(leaf, { iss: LEAF, sub: LEAF, jwks: leaf.jwks,
236
+ authority_hints: [INT], metadata: leafMeta });
237
+
238
+ // Superior-signed subordinate statements.
239
+ // anchor → about intermediate (pins intermediate's real jwks)
240
+ var subAboutInter = _signEntityStatement(anchor, { iss: ANCHOR, sub: INT, jwks: inter.jwks });
241
+ // intermediate → about leaf (pins leaf's jwks + constrains the leaf
242
+ // via metadata_policy: subset_of on redirect_uris + value on scope)
243
+ var leafPolicy = { openid_relying_party: {
244
+ redirect_uris: { subset_of: ["https://leaf.example/cb", "https://leaf.example/cb2"] },
245
+ scope: { value: "openid" },
246
+ } };
247
+ var subAboutLeaf = _signEntityStatement(inter, { iss: INT, sub: LEAF, jwks: leaf.jwks,
248
+ metadata_policy: leafPolicy });
249
+
250
+ function _fetcher(url) {
251
+ if (url === ANCHOR + "/.well-known/openid-federation") return Promise.resolve(anchorCfg);
252
+ if (url === INT + "/.well-known/openid-federation") return Promise.resolve(interCfg);
253
+ if (url === LEAF + "/.well-known/openid-federation") return Promise.resolve(leafCfg);
254
+ return Promise.reject(new Error("404 " + url));
255
+ }
256
+ function _fetchSubordinate(authority, sub) {
257
+ if (authority === ANCHOR && sub === INT) return Promise.resolve(subAboutInter);
258
+ if (authority === INT && sub === LEAF) return Promise.resolve(subAboutLeaf);
259
+ return Promise.reject(new Error("no subordinate " + authority + "→" + sub));
260
+ }
261
+
262
+ var opts = {
263
+ leafEntityId: LEAF,
264
+ trustAnchors: { "https://anchor.example": anchor.jwks },
265
+ fetcher: _fetcher,
266
+ fetchSubordinate: _fetchSubordinate,
267
+ };
268
+
269
+ // Success path: chain builds leaf→anchor, roles assigned, attested
270
+ // jwks flowed down from the pinned anchor.
271
+ var chain = await b.auth.openidFederation.buildTrustChain(opts);
272
+ check("federation chain: leaf-first, anchor-last",
273
+ chain.length === 3 && chain[0].role === "leaf" &&
274
+ chain[1].role === "intermediate" && chain[2].role === "trust_anchor");
275
+ check("federation chain: leaf carries superior-signed subordinate statement",
276
+ chain[0].subordinate && chain[0].subordinate.iss === INT && chain[0].subordinate.sub === LEAF);
277
+
278
+ // M2: the SUPERIOR-SIGNED metadata_policy is enforced (anchor→leaf,
279
+ // narrow-only). `value` forces scope down to "openid"; subset_of
280
+ // confirms the leaf's lone redirect_uri is within the allowed set.
281
+ var resolved = await b.auth.openidFederation.resolveLeaf(Object.assign({ kind: "openid_relying_party" }, opts));
282
+ check("federation M2: superior-signed value narrows leaf scope",
283
+ resolved.effectiveMetadata.scope === "openid");
284
+ check("federation M2: leaf redirect_uri within superior subset_of accepted",
285
+ Array.isArray(resolved.effectiveMetadata.redirect_uris) &&
286
+ resolved.effectiveMetadata.redirect_uris.length === 1 &&
287
+ resolved.effectiveMetadata.redirect_uris[0] === "https://leaf.example/cb");
288
+ }
289
+
290
+ async function testFederationSubsetOfRefusesWidening() {
291
+ // M2 refutation: a leaf that declares a redirect_uri OUTSIDE the
292
+ // superior-signed subset_of must be REFUSED — the leaf cannot widen
293
+ // past the anchor/intermediate constraint by self-declaration.
294
+ var threw = null;
295
+ try {
296
+ b.auth.openidFederation.applyMetadataPolicy(
297
+ { redirect_uris: ["https://leaf.example/cb", "https://leaf.example/evil"] },
298
+ [{ subordinate: { metadata_policy: { openid_relying_party: {
299
+ redirect_uris: { subset_of: ["https://leaf.example/cb"] } } } } }],
300
+ "openid_relying_party");
301
+ } catch (e) { threw = e; }
302
+ check("federation M2: leaf redirect_uri outside superior subset_of refused",
303
+ threw && threw.code === "auth-openid-federation/policy-subset-of-failed");
304
+
305
+ // And the leaf's OWN self-config can't relax that subset — self-config
306
+ // policy is never read, so a self-declared wide subset is ignored while
307
+ // the superior subset still binds.
308
+ var eff = b.auth.openidFederation.applyMetadataPolicy(
309
+ { scope: "openid" },
310
+ [{ claims: { metadata_policy: { openid_relying_party: { scope: { value: "admin" } } } },
311
+ subordinate: { metadata_policy: { openid_relying_party: { scope: { value: "openid" } } } } }],
312
+ "openid_relying_party");
313
+ check("federation M2: superior value wins over leaf self-config",
314
+ eff.scope === "openid");
315
+ }
316
+
317
+ async function testFederationSubordinateVerifiedAgainstAttestedKeys() {
318
+ // M5: the intermediate's SELF-PUBLISHED config jwks differ from the
319
+ // jwks the anchor ATTESTS for it. An attacker controlling the
320
+ // intermediate endpoint serves attacker keys in the self-config AND
321
+ // signs the leaf's subordinate statement with those attacker keys.
322
+ // The verifier MUST verify the leaf statement against the
323
+ // anchor-attested (genuine) intermediate keys, so the attacker-signed
324
+ // statement is rejected.
325
+ var ANCHOR = "https://anchor.example";
326
+ var INT = "https://intermediate.example";
327
+ var LEAF = "https://leaf.example";
328
+
329
+ var anchor = _fedEntity("anchor-1");
330
+ var interReal = _fedEntity("inter-1"); // anchor attests THESE keys
331
+ // The attacker self-publishes a DIFFERENT keypair under the SAME kid so
332
+ // the kid lookup matches the attested key slot — the only thing that
333
+ // can stop it is verifying against the attested key bytes (signature
334
+ // mismatch), which is exactly what the fix does.
335
+ var interEvil = _fedEntity("inter-1"); // same kid, different key
336
+ var leaf = _fedEntity("leaf-1");
337
+
338
+ var anchorCfg = _signEntityStatement(anchor, { iss: ANCHOR, sub: ANCHOR, jwks: anchor.jwks });
339
+ // Intermediate self-config: ATTACKER keys (TOCTOU — served at the
340
+ // self-config fetch). Self-signed with the attacker key so the
341
+ // self-statement integrity check passes.
342
+ var interCfg = _signEntityStatement(interEvil, { iss: INT, sub: INT, jwks: interEvil.jwks, authority_hints: [ANCHOR] });
343
+ var leafCfg = _signEntityStatement(leaf, { iss: LEAF, sub: LEAF, jwks: leaf.jwks, authority_hints: [INT] });
344
+
345
+ // Anchor attests the REAL intermediate keys.
346
+ var subAboutInter = _signEntityStatement(anchor, { iss: ANCHOR, sub: INT, jwks: interReal.jwks });
347
+ // The leaf's subordinate statement is signed with the ATTACKER key
348
+ // (interEvil) — it verifies against interEvil's self-published jwks
349
+ // (the pre-fix path) but NOT against the anchor-attested interReal keys.
350
+ var subAboutLeaf = _signEntityStatement(interEvil, { iss: INT, sub: LEAF, jwks: leaf.jwks });
351
+
352
+ function _fetcher(url) {
353
+ if (url === ANCHOR + "/.well-known/openid-federation") return Promise.resolve(anchorCfg);
354
+ if (url === INT + "/.well-known/openid-federation") return Promise.resolve(interCfg);
355
+ if (url === LEAF + "/.well-known/openid-federation") return Promise.resolve(leafCfg);
356
+ return Promise.reject(new Error("404 " + url));
357
+ }
358
+ function _fetchSubordinate(authority, sub) {
359
+ if (authority === ANCHOR && sub === INT) return Promise.resolve(subAboutInter);
360
+ if (authority === INT && sub === LEAF) return Promise.resolve(subAboutLeaf);
361
+ return Promise.reject(new Error("no subordinate " + authority + "→" + sub));
362
+ }
363
+
364
+ var threw = null;
365
+ try {
366
+ await b.auth.openidFederation.buildTrustChain({
367
+ leafEntityId: LEAF,
368
+ trustAnchors: { "https://anchor.example": anchor.jwks },
369
+ fetcher: _fetcher,
370
+ fetchSubordinate: _fetchSubordinate,
371
+ });
372
+ } catch (e) { threw = e; }
373
+ // A cryptographic refusal proves the attested-key gate held: the
374
+ // attacker-signed statement fails against the anchor-attested key bytes.
375
+ check("federation M5: leaf statement signed by attacker self-jwks is refused",
376
+ threw && (threw.code === "auth-openid-federation/bad-signature" ||
377
+ threw.code === "auth-openid-federation/no-matching-kid"));
378
+
379
+ // Positive control: when the leaf statement is signed by the REAL
380
+ // (anchor-attested) intermediate key, the chain builds.
381
+ var subAboutLeafGood = _signEntityStatement(interReal, { iss: INT, sub: LEAF, jwks: leaf.jwks });
382
+ function _fetchSubordinateGood(authority, sub) {
383
+ if (authority === ANCHOR && sub === INT) return Promise.resolve(subAboutInter);
384
+ if (authority === INT && sub === LEAF) return Promise.resolve(subAboutLeafGood);
385
+ return Promise.reject(new Error("no subordinate " + authority + "→" + sub));
386
+ }
387
+ var built = await b.auth.openidFederation.buildTrustChain({
388
+ leafEntityId: LEAF,
389
+ trustAnchors: { "https://anchor.example": anchor.jwks },
390
+ fetcher: _fetcher,
391
+ fetchSubordinate: _fetchSubordinateGood,
392
+ });
393
+ check("federation M5: leaf statement signed by anchor-attested key builds",
394
+ built.length === 3 && built[2].role === "trust_anchor");
395
+ // The attested intermediate key bytes (interReal), not the self-
396
+ // published attacker key bytes (interEvil), are reflected on the
397
+ // intermediate node — same kid, so compare the EC x-coordinate.
398
+ check("federation M5: intermediate node carries anchor-attested key bytes",
399
+ built[1].claims.jwks &&
400
+ built[1].claims.jwks.keys[0].x === interReal.jwks.keys[0].x &&
401
+ built[1].claims.jwks.keys[0].x !== interEvil.jwks.keys[0].x);
178
402
  }
179
403
 
180
404
  // ---- OID4VP DCQL ------------------------------------------------------
@@ -559,7 +783,12 @@ async function testOid4vciX5cProof() {
559
783
  await _refusedX5c([""], "empty string entry");
560
784
  await _refusedX5c(["@@@not-base64@@@"], "non-base64 entry");
561
785
  // base64url chars (- / _) are invalid for x5c (standard base64 only).
562
- await _refusedX5c([leafB64.replace(/[+/]/, "-")], "base64url-charset entry");
786
+ // Inject them unconditionally: leafB64's '+' / '/' population varies per
787
+ // generated cert, so replacing only the first such char is a no-op on the
788
+ // ~0.4% of certs that carry neither — which would leave a still-valid cert
789
+ // that is correctly accepted, making this refusal assertion flake. Prepend
790
+ // the base64url-only chars so the malformed entry is guaranteed every run.
791
+ await _refusedX5c(["-_" + leafB64.slice(2)], "base64url-charset entry");
563
792
  // valid base64 but not a parseable DER certificate.
564
793
  await _refusedX5c([Buffer.from("not a certificate").toString("base64")], "non-DER-cert entry");
565
794
 
@@ -790,6 +1019,9 @@ async function run() {
790
1019
  testSamlSpAuthnRequest();
791
1020
  testSamlSpRefusesUnsigned();
792
1021
  testFederationParseAndPolicy();
1022
+ await testFederationTrustChainMultiElement();
1023
+ await testFederationSubsetOfRefusesWidening();
1024
+ await testFederationSubordinateVerifiedAgainstAttestedKeys();
793
1025
  testDcqlMatch();
794
1026
  testOid4vciIssuerConfig();
795
1027
  await testOid4vciKidResolver();
@@ -8,9 +8,9 @@
8
8
  * - default behavior unchanged when the new opts are unset;
9
9
  * - deniedDest refuses a "webidentity" (FedCM) Sec-Fetch-Dest on a
10
10
  * non-identity route, regardless of Sec-Fetch-Site;
11
- * - allowStorageAccess:false refuses the cross-site
12
- * Sec-Fetch-Storage-Access: active|inactive escalation, and "none"
13
- * passes through;
11
+ * - the cross-site Sec-Fetch-Storage-Access: active|inactive escalation
12
+ * is REFUSED BY DEFAULT (v0.15.0), "none" passes through, and
13
+ * allowStorageAccess:true opts back in for Storage-Access-flow routes;
14
14
  * - strictDest throws at config time on an unknown destination value;
15
15
  * - membership tests are exact (no substring / prototype-pollution
16
16
  * bypass).
@@ -92,9 +92,9 @@ async function testDeniedDestWebIdentity() {
92
92
  allowed.next === true);
93
93
  }
94
94
 
95
- // Storage Access API escalation: active|inactive refused when
96
- // allowStorageAccess:false; none passes through; default (unset) lets it
97
- // through.
95
+ // Storage Access API escalation: active|inactive REFUSED BY DEFAULT
96
+ // (v0.15.0); none passes through; allowStorageAccess:true opts back in for
97
+ // Storage-Access-flow routes.
98
98
  async function testStorageAccessGate() {
99
99
  var strict = b.middleware.fetchMetadata({ allowStorageAccess: false });
100
100
 
@@ -117,12 +117,23 @@ async function testStorageAccessGate() {
117
117
  check("storage-access none on same-origin passes through",
118
118
  none.next === true);
119
119
 
120
- // Default (opt unset) does not refuse the escalation.
120
+ // Default (opt unset) REFUSES the escalation (v0.15.0) — checked before
121
+ // the allowCrossSite shortcut, so even a cross-site-permitting mount
122
+ // refuses the storage-access escalation by default.
121
123
  var dflt = b.middleware.fetchMetadata({ allowCrossSite: true });
122
- var permitted = await _run(dflt, _post({
124
+ var refusedByDefault = await _run(dflt, _post({
123
125
  "sec-fetch-site": "cross-site", "sec-fetch-storage-access": "active",
124
126
  }), _bodyRes());
125
- check("storage-access escalation permitted by default",
127
+ check("storage-access escalation refused by default (opt unset)",
128
+ refusedByDefault.next === false && refusedByDefault.status === 403);
129
+
130
+ // Documented opt-in: allowStorageAccess:true lets the Storage-Access-flow
131
+ // route through.
132
+ var optedIn = b.middleware.fetchMetadata({ allowCrossSite: true, allowStorageAccess: true });
133
+ var permitted = await _run(optedIn, _post({
134
+ "sec-fetch-site": "cross-site", "sec-fetch-storage-access": "active",
135
+ }), _bodyRes());
136
+ check("allowStorageAccess:true opts back in (escalation permitted)",
126
137
  permitted.next === true);
127
138
  }
128
139
 
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ /**
3
+ * file-upload — content-safety SKIP is audited.
4
+ *
5
+ * When the byte-level content-safety scan does not run for a finalized
6
+ * upload, an operator reviewing the audit log must be able to tell the
7
+ * upload bypassed scanning, and WHY. Each skip path emits exactly one
8
+ * `fileUpload.content_safety_skipped` audit naming the reason:
9
+ *
10
+ * - content-safety-disabled (contentSafety: null opt-out)
11
+ * - no-gate-for-extension (no gate registered for the ext)
12
+ * - streamed-over-reassembly-cap (upload streamed past the cap)
13
+ *
14
+ * The audit is observability-only: a throwing audit sink must NOT crash
15
+ * the upload (the finalize still returns its result).
16
+ */
17
+ var nodeOs = require("node:os");
18
+ var nodePath = require("node:path");
19
+ var nodeFs = require("node:fs");
20
+ var nodeCrypto = require("node:crypto");
21
+ var helpers = require("../helpers");
22
+ var check = helpers.check;
23
+ var b = helpers.b;
24
+
25
+ function _tmpDir(suffix) {
26
+ var dir = nodePath.join(nodeOs.tmpdir(), "fileupload-skipaudit-" + suffix + "-" +
27
+ nodeCrypto.randomBytes(6).toString("hex"));
28
+ nodeFs.mkdirSync(dir, { recursive: true, mode: 0o700 });
29
+ return dir;
30
+ }
31
+ function _chunkSha3(buf) { return require("../../lib/crypto").sha3Hash(buf); }
32
+ function _fullSha3(pieces) {
33
+ var h = nodeCrypto.createHash("sha3-512");
34
+ for (var i = 0; i < pieces.length; i++) h.update(pieces[i]);
35
+ return h.digest("hex");
36
+ }
37
+ function _skipEvents(emitted) {
38
+ return emitted.filter(function (e) {
39
+ return e.action === "fileUpload.content_safety_skipped";
40
+ });
41
+ }
42
+
43
+ // Drive one small (in-memory) upload end-to-end through finalize.
44
+ async function _uploadAndFinalize(u, uploadId, pieces, extra) {
45
+ await u.init({ uploadId: uploadId, actor: { id: uploadId },
46
+ metadata: (extra && extra.metadata) || {} });
47
+ var total = 0;
48
+ for (var i = 0; i < pieces.length; i++) {
49
+ await u.acceptChunk({ uploadId: uploadId, index: i, body: pieces[i],
50
+ sha3: _chunkSha3(pieces[i]), actor: { id: uploadId } });
51
+ total += pieces[i].length;
52
+ }
53
+ var manifest = {
54
+ totalBytes: total,
55
+ sha3: _fullSha3(pieces),
56
+ chunks: pieces.map(function (p, idx) { return { index: idx, sha3: _chunkSha3(p) }; }),
57
+ };
58
+ return u.finalize({ uploadId: uploadId, manifest: manifest, actor: { id: uploadId } });
59
+ }
60
+
61
+ async function run() {
62
+ // ---- Skip reason: content-safety-disabled (contentSafety: null) ----
63
+ {
64
+ var emitted = [];
65
+ var fakeAudit = { safeEmit: function (e) { emitted.push(e); } };
66
+ var u = b.fileUpload.create({
67
+ stagingDir: _tmpDir("disabled"),
68
+ contentSafety: null,
69
+ filenameSafety: null,
70
+ audit: fakeAudit,
71
+ });
72
+ emitted.length = 0; // drop the create-time disable audits
73
+ var c0 = Buffer.from("plain text body", "utf8");
74
+ await _uploadAndFinalize(u, "u-disabled", [c0],
75
+ { metadata: { filename: "doc.txt" } });
76
+ var skips = _skipEvents(emitted);
77
+ check("skip[disabled]: exactly one skip audit", skips.length === 1);
78
+ check("skip[disabled]: reason names the disable",
79
+ skips.length === 1 && skips[0].reason === "content-safety-disabled" &&
80
+ skips[0].metadata && skips[0].metadata.reason === "content-safety-disabled");
81
+ check("skip[disabled]: outcome is an accepted audit outcome",
82
+ skips.length === 1 && skips[0].outcome === "success");
83
+ check("skip[disabled]: metadata carries size",
84
+ skips.length === 1 && skips[0].metadata.size === c0.length);
85
+ }
86
+
87
+ // ---- Skip reason: no-gate-for-extension ----
88
+ // contentSafety wired with a gate ONLY for ".csv"; upload a ".txt" so
89
+ // no gate matches the extension.
90
+ {
91
+ var emitted2 = [];
92
+ var fakeAudit2 = { safeEmit: function (e) { emitted2.push(e); } };
93
+ var csvGateChecked = false;
94
+ var u2 = b.fileUpload.create({
95
+ stagingDir: _tmpDir("nogate"),
96
+ filenameSafety: null,
97
+ contentSafety: {
98
+ ".csv": { check: function () { csvGateChecked = true; return { ok: true, action: "serve" }; } },
99
+ },
100
+ audit: fakeAudit2,
101
+ });
102
+ emitted2.length = 0;
103
+ var t0 = Buffer.from("not a csv", "utf8");
104
+ await _uploadAndFinalize(u2, "u-nogate", [t0],
105
+ { metadata: { filename: "notes.txt" } });
106
+ var skips2 = _skipEvents(emitted2);
107
+ check("skip[no-gate]: the .csv gate was NOT invoked for a .txt", csvGateChecked === false);
108
+ check("skip[no-gate]: exactly one skip audit", skips2.length === 1);
109
+ check("skip[no-gate]: reason names the missing gate",
110
+ skips2.length === 1 && skips2[0].reason === "no-gate-for-extension");
111
+ check("skip[no-gate]: metadata carries ext",
112
+ skips2.length === 1 && skips2[0].metadata.ext === ".txt");
113
+ }
114
+
115
+ // ---- A gate that DOES match must NOT emit a skip audit ----
116
+ {
117
+ var emitted3 = [];
118
+ var fakeAudit3 = { safeEmit: function (e) { emitted3.push(e); } };
119
+ var u3 = b.fileUpload.create({
120
+ stagingDir: _tmpDir("matched"),
121
+ filenameSafety: null,
122
+ contentSafety: {
123
+ ".txt": { check: function () { return { ok: true, action: "serve" }; } },
124
+ },
125
+ audit: fakeAudit3,
126
+ });
127
+ emitted3.length = 0;
128
+ var m0 = Buffer.from("scanned body", "utf8");
129
+ await _uploadAndFinalize(u3, "u-matched", [m0],
130
+ { metadata: { filename: "scan.txt" } });
131
+ check("scan[matched]: gate ran → NO skip audit emitted",
132
+ _skipEvents(emitted3).length === 0);
133
+ }
134
+
135
+ // ---- Skip reason: streamed-over-reassembly-cap ----
136
+ // Force the streaming path by setting maxStreamReassemblyBytes below the
137
+ // upload size, while a gate IS registered for the extension.
138
+ {
139
+ var emitted4 = [];
140
+ var fakeAudit4 = { safeEmit: function (e) { emitted4.push(e); } };
141
+ var streamGateChecked = false;
142
+ var u4 = b.fileUpload.create({
143
+ stagingDir: _tmpDir("streamed"),
144
+ filenameSafety: null,
145
+ maxStreamReassemblyBytes: 4, // tiny — anything bigger streams
146
+ contentSafety: {
147
+ ".bin": { check: function () { streamGateChecked = true; return { ok: true, action: "serve" }; } },
148
+ },
149
+ audit: fakeAudit4,
150
+ onFinalize: async function (info) {
151
+ // Drain the stream so the readable doesn't leak open.
152
+ if (info.stream) { for await (var _c of info.stream) { void _c; } }
153
+ return { ok: true, sha3: info.sha3, size: info.size };
154
+ },
155
+ });
156
+ emitted4.length = 0;
157
+ var big0 = Buffer.from("0123456789", "utf8"); // 10 bytes > 4-byte cap
158
+ await _uploadAndFinalize(u4, "u-streamed", [big0],
159
+ { metadata: { filename: "blob.bin" } });
160
+ var skips4 = _skipEvents(emitted4);
161
+ check("skip[streamed]: gate could not run on a streamed body", streamGateChecked === false);
162
+ check("skip[streamed]: exactly one skip audit", skips4.length === 1);
163
+ check("skip[streamed]: reason names the reassembly cap",
164
+ skips4.length === 1 && skips4[0].reason === "streamed-over-reassembly-cap");
165
+ }
166
+
167
+ // ---- A throwing audit sink must NOT crash the upload ----
168
+ {
169
+ var u5 = b.fileUpload.create({
170
+ stagingDir: _tmpDir("throwing"),
171
+ contentSafety: null,
172
+ filenameSafety: null,
173
+ audit: { safeEmit: function () { throw new Error("audit sink down"); } },
174
+ });
175
+ var s0 = Buffer.from("survives a broken audit sink", "utf8");
176
+ var rv = null, threw = false;
177
+ try {
178
+ rv = await _uploadAndFinalize(u5, "u-throwing", [s0],
179
+ { metadata: { filename: "ok.txt" } });
180
+ } catch (_e) { threw = true; }
181
+ check("skip[throwing-sink]: upload finalized despite throwing audit sink",
182
+ threw === false && rv && rv.ok === true && rv.size === s0.length);
183
+ }
184
+ }
185
+
186
+ module.exports = { run: run };
187
+
188
+ if (require.main === module) {
189
+ run().then(
190
+ function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
191
+ function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
192
+ );
193
+ }
@@ -630,6 +630,94 @@ async function testGuardCsvGateAuditEmission() {
630
630
  /\.serve$/.test(emitted[0].action));
631
631
  }
632
632
 
633
+ async function testGateContractDefineGuard() {
634
+ // defineGuard assembles a full content-guard module from a spec: error
635
+ // class, registry exports, buildProfile / compliancePosture /
636
+ // loadRulePack wiring, the default gate, and the extras pass-through.
637
+ var GuardDemoError = b.gateContract.GateContractError;
638
+ var PROFILES = {
639
+ strict: { reject: true },
640
+ balanced: { reject: false },
641
+ permissive: { reject: false },
642
+ };
643
+ var POSTURES = b.gateContract.ALL_STRICT_POSTURES;
644
+ var mod = b.gateContract.defineGuard({
645
+ name: "demo",
646
+ kind: "content",
647
+ errorClass: GuardDemoError,
648
+ profiles: PROFILES,
649
+ defaults: PROFILES.strict,
650
+ postures: POSTURES,
651
+ mimeTypes: ["application/x-demo"],
652
+ extensions: [".demo"],
653
+ validate: function (text) {
654
+ if (text.indexOf("BAD") !== -1) {
655
+ return { ok: false, issues: [{ kind: "demo.bad", severity: "high", snippet: "BAD token" }] };
656
+ }
657
+ return { ok: true, issues: [] };
658
+ },
659
+ extra: { TOKENS: ["BAD"] },
660
+ });
661
+ check("defineGuard: NAME / KIND set", mod.NAME === "demo" && mod.KIND === "content");
662
+ check("defineGuard: MIME_TYPES / EXTENSIONS frozen",
663
+ Object.isFrozen(mod.MIME_TYPES) && mod.MIME_TYPES[0] === "application/x-demo" &&
664
+ mod.EXTENSIONS[0] === ".demo");
665
+ check("defineGuard: registry triplet assembled",
666
+ typeof mod.buildProfile === "function" &&
667
+ typeof mod.compliancePosture === "function" &&
668
+ typeof mod.loadRulePack === "function" &&
669
+ typeof mod.gate === "function" && typeof mod.validate === "function");
670
+ check("defineGuard: extras passed through", Array.isArray(mod.TOKENS) && mod.TOKENS[0] === "BAD");
671
+ check("defineGuard: compliancePosture clones overlay",
672
+ mod.compliancePosture("hipaa") === "strict" || typeof mod.compliancePosture("hipaa") === "object");
673
+ // Default gate runs the standard serve / refuse chain on ctx.bytes.
674
+ var g = mod.gate({ profile: "strict" });
675
+ var serve = await g.check({ bytes: Buffer.from("ok bytes") });
676
+ var refuse = await g.check({ bytes: Buffer.from("a BAD value") });
677
+ check("defineGuard: default gate serves clean bytes", serve.action === "serve");
678
+ check("defineGuard: default gate refuses on high issue", !refuse.ok && refuse.action === "refuse");
679
+ // Prototype-safe extras copy — __proto__ in spec.extra is dropped.
680
+ var poisoned = b.gateContract.defineGuard({
681
+ name: "demo2", kind: "filename", errorClass: GuardDemoError,
682
+ profiles: PROFILES, postures: POSTURES,
683
+ validate: function () { return { ok: true, issues: [] }; },
684
+ extra: JSON.parse('{"__proto__":{"polluted":true},"safe":1}'),
685
+ });
686
+ check("defineGuard: prototype-pollution key dropped from extras",
687
+ poisoned.safe === 1 && !({}).polluted);
688
+ }
689
+
690
+ async function testGateContractDefineParser() {
691
+ // defineParser assembles the minimal command / safe-* parser shape:
692
+ // the entry point, PROFILES, COMPLIANCE_POSTURES, a profile-name
693
+ // compliancePosture, and extras — no gate / buildProfile / loadRulePack.
694
+ var PROFILES = { strict: { cap: 1 }, balanced: { cap: 2 }, permissive: { cap: 3 } };
695
+ var mod = b.gateContract.defineParser({
696
+ name: "demo-command",
697
+ entry: function (line) { return { verb: String(line).toUpperCase() }; },
698
+ errorClass: b.gateContract.GateContractError,
699
+ profiles: PROFILES,
700
+ postures: b.gateContract.ALL_STRICT_POSTURES,
701
+ extra: { KNOWN: { NOOP: true } },
702
+ });
703
+ check("defineParser: entry exported as validate", typeof mod.validate === "function");
704
+ check("defineParser: entry runs", mod.validate("noop").verb === "NOOP");
705
+ check("defineParser: PROFILES / COMPLIANCE_POSTURES exported",
706
+ mod.PROFILES.strict.cap === 1 && mod.COMPLIANCE_POSTURES.hipaa === "strict");
707
+ check("defineParser: compliancePosture returns profile name",
708
+ mod.compliancePosture("hipaa") === "strict");
709
+ check("defineParser: compliancePosture('nope') → null", mod.compliancePosture("nope") === null);
710
+ check("defineParser: no gate / buildProfile / loadRulePack",
711
+ mod.gate === undefined && mod.buildProfile === undefined && mod.loadRulePack === undefined);
712
+ check("defineParser: extras passed through", mod.KNOWN.NOOP === true);
713
+ // Custom entryName.
714
+ var parserMod = b.gateContract.defineParser({
715
+ name: "demo-doc", entry: function () { return { ast: true }; }, entryName: "parse",
716
+ errorClass: b.gateContract.GateContractError, profiles: PROFILES,
717
+ });
718
+ check("defineParser: entryName overrides export key", typeof parserMod.parse === "function" && parserMod.validate === undefined);
719
+ }
720
+
633
721
  async function run() {
634
722
  // gateContract foundation
635
723
  testGateContractSurface();
@@ -647,6 +735,8 @@ async function run() {
647
735
  await testGateContractContentTypeMux();
648
736
  await testGateContractBuildProfile();
649
737
  await testGateContractBuildProfileCycleDetection();
738
+ await testGateContractDefineGuard();
739
+ await testGateContractDefineParser();
650
740
 
651
741
  // guardCsv surface + behavior
652
742
  testGuardCsvSurface();