@blamejs/blamejs-shop 0.5.12 → 0.5.14

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 (522) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/sales-tax-filings.js +143 -0
  4. package/lib/vendor/MANIFEST.json +632 -416
  5. package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +21 -10
  6. package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +30 -9
  7. package/lib/vendor/blamejs/.github/dependabot.yml +8 -0
  8. package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
  9. package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +7 -2
  10. package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +7 -2
  11. package/lib/vendor/blamejs/.github/workflows/ci.yml +21 -21
  12. package/lib/vendor/blamejs/.github/workflows/codeql.yml +3 -3
  13. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +4 -4
  14. package/lib/vendor/blamejs/.github/workflows/release-container.yml +7 -7
  15. package/lib/vendor/blamejs/.github/workflows/scorecard.yml +3 -3
  16. package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
  17. package/lib/vendor/blamejs/.gitignore +2 -0
  18. package/lib/vendor/blamejs/CHANGELOG.md +122 -0
  19. package/lib/vendor/blamejs/NOTICE +26 -1
  20. package/lib/vendor/blamejs/SECURITY.md +1 -1
  21. package/lib/vendor/blamejs/api-snapshot.json +207 -5
  22. package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +29 -0
  23. package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +1 -0
  24. package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +31 -0
  25. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +109 -0
  26. package/lib/vendor/blamejs/examples/wiki/nginx.conf +106 -0
  27. package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
  28. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +92 -0
  29. package/lib/vendor/blamejs/fuzz/asn1-der.fuzz.js +28 -0
  30. package/lib/vendor/blamejs/fuzz/cms-codec.fuzz.js +17 -0
  31. package/lib/vendor/blamejs/fuzz/link-header.fuzz.js +18 -0
  32. package/lib/vendor/blamejs/index.js +6 -0
  33. package/lib/vendor/blamejs/lib/a2a.js +4 -4
  34. package/lib/vendor/blamejs/lib/agent-idempotency.js +18 -2
  35. package/lib/vendor/blamejs/lib/agent-saga.js +36 -8
  36. package/lib/vendor/blamejs/lib/agent-snapshot.js +63 -6
  37. package/lib/vendor/blamejs/lib/app-shutdown.js +23 -20
  38. package/lib/vendor/blamejs/lib/archive-read.js +8 -0
  39. package/lib/vendor/blamejs/lib/archive-tar.js +12 -2
  40. package/lib/vendor/blamejs/lib/arg-parser.js +49 -5
  41. package/lib/vendor/blamejs/lib/asn1-der.js +12 -0
  42. package/lib/vendor/blamejs/lib/audit-sign.js +47 -1
  43. package/lib/vendor/blamejs/lib/audit.js +71 -8
  44. package/lib/vendor/blamejs/lib/auth/ciba.js +14 -2
  45. package/lib/vendor/blamejs/lib/auth/fido-mds3.js +58 -47
  46. package/lib/vendor/blamejs/lib/auth/jwt.js +34 -0
  47. package/lib/vendor/blamejs/lib/auth/oauth.js +90 -13
  48. package/lib/vendor/blamejs/lib/auth/oid4vci.js +20 -17
  49. package/lib/vendor/blamejs/lib/auth/openid-federation.js +214 -23
  50. package/lib/vendor/blamejs/lib/auth/saml.js +38 -20
  51. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +10 -4
  52. package/lib/vendor/blamejs/lib/backup/bundle.js +13 -3
  53. package/lib/vendor/blamejs/lib/backup/index.js +16 -5
  54. package/lib/vendor/blamejs/lib/backup/manifest.js +18 -5
  55. package/lib/vendor/blamejs/lib/base32.js +21 -0
  56. package/lib/vendor/blamejs/lib/break-glass.js +19 -10
  57. package/lib/vendor/blamejs/lib/bundler.js +5 -1
  58. package/lib/vendor/blamejs/lib/cache-status.js +3 -7
  59. package/lib/vendor/blamejs/lib/calendar.js +21 -1
  60. package/lib/vendor/blamejs/lib/cbor.js +12 -1
  61. package/lib/vendor/blamejs/lib/cert.js +29 -11
  62. package/lib/vendor/blamejs/lib/cli.js +94 -24
  63. package/lib/vendor/blamejs/lib/cloud-events.js +2 -2
  64. package/lib/vendor/blamejs/lib/cluster-provider-db.js +13 -3
  65. package/lib/vendor/blamejs/lib/codepoint-class.js +85 -0
  66. package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +8 -4
  67. package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +19 -4
  68. package/lib/vendor/blamejs/lib/compliance-ai-act.js +6 -8
  69. package/lib/vendor/blamejs/lib/content-credentials.js +1 -1
  70. package/lib/vendor/blamejs/lib/content-digest.js +2 -1
  71. package/lib/vendor/blamejs/lib/cookies.js +13 -3
  72. package/lib/vendor/blamejs/lib/cose.js +31 -16
  73. package/lib/vendor/blamejs/lib/crypto-field.js +32 -0
  74. package/lib/vendor/blamejs/lib/crypto-oprf.js +11 -2
  75. package/lib/vendor/blamejs/lib/crypto-xwing.js +22 -4
  76. package/lib/vendor/blamejs/lib/crypto.js +314 -19
  77. package/lib/vendor/blamejs/lib/csp.js +11 -3
  78. package/lib/vendor/blamejs/lib/daemon.js +270 -24
  79. package/lib/vendor/blamejs/lib/dark-patterns.js +5 -2
  80. package/lib/vendor/blamejs/lib/db-declare-view.js +2 -2
  81. package/lib/vendor/blamejs/lib/db-query.js +34 -7
  82. package/lib/vendor/blamejs/lib/db.js +18 -6
  83. package/lib/vendor/blamejs/lib/dbsc.js +17 -0
  84. package/lib/vendor/blamejs/lib/dev.js +21 -2
  85. package/lib/vendor/blamejs/lib/dsr.js +157 -102
  86. package/lib/vendor/blamejs/lib/external-db.js +41 -2
  87. package/lib/vendor/blamejs/lib/file-upload.js +25 -7
  88. package/lib/vendor/blamejs/lib/flag-providers.js +27 -15
  89. package/lib/vendor/blamejs/lib/forms.js +113 -27
  90. package/lib/vendor/blamejs/lib/gate-contract.js +23 -10
  91. package/lib/vendor/blamejs/lib/gdpr-ropa.js +8 -1
  92. package/lib/vendor/blamejs/lib/guard-agent-registry.js +1 -1
  93. package/lib/vendor/blamejs/lib/guard-archive.js +2 -2
  94. package/lib/vendor/blamejs/lib/guard-cidr.js +27 -1
  95. package/lib/vendor/blamejs/lib/guard-graphql.js +30 -11
  96. package/lib/vendor/blamejs/lib/guard-html.js +26 -53
  97. package/lib/vendor/blamejs/lib/guard-image.js +24 -5
  98. package/lib/vendor/blamejs/lib/guard-imap-command.js +10 -2
  99. package/lib/vendor/blamejs/lib/guard-jmap.js +19 -3
  100. package/lib/vendor/blamejs/lib/guard-jwt.js +18 -2
  101. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -2
  102. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +10 -2
  103. package/lib/vendor/blamejs/lib/guard-markdown.js +11 -24
  104. package/lib/vendor/blamejs/lib/guard-pdf.js +18 -2
  105. package/lib/vendor/blamejs/lib/guard-pop3-command.js +10 -2
  106. package/lib/vendor/blamejs/lib/guard-regex.js +8 -5
  107. package/lib/vendor/blamejs/lib/guard-svg.js +31 -32
  108. package/lib/vendor/blamejs/lib/guard-time.js +1 -1
  109. package/lib/vendor/blamejs/lib/guard-uuid.js +2 -2
  110. package/lib/vendor/blamejs/lib/html-balance.js +3 -3
  111. package/lib/vendor/blamejs/lib/http-client-cache.js +82 -4
  112. package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +16 -1
  113. package/lib/vendor/blamejs/lib/http-client.js +39 -7
  114. package/lib/vendor/blamejs/lib/http-message-signature.js +35 -1
  115. package/lib/vendor/blamejs/lib/i18n-messageformat.js +31 -6
  116. package/lib/vendor/blamejs/lib/i18n.js +35 -21
  117. package/lib/vendor/blamejs/lib/incident-report.js +8 -1
  118. package/lib/vendor/blamejs/lib/ip-utils.js +11 -1
  119. package/lib/vendor/blamejs/lib/json-path.js +23 -1
  120. package/lib/vendor/blamejs/lib/jwk.js +20 -3
  121. package/lib/vendor/blamejs/lib/link-header.js +2 -1
  122. package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +5 -1
  123. package/lib/vendor/blamejs/lib/log-stream-otlp.js +5 -1
  124. package/lib/vendor/blamejs/lib/log-stream.js +8 -3
  125. package/lib/vendor/blamejs/lib/mail-auth.js +123 -22
  126. package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
  127. package/lib/vendor/blamejs/lib/mail-bounce.js +8 -0
  128. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +46 -10
  129. package/lib/vendor/blamejs/lib/mail-dav.js +22 -3
  130. package/lib/vendor/blamejs/lib/mail-dkim.js +96 -30
  131. package/lib/vendor/blamejs/lib/mail-helo.js +6 -1
  132. package/lib/vendor/blamejs/lib/mail-scan.js +23 -16
  133. package/lib/vendor/blamejs/lib/mail-send-deliver.js +34 -8
  134. package/lib/vendor/blamejs/lib/mail-server-imap.js +3 -4
  135. package/lib/vendor/blamejs/lib/mail-server-managesieve.js +7 -11
  136. package/lib/vendor/blamejs/lib/mail-server-mx.js +93 -7
  137. package/lib/vendor/blamejs/lib/mail-server-registry.js +8 -2
  138. package/lib/vendor/blamejs/lib/mail.js +15 -5
  139. package/lib/vendor/blamejs/lib/metrics.js +268 -63
  140. package/lib/vendor/blamejs/lib/middleware/age-gate.js +7 -1
  141. package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +1 -1
  142. package/lib/vendor/blamejs/lib/middleware/body-parser.js +19 -4
  143. package/lib/vendor/blamejs/lib/middleware/bot-guard.js +5 -9
  144. package/lib/vendor/blamejs/lib/middleware/csp-report.js +40 -8
  145. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +24 -10
  146. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -5
  147. package/lib/vendor/blamejs/lib/middleware/index.js +6 -1
  148. package/lib/vendor/blamejs/lib/middleware/nel.js +1 -1
  149. package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +1 -1
  150. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +12 -1
  151. package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +1 -1
  152. package/lib/vendor/blamejs/lib/middleware/scim-server.js +1 -1
  153. package/lib/vendor/blamejs/lib/middleware/tus-upload.js +6 -2
  154. package/lib/vendor/blamejs/lib/mtls-ca.js +18 -2
  155. package/lib/vendor/blamejs/lib/network-dns-resolver.js +11 -1
  156. package/lib/vendor/blamejs/lib/network-dns.js +21 -2
  157. package/lib/vendor/blamejs/lib/network-tls.js +69 -7
  158. package/lib/vendor/blamejs/lib/nonce-store.js +9 -1
  159. package/lib/vendor/blamejs/lib/object-store/gcs.js +7 -0
  160. package/lib/vendor/blamejs/lib/object-store/sigv4.js +31 -0
  161. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +1 -1
  162. package/lib/vendor/blamejs/lib/outbox.js +1 -1
  163. package/lib/vendor/blamejs/lib/parsers/safe-ini.js +9 -1
  164. package/lib/vendor/blamejs/lib/parsers/safe-toml.js +10 -1
  165. package/lib/vendor/blamejs/lib/permissions.js +6 -3
  166. package/lib/vendor/blamejs/lib/pid-probe.js +55 -0
  167. package/lib/vendor/blamejs/lib/pqc-agent.js +8 -1
  168. package/lib/vendor/blamejs/lib/pqc-software.js +3 -3
  169. package/lib/vendor/blamejs/lib/pubsub.js +20 -9
  170. package/lib/vendor/blamejs/lib/queue-local.js +8 -1
  171. package/lib/vendor/blamejs/lib/queue-redis.js +5 -0
  172. package/lib/vendor/blamejs/lib/redact.js +54 -0
  173. package/lib/vendor/blamejs/lib/restore-bundle.js +10 -2
  174. package/lib/vendor/blamejs/lib/restore.js +19 -0
  175. package/lib/vendor/blamejs/lib/retention.js +12 -4
  176. package/lib/vendor/blamejs/lib/router.js +60 -6
  177. package/lib/vendor/blamejs/lib/safe-buffer.js +69 -0
  178. package/lib/vendor/blamejs/lib/safe-decompress.js +8 -2
  179. package/lib/vendor/blamejs/lib/safe-dns.js +25 -20
  180. package/lib/vendor/blamejs/lib/safe-ical.js +7 -3
  181. package/lib/vendor/blamejs/lib/safe-mime.js +12 -1
  182. package/lib/vendor/blamejs/lib/safe-object.js +80 -0
  183. package/lib/vendor/blamejs/lib/safe-path.js +52 -4
  184. package/lib/vendor/blamejs/lib/safe-redirect.js +10 -2
  185. package/lib/vendor/blamejs/lib/safe-schema.js +4 -1
  186. package/lib/vendor/blamejs/lib/safe-sieve.js +33 -1
  187. package/lib/vendor/blamejs/lib/safe-sql.js +88 -0
  188. package/lib/vendor/blamejs/lib/safe-vcard.js +7 -3
  189. package/lib/vendor/blamejs/lib/scheduler.js +8 -1
  190. package/lib/vendor/blamejs/lib/security-assert.js +6 -6
  191. package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +74 -27
  192. package/lib/vendor/blamejs/lib/self-update.js +517 -87
  193. package/lib/vendor/blamejs/lib/server-timing.js +2 -4
  194. package/lib/vendor/blamejs/lib/session.js +34 -12
  195. package/lib/vendor/blamejs/lib/sql.js +15 -1
  196. package/lib/vendor/blamejs/lib/ssrf-guard.js +52 -0
  197. package/lib/vendor/blamejs/lib/static.js +55 -13
  198. package/lib/vendor/blamejs/lib/storage.js +8 -2
  199. package/lib/vendor/blamejs/lib/subject.js +6 -1
  200. package/lib/vendor/blamejs/lib/tenant-quota.js +52 -13
  201. package/lib/vendor/blamejs/lib/tls-exporter.js +3 -3
  202. package/lib/vendor/blamejs/lib/tsa.js +34 -13
  203. package/lib/vendor/blamejs/lib/uri-template.js +8 -1
  204. package/lib/vendor/blamejs/lib/vault/index.js +13 -0
  205. package/lib/vendor/blamejs/lib/vc.js +44 -5
  206. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +26 -26
  207. package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +792 -1
  208. package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +3443 -1
  209. package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +2737 -1
  210. package/lib/vendor/blamejs/lib/vendor/pki.cjs +39550 -13
  211. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +55 -53
  212. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5551 -5554
  213. package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +22459 -32
  214. package/lib/vendor/blamejs/lib/watcher.js +89 -17
  215. package/lib/vendor/blamejs/lib/webhook-dispatcher.js +25 -3
  216. package/lib/vendor/blamejs/lib/webhook.js +12 -2
  217. package/lib/vendor/blamejs/lib/ws-client.js +32 -1
  218. package/lib/vendor/blamejs/lib/xml-c14n.js +29 -1
  219. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +1 -1
  220. package/lib/vendor/blamejs/package-lock.json +2 -2
  221. package/lib/vendor/blamejs/package.json +1 -1
  222. package/lib/vendor/blamejs/release-notes/v0.16.x.json +1462 -0
  223. package/lib/vendor/blamejs/release-notes/v0.17.0.json +31 -0
  224. package/lib/vendor/blamejs/release-notes/v0.17.1.json +31 -0
  225. package/lib/vendor/blamejs/release-notes/v0.17.10.json +30 -0
  226. package/lib/vendor/blamejs/release-notes/v0.17.11.json +22 -0
  227. package/lib/vendor/blamejs/release-notes/v0.17.12.json +18 -0
  228. package/lib/vendor/blamejs/release-notes/v0.17.13.json +77 -0
  229. package/lib/vendor/blamejs/release-notes/v0.17.14.json +40 -0
  230. package/lib/vendor/blamejs/release-notes/v0.17.15.json +18 -0
  231. package/lib/vendor/blamejs/release-notes/v0.17.16.json +27 -0
  232. package/lib/vendor/blamejs/release-notes/v0.17.17.json +31 -0
  233. package/lib/vendor/blamejs/release-notes/v0.17.18.json +18 -0
  234. package/lib/vendor/blamejs/release-notes/v0.17.19.json +18 -0
  235. package/lib/vendor/blamejs/release-notes/v0.17.2.json +26 -0
  236. package/lib/vendor/blamejs/release-notes/v0.17.20.json +43 -0
  237. package/lib/vendor/blamejs/release-notes/v0.17.21.json +26 -0
  238. package/lib/vendor/blamejs/release-notes/v0.17.22.json +18 -0
  239. package/lib/vendor/blamejs/release-notes/v0.17.3.json +43 -0
  240. package/lib/vendor/blamejs/release-notes/v0.17.4.json +35 -0
  241. package/lib/vendor/blamejs/release-notes/v0.17.5.json +26 -0
  242. package/lib/vendor/blamejs/release-notes/v0.17.6.json +26 -0
  243. package/lib/vendor/blamejs/release-notes/v0.17.7.json +35 -0
  244. package/lib/vendor/blamejs/release-notes/v0.17.8.json +22 -0
  245. package/lib/vendor/blamejs/release-notes/v0.17.9.json +26 -0
  246. package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +4 -0
  247. package/lib/vendor/blamejs/scripts/vendor-update.sh +293 -9
  248. package/lib/vendor/blamejs/socket.yml +94 -0
  249. package/lib/vendor/blamejs/test/00-primitives.js +605 -0
  250. package/lib/vendor/blamejs/test/helpers/drivers.js +12 -9
  251. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +90 -1
  252. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +122 -1
  253. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +30 -0
  254. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +29 -0
  255. package/lib/vendor/blamejs/test/integration/dsr-cluster-mysql.test.js +479 -0
  256. package/lib/vendor/blamejs/test/integration/dsr-cluster-pg.test.js +487 -0
  257. package/lib/vendor/blamejs/test/integration/federation-auth.test.js +313 -5
  258. package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +80 -0
  259. package/lib/vendor/blamejs/test/integration/openid-federation-chain.test.js +275 -0
  260. package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +96 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +401 -0
  262. package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +1649 -2
  263. package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +33 -0
  264. package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +60 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +524 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +891 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +128 -1
  269. package/lib/vendor/blamejs/test/layer-0-primitives/archive-adapters.test.js +271 -0
  270. package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +628 -1
  271. package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-read.test.js +0 -0
  272. package/lib/vendor/blamejs/test/layer-0-primitives/archive-wrap.test.js +227 -0
  273. package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +15 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +73 -0
  275. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file.test.js +114 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +141 -0
  277. package/lib/vendor/blamejs/test/layer-0-primitives/audit-tools.test.js +624 -0
  278. package/lib/vendor/blamejs/test/layer-0-primitives/audit.test.js +769 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +436 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth.test.js +1697 -0
  281. package/lib/vendor/blamejs/test/layer-0-primitives/auth-oid4vp.test.js +495 -0
  282. package/lib/vendor/blamejs/test/layer-0-primitives/auth-password.test.js +500 -0
  283. package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml.test.js +1809 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +243 -1
  285. package/lib/vendor/blamejs/test/layer-0-primitives/backup-index.test.js +2226 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +183 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/base32.test.js +105 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +693 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +603 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +557 -0
  291. package/lib/vendor/blamejs/test/layer-0-primitives/case-insensitive-security-token-guard.test.js +214 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/cbor.test.js +25 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +843 -6
  294. package/lib/vendor/blamejs/test/layer-0-primitives/ciba-authreqid-binding.test.js +77 -2
  295. package/lib/vendor/blamejs/test/layer-0-primitives/ciba.test.js +866 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +18 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +19 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/cli.test.js +2790 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/cluster-lease-renewal-ttl.test.js +114 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/cluster.test.js +1032 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +575 -0
  302. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +739 -1
  303. package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +8 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +194 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-lifecycle-proto-key.test.js +104 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +254 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +613 -3
  308. package/lib/vendor/blamejs/test/layer-0-primitives/cookies.test.js +140 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +407 -0
  310. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-adversarial.test.js +352 -0
  311. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +32 -0
  312. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field.test.js +575 -0
  313. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +10 -0
  314. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +24 -0
  315. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +17 -0
  316. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-spki-pin.test.js +183 -0
  317. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-xwing.test.js +48 -0
  318. package/lib/vendor/blamejs/test/layer-0-primitives/crypto.test.js +345 -0
  319. package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +58 -0
  320. package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +48 -0
  321. package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +459 -1
  322. package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +19 -0
  323. package/lib/vendor/blamejs/test/layer-0-primitives/db-query.test.js +1019 -0
  324. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +77 -0
  325. package/lib/vendor/blamejs/test/layer-0-primitives/db.test.js +1621 -0
  326. package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +462 -3
  327. package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +323 -0
  328. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-verify.test.js +562 -0
  329. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +901 -1
  330. package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +249 -0
  331. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +561 -0
  332. package/lib/vendor/blamejs/test/layer-0-primitives/external-db.test.js +1214 -0
  333. package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +21 -0
  334. package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +46 -20
  335. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +147 -3
  336. package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +907 -38
  337. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +29 -0
  338. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload.test.js +1135 -0
  339. package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +30 -0
  340. package/lib/vendor/blamejs/test/layer-0-primitives/forms.test.js +747 -0
  341. package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract.test.js +1855 -0
  342. package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +30 -0
  343. package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +86 -0
  344. package/lib/vendor/blamejs/test/layer-0-primitives/guard-auth.test.js +117 -0
  345. package/lib/vendor/blamejs/test/layer-0-primitives/guard-cidr.test.js +151 -0
  346. package/lib/vendor/blamejs/test/layer-0-primitives/guard-domain.test.js +422 -0
  347. package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +424 -0
  348. package/lib/vendor/blamejs/test/layer-0-primitives/{guard-gate-disposition-coverage.test.js → guard-gate-disposition.test.js} +1 -1
  349. package/lib/vendor/blamejs/test/layer-0-primitives/guard-graphql.test.js +340 -0
  350. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +29 -0
  351. package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +28 -0
  352. package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +519 -45
  353. package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
  354. package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +51 -0
  355. package/lib/vendor/blamejs/test/layer-0-primitives/guard-jsonpath.test.js +66 -0
  356. package/lib/vendor/blamejs/test/layer-0-primitives/guard-jwt.test.js +436 -0
  357. package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +25 -0
  358. package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +16 -0
  359. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mime.test.js +86 -0
  360. package/lib/vendor/blamejs/test/layer-0-primitives/guard-oauth.test.js +140 -0
  361. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +80 -0
  362. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +27 -0
  363. package/lib/vendor/blamejs/test/layer-0-primitives/guard-regex.test.js +99 -0
  364. package/lib/vendor/blamejs/test/layer-0-primitives/guard-shell.test.js +76 -0
  365. package/lib/vendor/blamejs/test/layer-0-primitives/{guard-sql-coverage.test.js → guard-sql.test.js} +3 -3
  366. package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +524 -0
  367. package/lib/vendor/blamejs/test/layer-0-primitives/guard-template.test.js +71 -0
  368. package/lib/vendor/blamejs/test/layer-0-primitives/guard-time.test.js +92 -0
  369. package/lib/vendor/blamejs/test/layer-0-primitives/guard-uuid.test.js +86 -0
  370. package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +38 -0
  371. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache-authorization.test.js +347 -0
  372. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +908 -0
  373. package/lib/vendor/blamejs/test/layer-0-primitives/http-client.test.js +2874 -0
  374. package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +643 -0
  375. package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +38 -0
  376. package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +1153 -1
  377. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +494 -0
  378. package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +30 -0
  379. package/lib/vendor/blamejs/test/layer-0-primitives/jsdoc-example-execution.test.js +177 -0
  380. package/lib/vendor/blamejs/test/layer-0-primitives/json-path.test.js +103 -0
  381. package/lib/vendor/blamejs/test/layer-0-primitives/json-schema.test.js +214 -0
  382. package/lib/vendor/blamejs/test/layer-0-primitives/jwk.test.js +41 -0
  383. package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +276 -0
  384. package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
  385. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream.test.js +241 -0
  386. package/lib/vendor/blamejs/test/layer-0-primitives/log.test.js +106 -0
  387. package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +441 -0
  388. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +2136 -4
  389. package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +759 -12
  390. package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +811 -1
  391. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +401 -3
  392. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +751 -8
  393. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +864 -0
  394. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto.test.js +110 -0
  395. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +830 -1
  396. package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +635 -86
  397. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +818 -0
  398. package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +27 -0
  399. package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +199 -0
  400. package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +686 -2
  401. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +778 -0
  402. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +1438 -0
  403. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +449 -6
  404. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +773 -2
  405. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +623 -3
  406. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +55 -0
  407. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +13 -0
  408. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +877 -1
  409. package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +326 -0
  410. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +55 -0
  411. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +242 -0
  412. package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +1596 -2
  413. package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +440 -4
  414. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +89 -0
  415. package/lib/vendor/blamejs/test/layer-0-primitives/metrics.test.js +1230 -0
  416. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-assetlinks.test.js +113 -0
  417. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-body-parser.test.js +1169 -0
  418. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-bot-disclose.test.js +123 -0
  419. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-cookies.test.js +108 -0
  420. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-gpc.test.js +121 -0
  421. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-headers.test.js +163 -0
  422. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-host-allowlist.test.js +154 -0
  423. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-require-content-type.test.js +127 -0
  424. package/lib/vendor/blamejs/test/layer-0-primitives/{middleware-tus-upload-coverage.test.js → middleware-tus-upload.test.js} +45 -0
  425. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-web-app-manifest.test.js +132 -0
  426. package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-crl-fingerprint-only.test.js +92 -0
  427. package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +65 -0
  428. package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +1727 -1
  429. package/lib/vendor/blamejs/test/layer-0-primitives/network-nts.test.js +699 -0
  430. package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy.test.js +1181 -0
  431. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +1876 -3
  432. package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +32 -0
  433. package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-enforce-replay-nonboolean.test.js +81 -0
  434. package/lib/vendor/blamejs/test/layer-0-primitives/ntp-check.test.js +122 -0
  435. package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +378 -0
  436. package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +77 -0
  437. package/lib/vendor/blamejs/test/layer-0-primitives/oid4vci.test.js +1016 -0
  438. package/lib/vendor/blamejs/test/layer-0-primitives/openid-federation.test.js +946 -0
  439. package/lib/vendor/blamejs/test/layer-0-primitives/outbox.test.js +818 -0
  440. package/lib/vendor/blamejs/test/layer-0-primitives/own-property-membership-guards.test.js +109 -0
  441. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-toml.test.js +543 -0
  442. package/lib/vendor/blamejs/test/layer-0-primitives/{parsers-safe-yaml-coverage.test.js → parsers-safe-yaml.test.js} +2 -2
  443. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +73 -0
  444. package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +649 -0
  445. package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +524 -0
  446. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +22 -0
  447. package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +295 -0
  448. package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +36 -0
  449. package/lib/vendor/blamejs/test/layer-0-primitives/queue.test.js +83 -0
  450. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +34 -0
  451. package/lib/vendor/blamejs/test/layer-0-primitives/redact.test.js +1031 -0
  452. package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +94 -0
  453. package/lib/vendor/blamejs/test/layer-0-primitives/require-bound-key.test.js +401 -0
  454. package/lib/vendor/blamejs/test/layer-0-primitives/restore-empty-manifest-wipe.test.js +154 -0
  455. package/lib/vendor/blamejs/test/layer-0-primitives/retention.test.js +600 -0
  456. package/lib/vendor/blamejs/test/layer-0-primitives/router-host-path-injection.test.js +231 -0
  457. package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +21 -0
  458. package/lib/vendor/blamejs/test/layer-0-primitives/router.test.js +1277 -0
  459. package/lib/vendor/blamejs/test/layer-0-primitives/safe-async.test.js +133 -0
  460. package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +14 -0
  461. package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer.test.js +80 -0
  462. package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +28 -0
  463. package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +245 -0
  464. package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +32 -0
  465. package/lib/vendor/blamejs/test/layer-0-primitives/safe-ini.test.js +376 -0
  466. package/lib/vendor/blamejs/test/layer-0-primitives/safe-json.test.js +175 -0
  467. package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +518 -0
  468. package/lib/vendor/blamejs/test/layer-0-primitives/safe-object.test.js +62 -0
  469. package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +30 -0
  470. package/lib/vendor/blamejs/test/layer-0-primitives/safe-schema.test.js +104 -0
  471. package/lib/vendor/blamejs/test/layer-0-primitives/safe-sql.test.js +142 -0
  472. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url.test.js +112 -0
  473. package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +26 -0
  474. package/lib/vendor/blamejs/test/layer-0-primitives/scheduler.test.js +157 -0
  475. package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +396 -0
  476. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +930 -0
  477. package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +755 -82
  478. package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +1053 -0
  479. package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +200 -0
  480. package/lib/vendor/blamejs/test/layer-0-primitives/session-strict-binding-missing-failclosed.test.js +110 -0
  481. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +636 -0
  482. package/lib/vendor/blamejs/test/layer-0-primitives/sql-offset-without-limit.test.js +94 -0
  483. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +1190 -0
  484. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +21 -0
  485. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +850 -0
  486. package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +146 -0
  487. package/lib/vendor/blamejs/test/layer-0-primitives/storage.test.js +621 -0
  488. package/lib/vendor/blamejs/test/layer-0-primitives/subject.test.js +47 -0
  489. package/lib/vendor/blamejs/test/layer-0-primitives/template.test.js +582 -0
  490. package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +587 -25
  491. package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +11 -0
  492. package/lib/vendor/blamejs/test/layer-0-primitives/tsa.test.js +524 -63
  493. package/lib/vendor/blamejs/test/layer-0-primitives/uri-template.test.js +27 -0
  494. package/lib/vendor/blamejs/test/layer-0-primitives/vault-rotate.test.js +866 -0
  495. package/lib/vendor/blamejs/test/layer-0-primitives/vault.test.js +74 -0
  496. package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +215 -0
  497. package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +550 -0
  498. package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +388 -0
  499. package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +262 -0
  500. package/lib/vendor/blamejs/test/layer-0-primitives/websocket.test.js +1294 -0
  501. package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +629 -0
  502. package/lib/vendor/blamejs/test/layer-0-primitives/xml-c14n-attr-normalization.test.js +164 -0
  503. package/lib/vendor/blamejs/test/smoke.js +2 -0
  504. package/package.json +1 -1
  505. package/lib/vendor/blamejs/release-notes/v0.16.0.json +0 -44
  506. package/lib/vendor/blamejs/release-notes/v0.16.1.json +0 -36
  507. package/lib/vendor/blamejs/release-notes/v0.16.2.json +0 -71
  508. package/lib/vendor/blamejs/release-notes/v0.16.3.json +0 -71
  509. package/lib/vendor/blamejs/test/layer-0-primitives/acme-coverage.test.js +0 -441
  510. package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth-coverage.test.js +0 -482
  511. package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml-coverage.test.js +0 -671
  512. package/lib/vendor/blamejs/test/layer-0-primitives/backup-index-coverage.test.js +0 -690
  513. package/lib/vendor/blamejs/test/layer-0-primitives/cli-coverage.test.js +0 -238
  514. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +0 -319
  515. package/lib/vendor/blamejs/test/layer-0-primitives/keychain-coverage.test.js +0 -0
  516. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth-coverage.test.js +0 -437
  517. package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +0 -443
  518. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +0 -395
  519. package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy-coverage.test.js +0 -565
  520. package/lib/vendor/blamejs/test/layer-0-primitives/router-coverage.test.js +0 -592
  521. package/lib/vendor/blamejs/test/layer-0-primitives/sql-coverage.test.js +0 -422
  522. package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +0 -573
@@ -0,0 +1,1135 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ /**
5
+ * file-upload — canonical per-primitive test file.
6
+ *
7
+ * Drives the b.fileUpload consumer path (create → init → acceptChunk →
8
+ * finalize → status/list/cancelUpload/purgeIncomplete) end-to-end, with a
9
+ * focus on the error / adversarial / defensive / option-default branches:
10
+ * create-time opt validation, path-traversal-hostile uploadIds, per-chunk
11
+ * hash + size + shape rejection, manifest verification failures, the
12
+ * magic-byte MIME allowlist + claim-vs-detected cross-check, filename- and
13
+ * content-safety gate refusal / throw / sanitize, the streaming reassembly
14
+ * path, quota exhaustion, idle / TTL expiry, and permission denial.
15
+ *
16
+ * Every gate is exercised through the REAL returned surface (no reaching
17
+ * past the public API except where a corrupt-on-disk sidecar is the
18
+ * scenario under test). Adversarial filenames / bytes assert refusal.
19
+ */
20
+ var nodeOs = require("node:os");
21
+ var nodePath = require("node:path");
22
+ var nodeFs = require("node:fs");
23
+ var nodeCrypto = require("node:crypto");
24
+ var helpers = require("../helpers");
25
+ var check = helpers.check;
26
+ var b = helpers.b;
27
+
28
+ // ---- shared fixtures ------------------------------------------------------
29
+
30
+ var _createdDirs = [];
31
+ function _tmpDir(suffix) {
32
+ var dir = nodePath.join(nodeOs.tmpdir(), "fileupload-canon-" + suffix + "-" +
33
+ nodeCrypto.randomBytes(6).toString("hex"));
34
+ nodeFs.mkdirSync(dir, { recursive: true, mode: 0o700 });
35
+ _createdDirs.push(dir);
36
+ return dir;
37
+ }
38
+ function _cleanupDirs() {
39
+ for (var i = 0; i < _createdDirs.length; i++) {
40
+ try { nodeFs.rmSync(_createdDirs[i], { recursive: true, force: true }); } catch (_e) { /* best-effort */ }
41
+ }
42
+ _createdDirs.length = 0;
43
+ }
44
+
45
+ // SHA3-512 hex — the framework's per-chunk + total hash. node's sha3-512
46
+ // digest matches b.crypto.sha3Hash, so the manifest the lib verifies against
47
+ // is computed the same way an operator's client would.
48
+ function _sha3(buf) { return nodeCrypto.createHash("sha3-512").update(buf).digest("hex"); }
49
+ function _fullSha3(pieces) {
50
+ var h = nodeCrypto.createHash("sha3-512");
51
+ for (var i = 0; i < pieces.length; i++) h.update(pieces[i]);
52
+ return h.digest("hex");
53
+ }
54
+ function _manifestFor(pieces) {
55
+ var total = 0;
56
+ for (var i = 0; i < pieces.length; i++) total += pieces[i].length;
57
+ return {
58
+ totalBytes: total,
59
+ sha3: _fullSha3(pieces),
60
+ chunks: pieces.map(function (p, idx) { return { index: idx, sha3: _sha3(p) }; }),
61
+ };
62
+ }
63
+ // init + push every chunk for `actor`, leaving the upload READY to finalize.
64
+ async function _seedUpload(u, uploadId, pieces, actor, metadata) {
65
+ await u.init({ uploadId: uploadId, actor: actor,
66
+ metadata: metadata || { filename: "doc.txt" } });
67
+ for (var i = 0; i < pieces.length; i++) {
68
+ await u.acceptChunk({ uploadId: uploadId, index: i, body: pieces[i],
69
+ sha3: _sha3(pieces[i]), actor: actor });
70
+ }
71
+ }
72
+ async function _expectThrows(fn) {
73
+ try { await fn(); return null; }
74
+ catch (e) { return e; }
75
+ }
76
+ // Drain a Readable so the streaming reassembly path never leaks an open fd.
77
+ async function _drain(stream) {
78
+ if (!stream) return 0;
79
+ var n = 0;
80
+ for await (var chunk of stream) { n += chunk.length; }
81
+ return n;
82
+ }
83
+
84
+ // PNG / PDF magic-byte fixtures for the fileType-sniff paths.
85
+ var PNG_BYTES = Buffer.concat([
86
+ Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
87
+ Buffer.from("IHDR-placeholder-body-bytes-for-a-valid-enough-sniff", "utf8"),
88
+ ]);
89
+ var PDF_BYTES = Buffer.from("%PDF-1.4\n1 0 obj<<>>endobj\ntrailer<<>>\n%%EOF\n", "utf8");
90
+
91
+ // ---- happy-path lifecycle -------------------------------------------------
92
+
93
+ async function testHappyLifecycle() {
94
+ var stagingDir = _tmpDir("happy");
95
+ var finalizeInfo = null;
96
+ var u = b.fileUpload.create({
97
+ stagingDir: stagingDir,
98
+ contentSafety: null,
99
+ filenameSafety: null,
100
+ onFinalize: async function (info) {
101
+ finalizeInfo = info;
102
+ return { ok: true, sha3: info.sha3, size: info.size, echoed: true };
103
+ },
104
+ });
105
+
106
+ var pieces = [Buffer.from("hello ", "utf8"), Buffer.from("world!", "utf8")];
107
+ var initRv = await u.init({ uploadId: "u-happy", actor: { id: "ada" },
108
+ metadata: { filename: "greeting.txt" } });
109
+ check("happy[init]: returns uploadId/createdAt/expiresAt",
110
+ initRv.uploadId === "u-happy" &&
111
+ typeof initRv.createdAt === "number" &&
112
+ initRv.expiresAt > initRv.createdAt);
113
+
114
+ var acc0 = await u.acceptChunk({ uploadId: "u-happy", index: 0, body: pieces[0],
115
+ sha3: _sha3(pieces[0]), actor: { id: "ada" } });
116
+ check("happy[accept]: first chunk received count is 1",
117
+ acc0.received === 1 && acc0.status === "in-progress" &&
118
+ acc0.totalBytesAccepted === pieces[0].length);
119
+ await u.acceptChunk({ uploadId: "u-happy", index: 1, body: pieces[1],
120
+ sha3: _sha3(pieces[1]), actor: { id: "ada" } });
121
+
122
+ // Idempotent re-PUT of the same (index, body) is a no-op flagged duplicate.
123
+ var dup = await u.acceptChunk({ uploadId: "u-happy", index: 0, body: pieces[0],
124
+ sha3: _sha3(pieces[0]), actor: { id: "ada" } });
125
+ check("happy[accept]: idempotent re-PUT flagged duplicate", dup.duplicate === true);
126
+
127
+ var manifest = _manifestFor(pieces);
128
+ var rv = await u.finalize({ uploadId: "u-happy", manifest: manifest, actor: { id: "ada" } });
129
+ check("happy[finalize]: onFinalize result returned",
130
+ rv.ok === true && rv.echoed === true && rv.size === manifest.totalBytes);
131
+ check("happy[finalize]: onFinalize got the reassembled buffer + total sha3",
132
+ Buffer.isBuffer(finalizeInfo.body) &&
133
+ finalizeInfo.body.toString("utf8") === "hello world!" &&
134
+ finalizeInfo.sha3 === manifest.sha3 &&
135
+ finalizeInfo.stream === null);
136
+ // Staging dir reclaimed on success.
137
+ check("happy[finalize]: staging dir removed on success",
138
+ nodeFs.existsSync(nodePath.join(stagingDir, "u-happy")) === false);
139
+ }
140
+
141
+ // Default onFinalize (operator supplies none) returns { ok, sha3, size }.
142
+ async function testDefaultOnFinalize() {
143
+ var u = b.fileUpload.create({
144
+ stagingDir: _tmpDir("default-final"), contentSafety: null, filenameSafety: null,
145
+ });
146
+ var pieces = [Buffer.from("no onFinalize wired", "utf8")];
147
+ await _seedUpload(u, "u-def", pieces, { id: "x" });
148
+ var rv = await u.finalize({ uploadId: "u-def", manifest: _manifestFor(pieces),
149
+ actor: { id: "x" } });
150
+ check("default-onFinalize: framework returns { ok, sha3, size }",
151
+ rv.ok === true && rv.size === pieces[0].length &&
152
+ rv.sha3 === _fullSha3(pieces));
153
+ }
154
+
155
+ // ---- status / list / cancel / purge ---------------------------------------
156
+
157
+ async function testStatusListCancel() {
158
+ var u = b.fileUpload.create({
159
+ stagingDir: _tmpDir("slc"), contentSafety: null, filenameSafety: null,
160
+ });
161
+ var pieces = [Buffer.from("status body", "utf8")];
162
+ await _seedUpload(u, "u-a", pieces, { id: "alice" }, { filename: "a.txt" });
163
+ await _seedUpload(u, "u-b", pieces, { id: "bob" }, { filename: "b.txt" });
164
+
165
+ var st = u.status("u-a", { actor: { id: "alice" } });
166
+ check("status: returns received indices + metadata for the owner",
167
+ st && st.uploadId === "u-a" && st.received.length === 1 &&
168
+ st.metadata.filename === "a.txt" && st.totalBytesAccepted === pieces[0].length);
169
+ check("status: unknown upload returns null",
170
+ u.status("u-missing", { actor: { id: "alice" } }) === null);
171
+
172
+ var mine = u.list({ actor: { id: "alice" } });
173
+ check("list: scoped to the calling actor by default",
174
+ mine.length === 1 && mine[0].uploadId === "u-a");
175
+ var all = u.list({ actor: { id: "alice" }, scopeToActor: false });
176
+ check("list: scopeToActor:false returns every actor's uploads", all.length === 2);
177
+ var sinceFuture = u.list({ actor: { id: "alice" }, since: Date.now() + 1e9 });
178
+ check("list: since filter excludes older uploads", sinceFuture.length === 0);
179
+
180
+ var cancelMissing = await u.cancelUpload("u-missing", { actor: { id: "alice" } });
181
+ check("cancel: unknown upload returns { ok:false, reason:'not-found' }",
182
+ cancelMissing.ok === false && cancelMissing.reason === "not-found");
183
+ var cancelled = await u.cancelUpload("u-a", { actor: { id: "alice" } });
184
+ check("cancel: owner cancel returns { ok:true }",
185
+ cancelled.ok === true && cancelled.uploadId === "u-a");
186
+ check("cancel: staging removed after cancel",
187
+ u.status("u-a", { actor: { id: "alice" } }) === null);
188
+
189
+ u.close(); // lifecycle parity — no-op, must not throw.
190
+ check("close: no-op close does not throw", true);
191
+ }
192
+
193
+ // A null-safeties manager — the plain lifecycle harness for the branches
194
+ // that aren't about the content/filename gates.
195
+ function _plainManager(suffix, extra) {
196
+ var opts = { stagingDir: _tmpDir(suffix), contentSafety: null, filenameSafety: null };
197
+ if (extra) for (var k in extra) opts[k] = extra[k];
198
+ return b.fileUpload.create(opts);
199
+ }
200
+
201
+ // ---- create() opt validation (config-time THROW tier) ---------------------
202
+
203
+ async function testCreateOptValidation() {
204
+ var abs = _tmpDir("optval");
205
+ var cases = [
206
+ ["stagingDir must be absolute", { stagingDir: "relative/dir" }],
207
+ ["allowedFileTypes without fileType is a misconfig",
208
+ { stagingDir: abs, contentSafety: null, filenameSafety: null, allowedFileTypes: ["image/png"] }],
209
+ ["contentSafety must be a plain object / null / undefined",
210
+ { stagingDir: abs, filenameSafety: null, contentSafety: 123 }],
211
+ ["contentSafety entry must be a gate",
212
+ { stagingDir: abs, filenameSafety: null, contentSafety: { ".csv": { notAGate: true } } }],
213
+ ["filenameSafety must be a gate",
214
+ { stagingDir: abs, contentSafety: null, filenameSafety: { notAGate: true } }],
215
+ ["fileType must expose detect()",
216
+ { stagingDir: abs, contentSafety: null, filenameSafety: null, fileType: { nope: 1 } }],
217
+ ["permissions must expose check()",
218
+ { stagingDir: abs, contentSafety: null, filenameSafety: null, permissions: { nope: 1 } }],
219
+ ["allowCrossActor must be a boolean",
220
+ { stagingDir: abs, contentSafety: null, filenameSafety: null, allowCrossActor: "yes" }],
221
+ ["unknown opt refused (exhaustive)",
222
+ { stagingDir: abs, contentSafety: null, filenameSafety: null, bogusOpt: 1 }],
223
+ ];
224
+ for (var i = 0; i < cases.length; i++) {
225
+ var label = cases[i][0], opts = cases[i][1];
226
+ var err = (function (o) { try { b.fileUpload.create(o); return null; } catch (e) { return e; } })(opts);
227
+ check("create-opt: " + label,
228
+ err !== null && err.code === "BAD_OPT" && err.name === "FileUploadError");
229
+ }
230
+ }
231
+
232
+ // contentSafety:null / filenameSafety:null emit an audited disable row whose
233
+ // reason defaults to operator-explicit-opt-out, or the operator's own reason.
234
+ async function testAuditedOptOut() {
235
+ var defaults = [];
236
+ b.fileUpload.create({
237
+ stagingDir: _tmpDir("optout-default"), contentSafety: null, filenameSafety: null,
238
+ audit: { safeEmit: function (e) { defaults.push(e); } },
239
+ });
240
+ var cs = defaults.filter(function (e) { return e.action === "fileUpload.contentSafety.disabled"; });
241
+ var fs2 = defaults.filter(function (e) { return e.action === "fileUpload.filenameSafety.disabled"; });
242
+ check("opt-out audit: content-safety disable row with default reason",
243
+ cs.length === 1 && cs[0].metadata.reason === "operator-explicit-opt-out" &&
244
+ cs[0].outcome === "success");
245
+ check("opt-out audit: filename-safety disable row with default reason",
246
+ fs2.length === 1 && fs2[0].metadata.reason === "operator-explicit-opt-out");
247
+
248
+ var custom = [];
249
+ b.fileUpload.create({
250
+ stagingDir: _tmpDir("optout-custom"), contentSafety: null, filenameSafety: null,
251
+ contentSafetyDisabledReason: "legacy-import-path",
252
+ filenameSafetyDisabledReason: "operator-accepts-unicode-names",
253
+ audit: { safeEmit: function (e) { custom.push(e); } },
254
+ });
255
+ check("opt-out audit: operator-supplied reasons are recorded",
256
+ custom.some(function (e) { return e.action === "fileUpload.contentSafety.disabled" &&
257
+ e.metadata.reason === "legacy-import-path"; }) &&
258
+ custom.some(function (e) { return e.action === "fileUpload.filenameSafety.disabled" &&
259
+ e.metadata.reason === "operator-accepts-unicode-names"; }));
260
+
261
+ // A throwing audit sink during the disable-audit must not break create().
262
+ var threw = false;
263
+ try {
264
+ b.fileUpload.create({
265
+ stagingDir: _tmpDir("optout-throw"), contentSafety: null, filenameSafety: null,
266
+ audit: { safeEmit: function () { throw new Error("sink down"); } },
267
+ });
268
+ } catch (_e) { threw = true; }
269
+ check("opt-out audit: throwing sink does not break create()", threw === false);
270
+ }
271
+
272
+ // ---- path-traversal-hostile uploadIds --------------------------------------
273
+
274
+ async function testUploadIdValidation() {
275
+ var u = _plainManager("uploadid");
276
+ var hostile = ["", "a".repeat(129), ".", "..", "a/b", "a\\b", "a\0b", "a*b",
277
+ "with space", "a?b", 123, null];
278
+ for (var i = 0; i < hostile.length; i++) {
279
+ var id = hostile[i];
280
+ var err = await _expectThrows(function () {
281
+ return u.init({ uploadId: id, actor: { id: "x" }, metadata: {} });
282
+ });
283
+ check("uploadId refused: " + JSON.stringify(id),
284
+ err !== null && err.code === "BAD_UPLOAD_ID");
285
+ }
286
+ // A well-formed id in the allowed char class is accepted.
287
+ var okRv = await u.init({ uploadId: "OK.file-1_2", actor: { id: "x" }, metadata: {} });
288
+ check("uploadId accepted: dotted/underscored/hyphenated id", okRv.uploadId === "OK.file-1_2");
289
+ }
290
+
291
+ // ---- init() branches -------------------------------------------------------
292
+
293
+ async function testInitBranches() {
294
+ var u = _plainManager("init");
295
+ check("init: null callerOpts throws BAD_OPT",
296
+ (await _expectThrows(function () { return u.init(null); })).code === "BAD_OPT");
297
+
298
+ var arrErr = await _expectThrows(function () {
299
+ return u.init({ uploadId: "u-meta-arr", actor: { id: "x" }, metadata: [1, 2, 3] });
300
+ });
301
+ check("init: array metadata → BAD_METADATA", arrErr.code === "BAD_METADATA");
302
+ var strErr = await _expectThrows(function () {
303
+ return u.init({ uploadId: "u-meta-str", actor: { id: "x" }, metadata: "not-an-object" });
304
+ });
305
+ check("init: string metadata → BAD_METADATA", strErr.code === "BAD_METADATA");
306
+
307
+ var bigErr = await _expectThrows(function () {
308
+ return u.init({ uploadId: "u-meta-big", actor: { id: "x" },
309
+ metadata: { blob: "x".repeat(70000) } });
310
+ });
311
+ check("init: oversized metadata → METADATA_TOO_LARGE", bigErr.code === "METADATA_TOO_LARGE");
312
+
313
+ await u.init({ uploadId: "u-dupe", actor: { id: "x" }, metadata: {} });
314
+ var dupeErr = await _expectThrows(function () {
315
+ return u.init({ uploadId: "u-dupe", actor: { id: "x" }, metadata: {} });
316
+ });
317
+ check("init: re-init of an existing upload → UPLOAD_EXISTS", dupeErr.code === "UPLOAD_EXISTS");
318
+
319
+ // Per-actor active-upload quota.
320
+ var q = _plainManager("init-actorq", { maxActiveUploadsPerActor: 1 });
321
+ await q.init({ uploadId: "q-1", actor: { id: "heavy" }, metadata: {} });
322
+ var qErr = await _expectThrows(function () {
323
+ return q.init({ uploadId: "q-2", actor: { id: "heavy" }, metadata: {} });
324
+ });
325
+ check("init: exceeding maxActiveUploadsPerActor → ACTOR_QUOTA_EXCEEDED",
326
+ qErr.code === "ACTOR_QUOTA_EXCEEDED");
327
+
328
+ // Total staging-bytes quota (a prior upload has accepted bytes).
329
+ var s = _plainManager("init-stagingq", { maxStagingBytes: 1 });
330
+ await _seedUpload(s, "s-1", [Buffer.from("some bytes on disk", "utf8")], { id: "a" });
331
+ var sErr = await _expectThrows(function () {
332
+ return s.init({ uploadId: "s-2", actor: { id: "b" }, metadata: {} });
333
+ });
334
+ check("init: exceeding maxStagingBytes → STAGING_QUOTA_EXCEEDED",
335
+ sErr.code === "STAGING_QUOTA_EXCEEDED");
336
+ }
337
+
338
+ // ---- acceptChunk() branches ------------------------------------------------
339
+
340
+ async function testAcceptChunkBranches() {
341
+ var u = _plainManager("accept");
342
+ await u.init({ uploadId: "u-ac", actor: { id: "x" }, metadata: {} });
343
+ var good = Buffer.from("a valid chunk body", "utf8");
344
+
345
+ check("acceptChunk: null callerOpts → BAD_OPT",
346
+ (await _expectThrows(function () { return u.acceptChunk(null); })).code === "BAD_OPT");
347
+
348
+ var unknownErr = await _expectThrows(function () {
349
+ return u.acceptChunk({ uploadId: "never-init", index: 0, body: good,
350
+ sha3: _sha3(good), actor: { id: "x" } });
351
+ });
352
+ check("acceptChunk: no init() → UNKNOWN_UPLOAD", unknownErr.code === "UNKNOWN_UPLOAD");
353
+
354
+ var badIdx = [-1, 1.5, 0x40000000];
355
+ for (var i = 0; i < badIdx.length; i++) {
356
+ var bErr = await (function (ix) {
357
+ return _expectThrows(function () {
358
+ return u.acceptChunk({ uploadId: "u-ac", index: ix, body: good,
359
+ sha3: _sha3(good), actor: { id: "x" } });
360
+ });
361
+ })(badIdx[i]);
362
+ check("acceptChunk: bad index " + badIdx[i] + " → BAD_INDEX", bErr.code === "BAD_INDEX");
363
+ }
364
+
365
+ var notBufErr = await _expectThrows(function () {
366
+ return u.acceptChunk({ uploadId: "u-ac", index: 0, body: "a string not a buffer",
367
+ sha3: _sha3(good), actor: { id: "x" } });
368
+ });
369
+ check("acceptChunk: non-Buffer body → BAD_BODY", notBufErr.code === "BAD_BODY");
370
+
371
+ var emptyErr = await _expectThrows(function () {
372
+ return u.acceptChunk({ uploadId: "u-ac", index: 0, body: Buffer.alloc(0),
373
+ sha3: _sha3(Buffer.alloc(0)), actor: { id: "x" } });
374
+ });
375
+ check("acceptChunk: empty chunk → EMPTY_CHUNK", emptyErr.code === "EMPTY_CHUNK");
376
+
377
+ var big = _plainManager("accept-big", { maxChunkBytes: 4 });
378
+ await big.init({ uploadId: "u-big", actor: { id: "x" }, metadata: {} });
379
+ var over = Buffer.from("way too many bytes", "utf8");
380
+ var bigErr = await _expectThrows(function () {
381
+ return big.acceptChunk({ uploadId: "u-big", index: 0, body: over,
382
+ sha3: _sha3(over), actor: { id: "x" } });
383
+ });
384
+ check("acceptChunk: chunk over maxChunkBytes → CHUNK_TOO_LARGE", bigErr.code === "CHUNK_TOO_LARGE");
385
+
386
+ var notHexErr = await _expectThrows(function () {
387
+ return u.acceptChunk({ uploadId: "u-ac", index: 0, body: good,
388
+ sha3: "zz-not-hex", actor: { id: "x" } });
389
+ });
390
+ check("acceptChunk: non-hex sha3 → BAD_CHUNK_HASH", notHexErr.code === "BAD_CHUNK_HASH");
391
+ var shortHexErr = await _expectThrows(function () {
392
+ return u.acceptChunk({ uploadId: "u-ac", index: 0, body: good,
393
+ sha3: "abcdef", actor: { id: "x" } });
394
+ });
395
+ check("acceptChunk: wrong-length sha3 → BAD_CHUNK_HASH", shortHexErr.code === "BAD_CHUNK_HASH");
396
+
397
+ var wrongHash = _sha3(Buffer.from("a different body entirely", "utf8"));
398
+ var mismatchErr = await _expectThrows(function () {
399
+ return u.acceptChunk({ uploadId: "u-ac", index: 0, body: good,
400
+ sha3: wrongHash, actor: { id: "x" } });
401
+ });
402
+ check("acceptChunk: supplied hash != computed → CHUNK_HASH_MISMATCH",
403
+ mismatchErr.code === "CHUNK_HASH_MISMATCH");
404
+
405
+ // onChunk hook rejection propagates the operator's error verbatim.
406
+ var hookMgr = _plainManager("accept-hook", {
407
+ onChunk: async function () { throw new Error("virus scanner said no"); },
408
+ });
409
+ await hookMgr.init({ uploadId: "u-hook", actor: { id: "x" }, metadata: {} });
410
+ var hookErr = await _expectThrows(function () {
411
+ return hookMgr.acceptChunk({ uploadId: "u-hook", index: 0, body: good,
412
+ sha3: _sha3(good), actor: { id: "x" } });
413
+ });
414
+ check("acceptChunk: onChunk throw propagates the operator error",
415
+ hookErr !== null && hookErr.message === "virus scanner said no");
416
+
417
+ // Re-PUT same index with a DIFFERENT body is refused.
418
+ await u.acceptChunk({ uploadId: "u-ac", index: 0, body: good, sha3: _sha3(good), actor: { id: "x" } });
419
+ var other = Buffer.from("totally different content", "utf8");
420
+ var reuseErr = await _expectThrows(function () {
421
+ return u.acceptChunk({ uploadId: "u-ac", index: 0, body: other,
422
+ sha3: _sha3(other), actor: { id: "x" } });
423
+ });
424
+ check("acceptChunk: same index, different body → CHUNK_REUSE_MISMATCH",
425
+ reuseErr.code === "CHUNK_REUSE_MISMATCH");
426
+
427
+ // Cumulative file-size cap trips mid-stream and reclaims staging.
428
+ var cap = _plainManager("accept-filecap", { maxFileBytes: 5 });
429
+ await cap.init({ uploadId: "u-cap", actor: { id: "x" }, metadata: {} });
430
+ var tenBytes = Buffer.from("0123456789", "utf8");
431
+ var capErr = await _expectThrows(function () {
432
+ return cap.acceptChunk({ uploadId: "u-cap", index: 0, body: tenBytes,
433
+ sha3: _sha3(tenBytes), actor: { id: "x" } });
434
+ });
435
+ check("acceptChunk: cumulative bytes over maxFileBytes → FILE_TOO_LARGE",
436
+ capErr.code === "FILE_TOO_LARGE");
437
+ check("acceptChunk: staging reclaimed after FILE_TOO_LARGE",
438
+ cap.status("u-cap", { actor: { id: "x" } }) === null);
439
+
440
+ // Idle timeout via an injected clock.
441
+ var now = 1000000;
442
+ var idle = _plainManager("accept-idle", { maxIdleMs: 500, clock: function () { return now; } });
443
+ await idle.init({ uploadId: "u-idle", actor: { id: "x" }, metadata: {} });
444
+ now += 2000; // > maxIdleMs since init/last chunk
445
+ var idleErr = await _expectThrows(function () {
446
+ return idle.acceptChunk({ uploadId: "u-idle", index: 0, body: good,
447
+ sha3: _sha3(good), actor: { id: "x" } });
448
+ });
449
+ check("acceptChunk: idle past maxIdleMs → UPLOAD_IDLE_EXPIRED", idleErr.code === "UPLOAD_IDLE_EXPIRED");
450
+ }
451
+
452
+ // ---- finalize() manifest verification --------------------------------------
453
+
454
+ async function testFinalizeManifestBranches() {
455
+ var A = Buffer.from("chunk-A-content", "utf8");
456
+ var B = Buffer.from("chunk-B-never-uploaded", "utf8");
457
+
458
+ var u = _plainManager("fin-man");
459
+ await _seedUpload(u, "u-man", [A], { id: "x" });
460
+
461
+ check("finalize: null callerOpts → BAD_OPT",
462
+ (await _expectThrows(function () { return u.finalize(null); })).code === "BAD_OPT");
463
+ check("finalize: unknown upload → UNKNOWN_UPLOAD",
464
+ (await _expectThrows(function () {
465
+ return u.finalize({ uploadId: "no-such", manifest: _manifestFor([A]), actor: { id: "x" } });
466
+ })).code === "UNKNOWN_UPLOAD");
467
+ check("finalize: non-object manifest → BAD_OPT",
468
+ (await _expectThrows(function () {
469
+ return u.finalize({ uploadId: "u-man", manifest: 42, actor: { id: "x" } });
470
+ })).code === "BAD_OPT");
471
+ check("finalize: empty chunks array → BAD_MANIFEST",
472
+ (await _expectThrows(function () {
473
+ return u.finalize({ uploadId: "u-man",
474
+ manifest: { chunks: [], totalBytes: 1, sha3: _sha3(A) }, actor: { id: "x" } });
475
+ })).code === "BAD_MANIFEST");
476
+ check("finalize: non-array chunks → BAD_MANIFEST",
477
+ (await _expectThrows(function () {
478
+ return u.finalize({ uploadId: "u-man",
479
+ manifest: { chunks: "nope", totalBytes: 1, sha3: _sha3(A) }, actor: { id: "x" } });
480
+ })).code === "BAD_MANIFEST");
481
+ check("finalize: non-positive totalBytes → BAD_MANIFEST",
482
+ (await _expectThrows(function () {
483
+ return u.finalize({ uploadId: "u-man",
484
+ manifest: { chunks: [{ index: 0, sha3: _sha3(A) }], totalBytes: 0, sha3: _sha3(A) },
485
+ actor: { id: "x" } });
486
+ })).code === "BAD_MANIFEST");
487
+ check("finalize: non-hex manifest.sha3 → BAD_MANIFEST",
488
+ (await _expectThrows(function () {
489
+ return u.finalize({ uploadId: "u-man",
490
+ manifest: { chunks: [{ index: 0, sha3: _sha3(A) }], totalBytes: A.length, sha3: "nothex" },
491
+ actor: { id: "x" } });
492
+ })).code === "BAD_MANIFEST");
493
+
494
+ // manifest.totalBytes over maxFileBytes (checked before chunk verification,
495
+ // so the seeded chunk just needs to fit the cap).
496
+ var tiny = Buffer.from("t", "utf8");
497
+ var capMgr = _plainManager("fin-cap", { maxFileBytes: 5 });
498
+ await _seedUpload(capMgr, "u-fcap", [tiny], { id: "x" });
499
+ check("finalize: manifest.totalBytes over maxFileBytes → FILE_TOO_LARGE",
500
+ (await _expectThrows(function () {
501
+ return capMgr.finalize({ uploadId: "u-fcap",
502
+ manifest: { chunks: [{ index: 0, sha3: _sha3(tiny) }], totalBytes: 100, sha3: _sha3(tiny) },
503
+ actor: { id: "x" } });
504
+ })).code === "FILE_TOO_LARGE");
505
+
506
+ // Too many declared chunks.
507
+ var mcMgr = _plainManager("fin-maxchunks", { maxChunks: 1 });
508
+ await _seedUpload(mcMgr, "u-mc", [A], { id: "x" });
509
+ check("finalize: chunks over maxChunks → TOO_MANY_CHUNKS",
510
+ (await _expectThrows(function () {
511
+ return mcMgr.finalize({ uploadId: "u-mc",
512
+ manifest: { chunks: [{ index: 0, sha3: _sha3(A) }, { index: 1, sha3: _sha3(B) }],
513
+ totalBytes: A.length + B.length, sha3: _fullSha3([A, B]) },
514
+ actor: { id: "x" } });
515
+ })).code === "TOO_MANY_CHUNKS");
516
+
517
+ // Non-contiguous manifest indices (0, 2 → gap at position 1).
518
+ check("finalize: non-contiguous chunk index → MANIFEST_INDEX_GAP",
519
+ (await _expectThrows(function () {
520
+ return u.finalize({ uploadId: "u-man",
521
+ manifest: { chunks: [{ index: 0, sha3: _sha3(A) }, { index: 2, sha3: _sha3(B) }],
522
+ totalBytes: A.length + B.length, sha3: _fullSha3([A, B]) },
523
+ actor: { id: "x" } });
524
+ })).code === "MANIFEST_INDEX_GAP");
525
+
526
+ // A manifest chunk with a non-hex sha3.
527
+ check("finalize: non-hex chunk.sha3 → BAD_MANIFEST",
528
+ (await _expectThrows(function () {
529
+ return u.finalize({ uploadId: "u-man",
530
+ manifest: { chunks: [{ index: 0, sha3: "not-hex-at-all" }],
531
+ totalBytes: A.length, sha3: _sha3(A) },
532
+ actor: { id: "x" } });
533
+ })).code === "BAD_MANIFEST");
534
+
535
+ // A chunk declared but never written to staging.
536
+ check("finalize: chunk missing from staging → MISSING_CHUNK",
537
+ (await _expectThrows(function () {
538
+ return u.finalize({ uploadId: "u-man",
539
+ manifest: { chunks: [{ index: 0, sha3: _sha3(A) }, { index: 1, sha3: _sha3(B) }],
540
+ totalBytes: A.length + B.length, sha3: _fullSha3([A, B]) },
541
+ actor: { id: "x" } });
542
+ })).code === "MISSING_CHUNK");
543
+
544
+ // On-disk chunk hash disagrees with the manifest's per-chunk sha3.
545
+ check("finalize: on-disk chunk hash != manifest → CHUNK_HASH_MISMATCH",
546
+ (await _expectThrows(function () {
547
+ return u.finalize({ uploadId: "u-man",
548
+ manifest: { chunks: [{ index: 0, sha3: _sha3(B) }], totalBytes: A.length, sha3: _sha3(A) },
549
+ actor: { id: "x" } });
550
+ })).code === "CHUNK_HASH_MISMATCH");
551
+
552
+ // Reassembled size disagrees with manifest.totalBytes.
553
+ check("finalize: reassembled size != manifest.totalBytes → MANIFEST_SIZE_MISMATCH",
554
+ (await _expectThrows(function () {
555
+ return u.finalize({ uploadId: "u-man",
556
+ manifest: { chunks: [{ index: 0, sha3: _sha3(A) }], totalBytes: A.length + 100,
557
+ sha3: _sha3(A) },
558
+ actor: { id: "x" } });
559
+ })).code === "MANIFEST_SIZE_MISMATCH");
560
+
561
+ // Reassembled total hash disagrees with manifest.sha3.
562
+ check("finalize: reassembled total hash != manifest.sha3 → MANIFEST_HASH_MISMATCH",
563
+ (await _expectThrows(function () {
564
+ return u.finalize({ uploadId: "u-man",
565
+ manifest: { chunks: [{ index: 0, sha3: _sha3(A) }], totalBytes: A.length,
566
+ sha3: _sha3(B) },
567
+ actor: { id: "x" } });
568
+ })).code === "MANIFEST_HASH_MISMATCH");
569
+ }
570
+
571
+ // ---- MIME magic-byte allowlist + claim cross-check -------------------------
572
+
573
+ async function testMimeGate() {
574
+ // Exact allow.
575
+ var exact = _plainManager("mime-exact", { allowedFileTypes: ["image/png"], fileType: b.fileType });
576
+ await _seedUpload(exact, "m-png", [PNG_BYTES], { id: "x" }, { filename: "pic.png" });
577
+ var okRv = await exact.finalize({ uploadId: "m-png", manifest: _manifestFor([PNG_BYTES]),
578
+ actor: { id: "x" } });
579
+ check("mime: allowed exact type finalizes", okRv.ok === true);
580
+
581
+ // Wildcard allow.
582
+ var wild = _plainManager("mime-wild", { allowedFileTypes: ["image/*"], fileType: b.fileType });
583
+ await _seedUpload(wild, "m-wild", [PNG_BYTES], { id: "x" }, { filename: "pic.png" });
584
+ check("mime: wildcard image/* accepts image/png",
585
+ (await wild.finalize({ uploadId: "m-wild", manifest: _manifestFor([PNG_BYTES]),
586
+ actor: { id: "x" } })).ok === true);
587
+
588
+ // Not in the allowlist.
589
+ var deny = _plainManager("mime-deny", { allowedFileTypes: ["application/pdf"], fileType: b.fileType });
590
+ await _seedUpload(deny, "m-deny", [PNG_BYTES], { id: "x" }, { filename: "pic.png" });
591
+ check("mime: detected type not in allowlist → MIME_NOT_ALLOWED",
592
+ (await _expectThrows(function () {
593
+ return deny.finalize({ uploadId: "m-deny", manifest: _manifestFor([PNG_BYTES]), actor: { id: "x" } });
594
+ })).code === "MIME_NOT_ALLOWED");
595
+
596
+ // Unclassifiable bytes.
597
+ var undet = _plainManager("mime-undet", { allowedFileTypes: ["image/png"], fileType: b.fileType });
598
+ var plain = [Buffer.from("just some ascii text, no magic bytes", "utf8")];
599
+ await _seedUpload(undet, "m-undet", plain, { id: "x" }, { filename: "note.png" });
600
+ check("mime: unclassifiable magic bytes → MIME_NOT_DETECTED",
601
+ (await _expectThrows(function () {
602
+ return undet.finalize({ uploadId: "m-undet", manifest: _manifestFor(plain), actor: { id: "x" } });
603
+ })).code === "MIME_NOT_DETECTED");
604
+
605
+ // Claimed Content-Type family disagrees with detected magic-byte family.
606
+ var claim = _plainManager("mime-claim", { allowedFileTypes: ["application/pdf"], fileType: b.fileType });
607
+ await _seedUpload(claim, "m-claim", [PDF_BYTES], { id: "x" },
608
+ { filename: "doc.pdf", contentType: "image/png" });
609
+ check("mime: claimed family != detected family → MIME_CLAIM_MISMATCH",
610
+ (await _expectThrows(function () {
611
+ return claim.finalize({ uploadId: "m-claim", manifest: _manifestFor([PDF_BYTES]), actor: { id: "x" } });
612
+ })).code === "MIME_CLAIM_MISMATCH");
613
+
614
+ // Same-family claim with a charset param + mixed case is accepted (synonym).
615
+ var syn = _plainManager("mime-syn", { allowedFileTypes: ["image/png"], fileType: b.fileType });
616
+ await _seedUpload(syn, "m-syn", [PNG_BYTES], { id: "x" },
617
+ { filename: "pic.png", contentType: "IMAGE/PNG; charset=binary" });
618
+ check("mime: same-family claim with param/case is accepted",
619
+ (await syn.finalize({ uploadId: "m-syn", manifest: _manifestFor([PNG_BYTES]), actor: { id: "x" } })).ok === true);
620
+ }
621
+
622
+ // ---- filename-safety gate (refuse / throw / sanitize) ----------------------
623
+
624
+ async function testFilenameSafety() {
625
+ // The default-on strict guard refuses a shell-exec extension.
626
+ var strict = b.fileUpload.create({ stagingDir: _tmpDir("fn-strict"), contentSafety: null });
627
+ await _seedUpload(strict, "f-exe", [Buffer.from("MZ payload", "utf8")], { id: "x" },
628
+ { filename: "evil.exe" });
629
+ check("filename: default strict guard refuses evil.exe → FILENAME_SAFETY_REFUSED",
630
+ (await _expectThrows(function () {
631
+ return strict.finalize({ uploadId: "f-exe",
632
+ manifest: _manifestFor([Buffer.from("MZ payload", "utf8")]), actor: { id: "x" } });
633
+ })).code === "FILENAME_SAFETY_REFUSED");
634
+
635
+ var strict2 = b.fileUpload.create({ stagingDir: _tmpDir("fn-strict2"), contentSafety: null });
636
+ await _seedUpload(strict2, "f-trav", [Buffer.from("body", "utf8")], { id: "x" },
637
+ { filename: "../../etc/passwd" });
638
+ check("filename: default strict guard refuses path traversal",
639
+ (await _expectThrows(function () {
640
+ return strict2.finalize({ uploadId: "f-trav",
641
+ manifest: _manifestFor([Buffer.from("body", "utf8")]), actor: { id: "x" } });
642
+ })).code === "FILENAME_SAFETY_REFUSED");
643
+
644
+ // A throwing filename gate surfaces as FILENAME_SAFETY_THREW.
645
+ var thrower = _plainManager("fn-throw", {
646
+ filenameSafety: { check: function () { throw new Error("gate exploded"); } },
647
+ });
648
+ // re-enable content=null already via _plainManager; override filenameSafety above.
649
+ await _seedUpload(thrower, "f-throw", [Buffer.from("body", "utf8")], { id: "x" },
650
+ { filename: "ok.txt" });
651
+ check("filename: a throwing gate → FILENAME_SAFETY_THREW",
652
+ (await _expectThrows(function () {
653
+ return thrower.finalize({ uploadId: "f-throw",
654
+ manifest: _manifestFor([Buffer.from("body", "utf8")]), actor: { id: "x" } });
655
+ })).code === "FILENAME_SAFETY_THREW");
656
+
657
+ // A sanitize decision rewrites metadata.filename that onFinalize sees.
658
+ var seen = null;
659
+ var san = b.fileUpload.create({
660
+ stagingDir: _tmpDir("fn-san"), contentSafety: null,
661
+ filenameSafety: { check: function () {
662
+ return { ok: true, action: "sanitize", sanitizedFilename: "cleaned.txt" };
663
+ } },
664
+ onFinalize: async function (info) { seen = info.metadata.filename; return { ok: true }; },
665
+ });
666
+ await _seedUpload(san, "f-san", [Buffer.from("body", "utf8")], { id: "x" },
667
+ { filename: "we!rd name.txt" });
668
+ await san.finalize({ uploadId: "f-san", manifest: _manifestFor([Buffer.from("body", "utf8")]),
669
+ actor: { id: "x" } });
670
+ check("filename: sanitize rewrites the filename onFinalize sees", seen === "cleaned.txt");
671
+ }
672
+
673
+ // ---- content-safety gate (refuse / throw / sanitize) -----------------------
674
+
675
+ async function testContentSafety() {
676
+ // The default-on strict content guard refuses HTML with a <script> tag.
677
+ var strict = b.fileUpload.create({ stagingDir: _tmpDir("cs-strict"), filenameSafety: null });
678
+ var evil = Buffer.from("<script>alert(document.cookie)</script>", "utf8");
679
+ await _seedUpload(strict, "c-html", [evil], { id: "x" }, { filename: "page.html" });
680
+ check("content: default strict guard refuses <script> HTML → CONTENT_SAFETY_REFUSED",
681
+ (await _expectThrows(function () {
682
+ return strict.finalize({ uploadId: "c-html", manifest: _manifestFor([evil]), actor: { id: "x" } });
683
+ })).code === "CONTENT_SAFETY_REFUSED");
684
+
685
+ // A throwing content gate surfaces as CONTENT_SAFETY_THREW.
686
+ var body = Buffer.from("some text body", "utf8");
687
+ var thrower = b.fileUpload.create({
688
+ stagingDir: _tmpDir("cs-throw"), filenameSafety: null,
689
+ contentSafety: { ".txt": { check: function () { throw new Error("scanner crashed"); } } },
690
+ });
691
+ await _seedUpload(thrower, "c-throw", [body], { id: "x" }, { filename: "note.txt" });
692
+ check("content: a throwing gate → CONTENT_SAFETY_THREW",
693
+ (await _expectThrows(function () {
694
+ return thrower.finalize({ uploadId: "c-throw", manifest: _manifestFor([body]), actor: { id: "x" } });
695
+ })).code === "CONTENT_SAFETY_THREW");
696
+
697
+ // A sanitize decision replaces the bytes onFinalize receives.
698
+ var seen = null;
699
+ var san = b.fileUpload.create({
700
+ stagingDir: _tmpDir("cs-san"), filenameSafety: null,
701
+ contentSafety: { ".txt": { check: function () {
702
+ return { ok: true, action: "sanitize", sanitized: Buffer.from("SANITIZED-BODY", "utf8") };
703
+ } } },
704
+ onFinalize: async function (info) { seen = info.body; return { ok: true }; },
705
+ });
706
+ await _seedUpload(san, "c-san", [body], { id: "x" }, { filename: "note.txt" });
707
+ await san.finalize({ uploadId: "c-san", manifest: _manifestFor([body]), actor: { id: "x" } });
708
+ check("content: sanitize replaces the body onFinalize receives",
709
+ Buffer.isBuffer(seen) && seen.toString("utf8") === "SANITIZED-BODY");
710
+
711
+ // An explicit refuse decision (ok:false) also refuses.
712
+ var refuser = b.fileUpload.create({
713
+ stagingDir: _tmpDir("cs-refuse"), filenameSafety: null,
714
+ contentSafety: { ".txt": { check: function () {
715
+ return { ok: false, action: "refuse", issues: [{ kind: "policy-violation" }] };
716
+ } } },
717
+ });
718
+ await _seedUpload(refuser, "c-refuse", [body], { id: "x" }, { filename: "note.txt" });
719
+ check("content: an explicit refuse decision → CONTENT_SAFETY_REFUSED",
720
+ (await _expectThrows(function () {
721
+ return refuser.finalize({ uploadId: "c-refuse", manifest: _manifestFor([body]), actor: { id: "x" } });
722
+ })).code === "CONTENT_SAFETY_REFUSED");
723
+ }
724
+
725
+ // ---- streaming reassembly path + onFinalize throw --------------------------
726
+
727
+ async function testStreamAndFinalizeThrow() {
728
+ var drained = -1;
729
+ var streamInfo = null;
730
+ var streamMgr = _plainManager("stream", {
731
+ maxStreamReassemblyBytes: 4,
732
+ onFinalize: async function (info) {
733
+ streamInfo = { hasBody: info.body !== null, isStream: !!info.stream, size: info.size };
734
+ drained = await _drain(info.stream);
735
+ return { ok: true, sha3: info.sha3, size: info.size };
736
+ },
737
+ });
738
+ var big = [Buffer.from("0123456789abcdef", "utf8")]; // 16 bytes > 4-byte cap
739
+ await _seedUpload(streamMgr, "u-stream", big, { id: "x" }, { filename: "blob.dat" });
740
+ var rv = await streamMgr.finalize({ uploadId: "u-stream", manifest: _manifestFor(big),
741
+ actor: { id: "x" } });
742
+ check("stream: upload past cap hands onFinalize a stream (body null)",
743
+ streamInfo && streamInfo.hasBody === false && streamInfo.isStream === true &&
744
+ streamInfo.size === big[0].length);
745
+ check("stream: draining the stream yields every byte",
746
+ drained === big[0].length && rv.ok === true);
747
+
748
+ // onFinalize throwing propagates out of finalize.
749
+ var throwMgr = _plainManager("finalize-throw", {
750
+ onFinalize: async function () { throw new Error("storage backend down"); },
751
+ });
752
+ var body = [Buffer.from("body for finalize throw", "utf8")];
753
+ await _seedUpload(throwMgr, "u-fthrow", body, { id: "x" });
754
+ var fErr = await _expectThrows(function () {
755
+ return throwMgr.finalize({ uploadId: "u-fthrow", manifest: _manifestFor(body), actor: { id: "x" } });
756
+ });
757
+ check("finalize: onFinalize throw propagates to the caller",
758
+ fErr !== null && fErr.message === "storage backend down");
759
+ // Staging is retained after an onFinalize failure so the caller can retry.
760
+ check("finalize: staging retained after onFinalize throw",
761
+ throwMgr.status("u-fthrow", { actor: { id: "x" } }) !== null);
762
+ }
763
+
764
+ // ---- permissions (coarse capability denial) --------------------------------
765
+
766
+ async function testPermissionDenied() {
767
+ var obsEvents = [];
768
+ var deny = b.fileUpload.create({
769
+ stagingDir: _tmpDir("perm-deny"), contentSafety: null, filenameSafety: null,
770
+ permissions: { check: function () { return false; } },
771
+ observability: { event: function () {}, safeEvent: function (n) { obsEvents.push(n); } },
772
+ });
773
+ var body = Buffer.from("x", "utf8");
774
+ var ops = [
775
+ ["init", function () { return deny.init({ uploadId: "p-1", actor: { id: "x" }, metadata: {} }); }],
776
+ ["accept", function () { return deny.acceptChunk({ uploadId: "p-1", index: 0, body: body, sha3: _sha3(body), actor: { id: "x" } }); }],
777
+ ["finalize", function () { return deny.finalize({ uploadId: "p-1", manifest: _manifestFor([body]), actor: { id: "x" } }); }],
778
+ ["status", function () { return Promise.resolve(deny.status("p-1", { actor: { id: "x" } })); }],
779
+ ["list", function () { return Promise.resolve(deny.list({ actor: { id: "x" } })); }],
780
+ ["cancel", function () { return deny.cancelUpload("p-1", { actor: { id: "x" } }); }],
781
+ ];
782
+ for (var i = 0; i < ops.length; i++) {
783
+ var name = ops[i][0], fn = ops[i][1];
784
+ var err = await _expectThrows(fn);
785
+ check("permission: " + name + " denied → PERMISSION_DENIED",
786
+ err !== null && err.code === "PERMISSION_DENIED");
787
+ }
788
+ check("permission: denial emitted a permission_denied observability event",
789
+ obsEvents.indexOf("fileUpload.permission_denied") !== -1);
790
+
791
+ // A permissions.check that THROWS is treated as a denial (fail-closed).
792
+ var throwPerms = b.fileUpload.create({
793
+ stagingDir: _tmpDir("perm-throw"), contentSafety: null, filenameSafety: null,
794
+ permissions: { check: function () { throw new Error("authz backend unreachable"); } },
795
+ });
796
+ var tErr = await _expectThrows(function () {
797
+ return throwPerms.init({ uploadId: "p-2", actor: { id: "x" }, metadata: {} });
798
+ });
799
+ check("permission: a throwing check() fails closed → PERMISSION_DENIED",
800
+ tErr !== null && tErr.code === "PERMISSION_DENIED");
801
+ }
802
+
803
+ // ---- ownership (IDOR / cross-actor) + admin escape hatch -------------------
804
+
805
+ async function testOwnershipAndAdmin() {
806
+ var A = { id: "actor-a" }, B = { id: "actor-b" };
807
+ var pieces = [Buffer.from("owned bytes", "utf8")];
808
+ var coarse = { check: function (actor, scope) { return scope !== "fileUpload.admin"; } };
809
+
810
+ // Cross-actor finalize is refused; the owner succeeds.
811
+ var u = _plainManager("own", { permissions: coarse });
812
+ await _seedUpload(u, "o-1", pieces, A);
813
+ check("ownership: actor B cannot finalize A's upload → OWNERSHIP_VIOLATION",
814
+ (await _expectThrows(function () {
815
+ return u.finalize({ uploadId: "o-1", manifest: _manifestFor(pieces), actor: B });
816
+ })).code === "OWNERSHIP_VIOLATION");
817
+ check("ownership: owner A finalizes successfully",
818
+ (await u.finalize({ uploadId: "o-1", manifest: _manifestFor(pieces), actor: A })).ok === true);
819
+
820
+ // allowCrossActor + the admin scope lets an admin act cross-actor.
821
+ var admin = _plainManager("own-admin", {
822
+ permissions: { check: function () { return true; } }, allowCrossActor: true,
823
+ });
824
+ await _seedUpload(admin, "o-2", pieces, A);
825
+ check("ownership: allowCrossActor + admin scope lets B finalize A's upload",
826
+ (await admin.finalize({ uploadId: "o-2", manifest: _manifestFor(pieces), actor: B })).ok === true);
827
+
828
+ // allowCrossActor WITHOUT the admin scope is still refused.
829
+ var gated = _plainManager("own-gated", { permissions: coarse, allowCrossActor: true });
830
+ await _seedUpload(gated, "o-3", pieces, A);
831
+ check("ownership: allowCrossActor still requires the admin scope → PERMISSION_DENIED",
832
+ (await _expectThrows(function () {
833
+ return gated.finalize({ uploadId: "o-3", manifest: _manifestFor(pieces), actor: B });
834
+ })).code === "PERMISSION_DENIED");
835
+
836
+ // allowCrossActor with NO permissions instance (single-tenant) is allowed.
837
+ var singleTenant = _plainManager("own-single", { allowCrossActor: true });
838
+ await _seedUpload(singleTenant, "o-4", pieces, A);
839
+ check("ownership: allowCrossActor alone (no permissions) permits cross-actor",
840
+ (await singleTenant.finalize({ uploadId: "o-4", manifest: _manifestFor(pieces), actor: B })).ok === true);
841
+ }
842
+
843
+ // ---- purgeIncomplete -------------------------------------------------------
844
+
845
+ async function testPurgeIncomplete() {
846
+ // TTL-exceeded (createdAt older than incompleteTtlMs).
847
+ var now = 5000000;
848
+ var clock = function () { return now; };
849
+ var ttl = _plainManager("purge-ttl", { incompleteTtlMs: 1000, maxIdleMs: 500, clock: clock });
850
+ await _seedUpload(ttl, "t-1", [Buffer.from("body", "utf8")], { id: "x" });
851
+ // Immediately, nothing is expired.
852
+ check("purge: fresh upload is not purged", ttl.purgeIncomplete().purged === 0);
853
+ now += 2000;
854
+ var ttlRv = ttl.purgeIncomplete();
855
+ check("purge: TTL-exceeded upload reclaimed",
856
+ ttlRv.purged === 1 && ttlRv.ids.indexOf("t-1") !== -1 &&
857
+ ttlRv.reasons[0].reason === "ttl-exceeded");
858
+
859
+ // Idle-exceeded (within TTL, but lastChunkAt older than maxIdleMs).
860
+ var now2 = 8000000;
861
+ var idle = _plainManager("purge-idle", {
862
+ incompleteTtlMs: 1000000, maxIdleMs: 500, clock: function () { return now2; },
863
+ });
864
+ await _seedUpload(idle, "i-1", [Buffer.from("body", "utf8")], { id: "x" });
865
+ now2 += 900; // < TTL, > idle
866
+ var idleRv = idle.purgeIncomplete();
867
+ check("purge: idle-exceeded upload reclaimed",
868
+ idleRv.purged === 1 && idleRv.reasons[0].reason === "idle-exceeded");
869
+
870
+ // Orphan directory with no _meta sidecar, reclaimed by mtime (real clock).
871
+ var orphanStaging = _tmpDir("purge-orphan");
872
+ var orphanMgr = b.fileUpload.create({
873
+ stagingDir: orphanStaging, contentSafety: null, filenameSafety: null,
874
+ });
875
+ var orphanDir = nodePath.join(orphanStaging, "orphan-1");
876
+ nodeFs.mkdirSync(orphanDir, { recursive: true, mode: 0o700 });
877
+ helpers.backdateFile(orphanDir, 48 * 60 * 60 * 1000); // 48h ago — older than the 24h default TTL
878
+ var orphanRv = orphanMgr.purgeIncomplete();
879
+ check("purge: orphan dir (no meta) reclaimed by mtime",
880
+ orphanRv.purged === 1 && orphanRv.reasons[0].reason === "orphan");
881
+
882
+ // Empty staging (dir removed) → { purged: 0 }.
883
+ var emptyStaging = _tmpDir("purge-empty");
884
+ var emptyMgr = b.fileUpload.create({
885
+ stagingDir: emptyStaging, contentSafety: null, filenameSafety: null,
886
+ });
887
+ nodeFs.rmSync(emptyStaging, { recursive: true, force: true });
888
+ var emptyRv = emptyMgr.purgeIncomplete();
889
+ check("purge: missing staging dir → { purged: 0 }",
890
+ emptyRv.purged === 0 && emptyRv.ids.length === 0);
891
+ }
892
+
893
+ // ---- defensive: corrupt meta sidecar is treated as not-found ---------------
894
+
895
+ async function testCorruptSidecar() {
896
+ var stagingDir = _tmpDir("corrupt");
897
+ var u = b.fileUpload.create({ stagingDir: stagingDir, contentSafety: null, filenameSafety: null });
898
+ await u.init({ uploadId: "u-corrupt", actor: { id: "x" }, metadata: {} });
899
+ // Corrupt the on-disk meta sidecar (disk-corruption / partial-write scenario).
900
+ nodeFs.writeFileSync(nodePath.join(stagingDir, "u-corrupt", "_meta.json"), "{ this is not json", "utf8");
901
+ var body = Buffer.from("body", "utf8");
902
+ var err = await _expectThrows(function () {
903
+ return u.acceptChunk({ uploadId: "u-corrupt", index: 0, body: body,
904
+ sha3: _sha3(body), actor: { id: "x" } });
905
+ });
906
+ check("corrupt-sidecar: unreadable _meta.json is treated as UNKNOWN_UPLOAD",
907
+ err !== null && err.code === "UNKNOWN_UPLOAD");
908
+ }
909
+
910
+ // ---- mid-walk cumulative cap (defensive, inside _verifyChunksOnDisk) -------
911
+
912
+ async function testMidWalkFileCap() {
913
+ // Two 10-byte chunks (20 bytes total) with a 15-byte cap. manifest.totalBytes
914
+ // is set to the cap (15) so _validateManifest's up-front check passes, and the
915
+ // cumulative overflow is only caught mid-walk once both chunk files are summed.
916
+ // Seed through a high-cap manager (acceptChunk enforces its own maxFileBytes),
917
+ // then finalize through a low-cap manager over the SAME on-disk staging dir.
918
+ var c0 = Buffer.from("0123456789", "utf8");
919
+ var c1 = Buffer.from("abcdefghij", "utf8");
920
+ var shared = _tmpDir("midwalk");
921
+ var seedMgr = b.fileUpload.create({ stagingDir: shared, contentSafety: null, filenameSafety: null });
922
+ await _seedUpload(seedMgr, "u-mid", [c0, c1], { id: "x" });
923
+ var capMgr = b.fileUpload.create({
924
+ stagingDir: shared, contentSafety: null, filenameSafety: null, maxFileBytes: 15,
925
+ });
926
+ var err = await _expectThrows(function () {
927
+ return capMgr.finalize({ uploadId: "u-mid",
928
+ manifest: { chunks: [{ index: 0, sha3: _sha3(c0) }, { index: 1, sha3: _sha3(c1) }],
929
+ totalBytes: 15, sha3: _fullSha3([c0, c1]) },
930
+ actor: { id: "x" } });
931
+ });
932
+ check("mid-walk: reassembly exceeding maxFileBytes mid-walk → FILE_TOO_LARGE",
933
+ err.code === "FILE_TOO_LARGE");
934
+ }
935
+
936
+ // ---- content-safety skip audits + sniffed-extension routing ---------------
937
+
938
+ async function testContentSafetySkipAndSniff() {
939
+ // no-gate-for-extension: a gate is wired only for .csv; a .txt upload has no
940
+ // matching gate, so the byte scan is skipped and audited.
941
+ var skips1 = [];
942
+ var noGate = b.fileUpload.create({
943
+ stagingDir: _tmpDir("skip-nogate"), filenameSafety: null,
944
+ contentSafety: { ".csv": { check: function () { return { ok: true, action: "serve" }; } } },
945
+ audit: { safeEmit: function (e) { skips1.push(e); } },
946
+ });
947
+ var txt = [Buffer.from("plain text, no gate for .txt", "utf8")];
948
+ await _seedUpload(noGate, "s-nogate", txt, { id: "x" }, { filename: "notes.txt" });
949
+ await noGate.finalize({ uploadId: "s-nogate", manifest: _manifestFor(txt), actor: { id: "x" } });
950
+ check("skip: no gate for the extension → no-gate-for-extension audit",
951
+ skips1.some(function (e) { return e.action === "fileUpload.content_safety_skipped" &&
952
+ e.reason === "no-gate-for-extension"; }));
953
+
954
+ // streamed-over-reassembly-cap: a gate IS registered for the ext but the
955
+ // upload streamed past the cap, so the byte gate could not run.
956
+ var skips2 = [];
957
+ var streamed = b.fileUpload.create({
958
+ stagingDir: _tmpDir("skip-stream"), filenameSafety: null, maxStreamReassemblyBytes: 4,
959
+ contentSafety: { ".dat": { check: function () { return { ok: true, action: "serve" }; } } },
960
+ audit: { safeEmit: function (e) { skips2.push(e); } },
961
+ onFinalize: async function (info) { await _drain(info.stream); return { ok: true }; },
962
+ });
963
+ var big = [Buffer.from("0123456789abcdef", "utf8")]; // 16 bytes > 4-byte cap
964
+ await _seedUpload(streamed, "s-stream", big, { id: "x" }, { filename: "blob.dat" });
965
+ await streamed.finalize({ uploadId: "s-stream", manifest: _manifestFor(big), actor: { id: "x" } });
966
+ check("skip: gate present but streamed past cap → streamed-over-reassembly-cap audit",
967
+ skips2.some(function (e) { return e.reason === "streamed-over-reassembly-cap"; }));
968
+
969
+ // Sniffed-extension routing: PDF magic bytes named "*.png" (no .png gate)
970
+ // route to the .pdf gate via magic-byte detection, so the mislabel cannot
971
+ // dodge its real type's scanner by choosing the extension.
972
+ var pdfGateRan = false;
973
+ var sniff = b.fileUpload.create({
974
+ stagingDir: _tmpDir("skip-sniff"), filenameSafety: null, fileType: b.fileType,
975
+ contentSafety: { ".pdf": { check: function () {
976
+ pdfGateRan = true;
977
+ return { ok: false, action: "refuse", issues: [{ kind: "pdf-refused" }] };
978
+ } } },
979
+ audit: { safeEmit: function () {} },
980
+ });
981
+ await _seedUpload(sniff, "s-sniff", [PDF_BYTES], { id: "x" }, { filename: "evil.png" });
982
+ var sniffErr = await _expectThrows(function () {
983
+ return sniff.finalize({ uploadId: "s-sniff", manifest: _manifestFor([PDF_BYTES]), actor: { id: "x" } });
984
+ });
985
+ check("sniff: mislabeled PDF (.png) routes to the sniffed .pdf gate → CONTENT_SAFETY_REFUSED",
986
+ pdfGateRan === true && sniffErr.code === "CONTENT_SAFETY_REFUSED");
987
+ }
988
+
989
+ // ---- actor-identity defaults + no-opts accessor arms -----------------------
990
+
991
+ async function testActorDefaultsAndAccessors() {
992
+ var u = _plainManager("anon");
993
+ // init with only uploadId — actor defaults to anonymous, metadata to {}.
994
+ await u.init({ uploadId: "anon-1" });
995
+ var body = Buffer.from("anonymous upload body", "utf8");
996
+ // acceptChunk with no actor — the anonymous owner matches.
997
+ await u.acceptChunk({ uploadId: "anon-1", index: 0, body: body, sha3: _sha3(body) });
998
+ // A non-string sha3 exercises the non-string arm of the hash-shape error.
999
+ var numHashErr = await _expectThrows(function () {
1000
+ return u.acceptChunk({ uploadId: "anon-1", index: 1, body: body, sha3: 12345 });
1001
+ });
1002
+ check("accept: numeric (non-string) sha3 → BAD_CHUNK_HASH", numHashErr.code === "BAD_CHUNK_HASH");
1003
+
1004
+ // status / list with no opts object at all.
1005
+ var st = u.status("anon-1");
1006
+ check("status: no-opts call resolves for the anonymous owner",
1007
+ st !== null && st.uploadId === "anon-1");
1008
+ var listed = u.list();
1009
+ check("list: no-opts call returns every upload", listed.length >= 1);
1010
+
1011
+ // finalize with no actor — metadata has no filename, so filename falls back
1012
+ // to the uploadId.
1013
+ var seenName = null;
1014
+ var uf = b.fileUpload.create({
1015
+ stagingDir: _tmpDir("anon-fin"), contentSafety: null,
1016
+ filenameSafety: { check: function (arg) { seenName = arg.filename;
1017
+ return { ok: true, action: "serve" }; } },
1018
+ });
1019
+ await uf.init({ uploadId: "anon-fin-1" });
1020
+ await uf.acceptChunk({ uploadId: "anon-fin-1", index: 0, body: body, sha3: _sha3(body) });
1021
+ await uf.finalize({ uploadId: "anon-fin-1", manifest: _manifestFor([body]) });
1022
+ check("finalize: filename falls back to uploadId when metadata carries none",
1023
+ seenName === "anon-fin-1");
1024
+
1025
+ // cancelUpload with no opts object.
1026
+ var cancelled = await u.cancelUpload("anon-1");
1027
+ check("cancel: no-opts call cancels the anonymous owner's upload", cancelled.ok === true);
1028
+
1029
+ // actor.userId (not .id) is the identity key.
1030
+ var uid = _plainManager("uid");
1031
+ var uidInit = await uid.init({ uploadId: "uid-1", actor: { userId: "by-user-id" } });
1032
+ check("init: actor.userId is accepted as the owner key", uidInit.uploadId === "uid-1");
1033
+ }
1034
+
1035
+ // ---- ownership admin escape: a throwing admin check fails closed -----------
1036
+
1037
+ async function testAdminScopeCheckThrows() {
1038
+ var A = { id: "actor-a" }, B = { id: "actor-b" };
1039
+ var pieces = [Buffer.from("admin-throw body", "utf8")];
1040
+ var mgr = _plainManager("own-admin-throw", {
1041
+ permissions: { check: function (actor, scope) {
1042
+ if (scope === "fileUpload.admin") throw new Error("authz backend down");
1043
+ return true;
1044
+ } },
1045
+ allowCrossActor: true,
1046
+ });
1047
+ await _seedUpload(mgr, "oa-1", pieces, A);
1048
+ var err = await _expectThrows(function () {
1049
+ return mgr.finalize({ uploadId: "oa-1", manifest: _manifestFor(pieces), actor: B });
1050
+ });
1051
+ check("ownership: a throwing admin-scope check fails closed → PERMISSION_DENIED",
1052
+ err !== null && err.code === "PERMISSION_DENIED");
1053
+ }
1054
+
1055
+ // ---- accessors on an in-progress (0-byte) upload + a few default arms ------
1056
+
1057
+ async function testInProgressAndDefaultArms() {
1058
+ var u = _plainManager("inprogress");
1059
+ // init only — no chunk yet, so totalBytesAccepted is 0.
1060
+ await u.init({ uploadId: "ip-1", actor: { id: "x" } });
1061
+ var st = u.status("ip-1", { actor: { id: "x" } });
1062
+ check("status: an init-only upload reports zero bytes + empty received",
1063
+ st !== null && st.totalBytesAccepted === 0 && st.received.length === 0);
1064
+ var cancelled = await u.cancelUpload("ip-1", { actor: { id: "x" } });
1065
+ check("cancel: an init-only (zero-byte) upload cancels cleanly", cancelled.ok === true);
1066
+
1067
+ // list filtered by an actor keyed on userId (not id).
1068
+ var uid = _plainManager("inprogress-uid");
1069
+ await uid.init({ uploadId: "ip-uid", actor: { userId: "by-user-id" } });
1070
+ var mine = uid.list({ actor: { userId: "by-user-id" } });
1071
+ check("list: filter matches an actor keyed on userId",
1072
+ mine.length === 1 && mine[0].uploadId === "ip-uid");
1073
+
1074
+ // onChunk throwing a non-Error value (a string) still refuses the chunk.
1075
+ var hookMgr = _plainManager("inprogress-hook", {
1076
+ onChunk: function () { throw "string rejection"; }, // eslint-disable-line no-throw-literal
1077
+ });
1078
+ await hookMgr.init({ uploadId: "ip-hook", actor: { id: "x" } });
1079
+ var body = Buffer.from("body", "utf8");
1080
+ var hookErr = await _expectThrows(function () {
1081
+ return hookMgr.acceptChunk({ uploadId: "ip-hook", index: 0, body: body,
1082
+ sha3: _sha3(body), actor: { id: "x" } });
1083
+ });
1084
+ check("acceptChunk: onChunk throwing a non-Error propagates the raw value",
1085
+ hookErr === "string rejection");
1086
+
1087
+ // A content gate that refuses with no issues array still refuses.
1088
+ var refuser = b.fileUpload.create({
1089
+ stagingDir: _tmpDir("inprogress-refuse"), filenameSafety: null,
1090
+ contentSafety: { ".txt": { check: function () { return { ok: false, action: "refuse" }; } } },
1091
+ });
1092
+ await _seedUpload(refuser, "ip-refuse", [body], { id: "x" }, { filename: "note.txt" });
1093
+ check("content: refuse with no issues array still → CONTENT_SAFETY_REFUSED",
1094
+ (await _expectThrows(function () {
1095
+ return refuser.finalize({ uploadId: "ip-refuse", manifest: _manifestFor([body]), actor: { id: "x" } });
1096
+ })).code === "CONTENT_SAFETY_REFUSED");
1097
+ }
1098
+
1099
+ async function run() {
1100
+ try {
1101
+ await testHappyLifecycle();
1102
+ await testDefaultOnFinalize();
1103
+ await testStatusListCancel();
1104
+ await testCreateOptValidation();
1105
+ await testAuditedOptOut();
1106
+ await testUploadIdValidation();
1107
+ await testInitBranches();
1108
+ await testAcceptChunkBranches();
1109
+ await testFinalizeManifestBranches();
1110
+ await testMimeGate();
1111
+ await testFilenameSafety();
1112
+ await testContentSafety();
1113
+ await testStreamAndFinalizeThrow();
1114
+ await testPermissionDenied();
1115
+ await testOwnershipAndAdmin();
1116
+ await testPurgeIncomplete();
1117
+ await testCorruptSidecar();
1118
+ await testMidWalkFileCap();
1119
+ await testContentSafetySkipAndSniff();
1120
+ await testActorDefaultsAndAccessors();
1121
+ await testAdminScopeCheckThrows();
1122
+ await testInProgressAndDefaultArms();
1123
+ } finally {
1124
+ _cleanupDirs();
1125
+ }
1126
+ }
1127
+
1128
+ module.exports = { run: run };
1129
+
1130
+ if (require.main === module) {
1131
+ run().then(
1132
+ function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
1133
+ function (e) { console.error("FAIL:", e.stack || e); process.exit(1); }
1134
+ );
1135
+ }