@blamejs/blamejs-shop 0.4.31 → 0.4.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/vendor/MANIFEST.json +392 -278
  4. package/lib/vendor/blamejs/.github/workflows/ci.yml +34 -3
  5. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +21 -4
  6. package/lib/vendor/blamejs/.gitignore +6 -0
  7. package/lib/vendor/blamejs/CHANGELOG.md +26 -0
  8. package/lib/vendor/blamejs/MIGRATING.md +43 -0
  9. package/lib/vendor/blamejs/README.md +8 -6
  10. package/lib/vendor/blamejs/SECURITY.md +19 -3
  11. package/lib/vendor/blamejs/api-snapshot.json +2190 -664
  12. package/lib/vendor/blamejs/docker/caddy/localstack.Caddyfile +19 -0
  13. package/lib/vendor/blamejs/docker/init/generate-certs.sh +1 -1
  14. package/lib/vendor/blamejs/docker/otel/config.yaml +42 -0
  15. package/lib/vendor/blamejs/docker/otel/export/.gitkeep +0 -0
  16. package/lib/vendor/blamejs/docker/postgres/initdb/10-replication.sh +15 -0
  17. package/lib/vendor/blamejs/docker/postgres/replica-entrypoint.sh +38 -0
  18. package/lib/vendor/blamejs/docker/toxiproxy/toxiproxy.json +14 -0
  19. package/lib/vendor/blamejs/docker-compose.test.yml +209 -0
  20. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +132 -0
  21. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +221 -61
  22. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +144 -9
  23. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +99 -0
  24. package/lib/vendor/blamejs/fuzz/guard-sql.fuzz.js +36 -0
  25. package/lib/vendor/blamejs/index.js +4 -0
  26. package/lib/vendor/blamejs/lib/agent-envelope-mac.js +104 -0
  27. package/lib/vendor/blamejs/lib/agent-event-bus.js +105 -4
  28. package/lib/vendor/blamejs/lib/agent-posture-chain.js +8 -42
  29. package/lib/vendor/blamejs/lib/ai-content-detect.js +9 -10
  30. package/lib/vendor/blamejs/lib/api-key.js +158 -77
  31. package/lib/vendor/blamejs/lib/atomic-file.js +62 -4
  32. package/lib/vendor/blamejs/lib/audit-chain.js +47 -11
  33. package/lib/vendor/blamejs/lib/audit-sign.js +77 -2
  34. package/lib/vendor/blamejs/lib/audit-tools.js +79 -51
  35. package/lib/vendor/blamejs/lib/audit.js +259 -123
  36. package/lib/vendor/blamejs/lib/auth/oauth.js +53 -9
  37. package/lib/vendor/blamejs/lib/auth/openid-federation.js +108 -47
  38. package/lib/vendor/blamejs/lib/auth/saml.js +6 -8
  39. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +31 -5
  40. package/lib/vendor/blamejs/lib/backup/index.js +45 -10
  41. package/lib/vendor/blamejs/lib/break-glass.js +355 -147
  42. package/lib/vendor/blamejs/lib/cache.js +174 -105
  43. package/lib/vendor/blamejs/lib/chain-writer.js +38 -16
  44. package/lib/vendor/blamejs/lib/cli.js +19 -14
  45. package/lib/vendor/blamejs/lib/cluster-provider-db.js +130 -104
  46. package/lib/vendor/blamejs/lib/cluster-storage.js +119 -22
  47. package/lib/vendor/blamejs/lib/cluster.js +119 -71
  48. package/lib/vendor/blamejs/lib/codepoint-class.js +23 -0
  49. package/lib/vendor/blamejs/lib/compliance.js +206 -4
  50. package/lib/vendor/blamejs/lib/consent.js +82 -29
  51. package/lib/vendor/blamejs/lib/constants.js +27 -11
  52. package/lib/vendor/blamejs/lib/crypto-field.js +916 -156
  53. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +35 -22
  54. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +3 -2
  55. package/lib/vendor/blamejs/lib/db-query.js +882 -260
  56. package/lib/vendor/blamejs/lib/db-schema.js +228 -44
  57. package/lib/vendor/blamejs/lib/db.js +249 -99
  58. package/lib/vendor/blamejs/lib/dsr.js +385 -55
  59. package/lib/vendor/blamejs/lib/error-page.js +14 -1
  60. package/lib/vendor/blamejs/lib/external-db-migrate.js +239 -137
  61. package/lib/vendor/blamejs/lib/external-db.js +549 -34
  62. package/lib/vendor/blamejs/lib/file-upload.js +52 -7
  63. package/lib/vendor/blamejs/lib/framework-error.js +20 -1
  64. package/lib/vendor/blamejs/lib/framework-files.js +73 -0
  65. package/lib/vendor/blamejs/lib/framework-schema.js +695 -394
  66. package/lib/vendor/blamejs/lib/gate-contract.js +659 -1
  67. package/lib/vendor/blamejs/lib/guard-agent-registry.js +26 -44
  68. package/lib/vendor/blamejs/lib/guard-all.js +1 -0
  69. package/lib/vendor/blamejs/lib/guard-auth.js +42 -112
  70. package/lib/vendor/blamejs/lib/guard-cidr.js +33 -154
  71. package/lib/vendor/blamejs/lib/guard-csv.js +46 -113
  72. package/lib/vendor/blamejs/lib/guard-domain.js +34 -157
  73. package/lib/vendor/blamejs/lib/guard-dsn.js +27 -43
  74. package/lib/vendor/blamejs/lib/guard-email.js +47 -69
  75. package/lib/vendor/blamejs/lib/guard-envelope.js +19 -32
  76. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +24 -42
  77. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +25 -43
  78. package/lib/vendor/blamejs/lib/guard-filename.js +42 -106
  79. package/lib/vendor/blamejs/lib/guard-graphql.js +42 -123
  80. package/lib/vendor/blamejs/lib/guard-html.js +53 -108
  81. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +24 -42
  82. package/lib/vendor/blamejs/lib/guard-image.js +46 -103
  83. package/lib/vendor/blamejs/lib/guard-imap-command.js +18 -32
  84. package/lib/vendor/blamejs/lib/guard-jmap.js +16 -30
  85. package/lib/vendor/blamejs/lib/guard-json.js +38 -108
  86. package/lib/vendor/blamejs/lib/guard-jsonpath.js +38 -171
  87. package/lib/vendor/blamejs/lib/guard-jwt.js +49 -179
  88. package/lib/vendor/blamejs/lib/guard-list-id.js +25 -41
  89. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +27 -43
  90. package/lib/vendor/blamejs/lib/guard-mail-compose.js +24 -42
  91. package/lib/vendor/blamejs/lib/guard-mail-move.js +26 -44
  92. package/lib/vendor/blamejs/lib/guard-mail-query.js +28 -46
  93. package/lib/vendor/blamejs/lib/guard-mail-reply.js +24 -42
  94. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +24 -42
  95. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +17 -31
  96. package/lib/vendor/blamejs/lib/guard-markdown.js +37 -104
  97. package/lib/vendor/blamejs/lib/guard-message-id.js +26 -45
  98. package/lib/vendor/blamejs/lib/guard-mime.js +39 -151
  99. package/lib/vendor/blamejs/lib/guard-oauth.js +54 -135
  100. package/lib/vendor/blamejs/lib/guard-pdf.js +45 -101
  101. package/lib/vendor/blamejs/lib/guard-pop3-command.js +21 -31
  102. package/lib/vendor/blamejs/lib/guard-posture-chain.js +24 -42
  103. package/lib/vendor/blamejs/lib/guard-regex.js +33 -107
  104. package/lib/vendor/blamejs/lib/guard-saga-config.js +24 -42
  105. package/lib/vendor/blamejs/lib/guard-shell.js +42 -172
  106. package/lib/vendor/blamejs/lib/guard-smtp-command.js +48 -54
  107. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +24 -42
  108. package/lib/vendor/blamejs/lib/guard-sql.js +1491 -0
  109. package/lib/vendor/blamejs/lib/guard-stream-args.js +24 -43
  110. package/lib/vendor/blamejs/lib/guard-svg.js +47 -65
  111. package/lib/vendor/blamejs/lib/guard-template.js +35 -172
  112. package/lib/vendor/blamejs/lib/guard-tenant-id.js +26 -45
  113. package/lib/vendor/blamejs/lib/guard-time.js +32 -154
  114. package/lib/vendor/blamejs/lib/guard-trace-context.js +25 -44
  115. package/lib/vendor/blamejs/lib/guard-uuid.js +32 -153
  116. package/lib/vendor/blamejs/lib/guard-xml.js +38 -113
  117. package/lib/vendor/blamejs/lib/guard-yaml.js +51 -163
  118. package/lib/vendor/blamejs/lib/http-client.js +37 -9
  119. package/lib/vendor/blamejs/lib/inbox.js +120 -107
  120. package/lib/vendor/blamejs/lib/legal-hold.js +121 -50
  121. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +47 -31
  122. package/lib/vendor/blamejs/lib/log-stream-otlp.js +32 -18
  123. package/lib/vendor/blamejs/lib/mail-auth.js +236 -0
  124. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +2 -6
  125. package/lib/vendor/blamejs/lib/mail-dkim.js +1 -0
  126. package/lib/vendor/blamejs/lib/mail-greylist.js +2 -6
  127. package/lib/vendor/blamejs/lib/mail-helo.js +2 -6
  128. package/lib/vendor/blamejs/lib/mail-journal.js +85 -64
  129. package/lib/vendor/blamejs/lib/mail-rbl.js +2 -6
  130. package/lib/vendor/blamejs/lib/mail-scan.js +2 -6
  131. package/lib/vendor/blamejs/lib/mail-server-jmap.js +117 -12
  132. package/lib/vendor/blamejs/lib/mail-server-mx.js +276 -7
  133. package/lib/vendor/blamejs/lib/mail-spam-score.js +2 -6
  134. package/lib/vendor/blamejs/lib/mail-store.js +293 -154
  135. package/lib/vendor/blamejs/lib/mail.js +8 -4
  136. package/lib/vendor/blamejs/lib/middleware/body-parser.js +71 -25
  137. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +19 -8
  138. package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -1
  139. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +17 -7
  140. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +75 -51
  141. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +102 -32
  142. package/lib/vendor/blamejs/lib/middleware/security-headers.js +21 -5
  143. package/lib/vendor/blamejs/lib/migrations.js +108 -66
  144. package/lib/vendor/blamejs/lib/network-heartbeat.js +7 -0
  145. package/lib/vendor/blamejs/lib/network-proxy.js +24 -1
  146. package/lib/vendor/blamejs/lib/nonce-store.js +31 -9
  147. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +9 -4
  148. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +57 -3
  149. package/lib/vendor/blamejs/lib/object-store/gcs.js +4 -1
  150. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -2
  151. package/lib/vendor/blamejs/lib/object-store/sigv4.js +38 -6
  152. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +9 -1
  153. package/lib/vendor/blamejs/lib/observability.js +124 -0
  154. package/lib/vendor/blamejs/lib/otel-export.js +12 -3
  155. package/lib/vendor/blamejs/lib/outbox.js +184 -83
  156. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +47 -7
  157. package/lib/vendor/blamejs/lib/pqc-agent.js +44 -0
  158. package/lib/vendor/blamejs/lib/pubsub-cluster.js +42 -20
  159. package/lib/vendor/blamejs/lib/queue-local.js +225 -140
  160. package/lib/vendor/blamejs/lib/queue-redis.js +9 -1
  161. package/lib/vendor/blamejs/lib/queue-sqs.js +6 -0
  162. package/lib/vendor/blamejs/lib/queue.js +7 -0
  163. package/lib/vendor/blamejs/lib/redact.js +68 -11
  164. package/lib/vendor/blamejs/lib/redis-client.js +160 -31
  165. package/lib/vendor/blamejs/lib/request-helpers.js +7 -0
  166. package/lib/vendor/blamejs/lib/retention.js +101 -40
  167. package/lib/vendor/blamejs/lib/router.js +212 -5
  168. package/lib/vendor/blamejs/lib/safe-dns.js +29 -45
  169. package/lib/vendor/blamejs/lib/safe-ical.js +18 -33
  170. package/lib/vendor/blamejs/lib/safe-icap.js +27 -43
  171. package/lib/vendor/blamejs/lib/safe-sieve.js +21 -40
  172. package/lib/vendor/blamejs/lib/safe-sql.js +212 -3
  173. package/lib/vendor/blamejs/lib/safe-url.js +170 -3
  174. package/lib/vendor/blamejs/lib/safe-vcard.js +18 -33
  175. package/lib/vendor/blamejs/lib/scheduler.js +35 -12
  176. package/lib/vendor/blamejs/lib/seeders.js +122 -74
  177. package/lib/vendor/blamejs/lib/session-stores.js +42 -14
  178. package/lib/vendor/blamejs/lib/session.js +175 -77
  179. package/lib/vendor/blamejs/lib/sql.js +3842 -0
  180. package/lib/vendor/blamejs/lib/sse.js +26 -0
  181. package/lib/vendor/blamejs/lib/ssrf-guard.js +151 -4
  182. package/lib/vendor/blamejs/lib/static.js +177 -34
  183. package/lib/vendor/blamejs/lib/subject.js +96 -49
  184. package/lib/vendor/blamejs/lib/vault/index.js +3 -2
  185. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +3 -2
  186. package/lib/vendor/blamejs/lib/vault/rotate.js +168 -108
  187. package/lib/vendor/blamejs/lib/vault-aad.js +6 -0
  188. package/lib/vendor/blamejs/lib/vendor-data.js +2 -0
  189. package/lib/vendor/blamejs/lib/websocket.js +35 -5
  190. package/lib/vendor/blamejs/lib/worker-pool.js +11 -0
  191. package/lib/vendor/blamejs/package.json +2 -2
  192. package/lib/vendor/blamejs/release-notes/v0.14.x.json +1503 -0
  193. package/lib/vendor/blamejs/release-notes/v0.15.0.json +77 -0
  194. package/lib/vendor/blamejs/release-notes/v0.15.1.json +22 -0
  195. package/lib/vendor/blamejs/release-notes/v0.15.2.json +22 -0
  196. package/lib/vendor/blamejs/release-notes/v0.15.3.json +39 -0
  197. package/lib/vendor/blamejs/release-notes/v0.15.4.json +39 -0
  198. package/lib/vendor/blamejs/release-notes/v0.15.5.json +22 -0
  199. package/lib/vendor/blamejs/release-notes/v0.15.6.json +59 -0
  200. package/lib/vendor/blamejs/scripts/check-services.js +21 -0
  201. package/lib/vendor/blamejs/scripts/gen-migrating.js +51 -0
  202. package/lib/vendor/blamejs/scripts/release.js +398 -38
  203. package/lib/vendor/blamejs/test/00-primitives.js +117 -0
  204. package/lib/vendor/blamejs/test/10-state.js +140 -14
  205. package/lib/vendor/blamejs/test/20-db.js +65 -2
  206. package/lib/vendor/blamejs/test/helpers/db.js +9 -0
  207. package/lib/vendor/blamejs/test/helpers/drivers.js +27 -15
  208. package/lib/vendor/blamejs/test/helpers/services.js +21 -0
  209. package/lib/vendor/blamejs/test/integration/audit-actor-binding-pg.test.js +246 -0
  210. package/lib/vendor/blamejs/test/integration/audit-chain-external-db.test.js +517 -0
  211. package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +639 -0
  212. package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +832 -0
  213. package/lib/vendor/blamejs/test/integration/backup-restore-objectstore.test.js +453 -0
  214. package/lib/vendor/blamejs/test/integration/data-layer-cluster-mysql.test.js +649 -0
  215. package/lib/vendor/blamejs/test/integration/data-layer-cluster-pg.test.js +770 -0
  216. package/lib/vendor/blamejs/test/integration/data-layer-mysql-privacy.test.js +630 -0
  217. package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +610 -0
  218. package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +577 -0
  219. package/lib/vendor/blamejs/test/integration/data-layer-postgres.test.js +771 -0
  220. package/lib/vendor/blamejs/test/integration/db-layer-mysql.test.js +549 -0
  221. package/lib/vendor/blamejs/test/integration/db-layer-postgres.test.js +598 -0
  222. package/lib/vendor/blamejs/test/integration/distributed-scheduler-fencing-pg.test.js +602 -0
  223. package/lib/vendor/blamejs/test/integration/external-db-postgres.test.js +576 -0
  224. package/lib/vendor/blamejs/test/integration/framework-schema-mysql.test.js +353 -0
  225. package/lib/vendor/blamejs/test/integration/log-stream-cloudwatch.test.js +224 -0
  226. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +142 -17
  227. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +25 -10
  228. package/lib/vendor/blamejs/test/integration/object-store-azure.test.js +101 -0
  229. package/lib/vendor/blamejs/test/integration/object-store-gcs.test.js +239 -0
  230. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +35 -16
  231. package/lib/vendor/blamejs/test/integration/object-store-worm-lock.test.js +291 -0
  232. package/lib/vendor/blamejs/test/integration/pubsub.test.js +14 -0
  233. package/lib/vendor/blamejs/test/integration/queue-sqs.test.js +322 -0
  234. package/lib/vendor/blamejs/test/integration/redis-reconnect-toxiproxy.test.js +300 -0
  235. package/lib/vendor/blamejs/test/integration/sql-fts5-catalog-sqlite.test.js +154 -0
  236. package/lib/vendor/blamejs/test/integration/tls-classical-downgrade-audit.test.js +71 -0
  237. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +175 -12
  238. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-exclusive-temp.test.js +216 -0
  239. package/lib/vendor/blamejs/test/layer-0-primitives/audit-checkpoint-false-rollback.test.js +203 -0
  240. package/lib/vendor/blamejs/test/layer-0-primitives/audit-query-self-log.test.js +126 -0
  241. package/lib/vendor/blamejs/test/layer-0-primitives/audit-safeemit-redacts-secrets.test.js +196 -0
  242. package/lib/vendor/blamejs/test/layer-0-primitives/audit-signing-key-rotation.test.js +197 -0
  243. package/lib/vendor/blamejs/test/layer-0-primitives/audit-verifybundle-tamper.test.js +209 -0
  244. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-key-encoding.test.js +121 -0
  245. package/lib/vendor/blamejs/test/layer-0-primitives/backup-residency-posture.test.js +168 -0
  246. package/lib/vendor/blamejs/test/layer-0-primitives/backup-scheduletest-drill.test.js +318 -0
  247. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +233 -7
  248. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +1120 -14
  249. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +229 -0
  250. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +24 -7
  251. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-dual-read-migrate.test.js +165 -0
  252. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-per-row-key.test.js +350 -0
  253. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +27 -9
  254. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-upgrade-dialect.test.js +76 -0
  255. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-interop-oracles.test.js +392 -0
  256. package/lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js +159 -0
  257. package/lib/vendor/blamejs/test/layer-0-primitives/db-column-gate.test.js +180 -1
  258. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +5 -2
  259. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-sealed-field-in.test.js +101 -0
  260. package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +128 -0
  261. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +38 -5
  262. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-reconcile-emittable.test.js +127 -0
  263. package/lib/vendor/blamejs/test/layer-0-primitives/db-stream-and-payload-shape.test.js +267 -0
  264. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +150 -0
  265. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +30 -0
  266. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +46 -0
  267. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +218 -0
  268. package/lib/vendor/blamejs/test/layer-0-primitives/erase-posture-vacuum.test.js +210 -0
  269. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +4 -1
  270. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +48 -2
  271. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +237 -5
  272. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +20 -9
  273. package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +193 -0
  274. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +90 -0
  275. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +85 -0
  276. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +10 -6
  277. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +15 -4
  278. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +146 -0
  279. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +189 -0
  280. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +3 -1
  281. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +123 -4
  282. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +207 -2
  283. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +74 -0
  284. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +43 -0
  285. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +133 -0
  286. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +101 -0
  287. package/lib/vendor/blamejs/test/layer-0-primitives/outbox-inflight-reaper.test.js +136 -0
  288. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +83 -0
  289. package/lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js +429 -0
  290. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +21 -11
  291. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +40 -0
  292. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +83 -0
  293. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +113 -0
  294. package/lib/vendor/blamejs/test/layer-0-primitives/retention-dryrun-no-vacuum.test.js +99 -0
  295. package/lib/vendor/blamejs/test/layer-0-primitives/router-use-path-scope.test.js +255 -0
  296. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-canonicalize.test.js +309 -0
  297. package/lib/vendor/blamejs/test/layer-0-primitives/safe-xml.test.js +143 -0
  298. package/lib/vendor/blamejs/test/layer-0-primitives/saml-subjectconfirmation-notonorafter.test.js +287 -0
  299. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc-ecdsa-p1363.test.js +79 -0
  300. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +50 -0
  301. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +31 -4
  302. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +45 -0
  303. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +49 -0
  304. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +595 -0
  305. package/lib/vendor/blamejs/test/layer-0-primitives/sse-backpressure.test.js +91 -0
  306. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +69 -0
  307. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +194 -2
  308. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-extension-header.test.js +88 -0
  309. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool-recycle-race.test.js +66 -0
  310. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +84 -0
  311. package/lib/vendor/blamejs/test/layer-5-integration/external-db-residency.test.js +638 -0
  312. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +21 -0
  313. package/lib/vendor/blamejs/test/smoke.js +79 -21
  314. package/package.json +1 -1
  315. package/lib/vendor/blamejs/release-notes/v0.14.0.json +0 -43
  316. package/lib/vendor/blamejs/release-notes/v0.14.1.json +0 -60
  317. package/lib/vendor/blamejs/release-notes/v0.14.10.json +0 -54
  318. package/lib/vendor/blamejs/release-notes/v0.14.11.json +0 -72
  319. package/lib/vendor/blamejs/release-notes/v0.14.12.json +0 -95
  320. package/lib/vendor/blamejs/release-notes/v0.14.13.json +0 -52
  321. package/lib/vendor/blamejs/release-notes/v0.14.14.json +0 -31
  322. package/lib/vendor/blamejs/release-notes/v0.14.16.json +0 -45
  323. package/lib/vendor/blamejs/release-notes/v0.14.17.json +0 -57
  324. package/lib/vendor/blamejs/release-notes/v0.14.18.json +0 -127
  325. package/lib/vendor/blamejs/release-notes/v0.14.19.json +0 -61
  326. package/lib/vendor/blamejs/release-notes/v0.14.2.json +0 -18
  327. package/lib/vendor/blamejs/release-notes/v0.14.20.json +0 -73
  328. package/lib/vendor/blamejs/release-notes/v0.14.21.json +0 -98
  329. package/lib/vendor/blamejs/release-notes/v0.14.22.json +0 -91
  330. package/lib/vendor/blamejs/release-notes/v0.14.3.json +0 -18
  331. package/lib/vendor/blamejs/release-notes/v0.14.4.json +0 -18
  332. package/lib/vendor/blamejs/release-notes/v0.14.5.json +0 -18
  333. package/lib/vendor/blamejs/release-notes/v0.14.6.json +0 -60
  334. package/lib/vendor/blamejs/release-notes/v0.14.7.json +0 -77
  335. package/lib/vendor/blamejs/release-notes/v0.14.8.json +0 -27
  336. package/lib/vendor/blamejs/release-notes/v0.14.9.json +0 -40
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ // #126: SSE _writeRaw wrote to the response with no regard for backpressure
3
+ // and no bound on the outbound buffer. res.write() returns false when the
4
+ // socket's send buffer is full; a slow/stalled client never drains, so the
5
+ // server's writable buffer (res.writableLength) grows without bound — one
6
+ // stuck connection can exhaust the server heap (memory-exhaustion DoS).
7
+ //
8
+ // The fix caps the per-channel buffered bytes (maxBufferedBytes) and evicts
9
+ // the slow consumer (closes the channel + throws sse/backpressure) once the
10
+ // buffer blows the cap, instead of buffering indefinitely.
11
+ //
12
+ // RED on the buggy tree: send() can be called unbounded against a stalled
13
+ // client and never refuses; the channel stays open. GREEN after the fix:
14
+ // send() throws sse/backpressure once the buffer exceeds the cap and the
15
+ // channel is closed.
16
+
17
+ var EventEmitter = require("events");
18
+ var helpers = require("../helpers");
19
+ var b = helpers.b;
20
+ var check = helpers.check;
21
+
22
+ // A response whose socket never drains: every write accumulates into
23
+ // writableLength and write() reports backpressure (false) past the highwater.
24
+ function _stalledRes(highWaterBytes) {
25
+ var res = new EventEmitter();
26
+ var buffered = 0;
27
+ res.writableLength = 0;
28
+ res.setHeader = function () {};
29
+ res.flushHeaders = function () {};
30
+ res.write = function (chunk) {
31
+ buffered += Buffer.byteLength(chunk, "utf8");
32
+ res.writableLength = buffered; // stalled client: nothing is ever flushed
33
+ return buffered < highWaterBytes; // false once over the socket highwater
34
+ };
35
+ res.end = function () {};
36
+ return res;
37
+ }
38
+
39
+ async function run() {
40
+ var req = { method: "GET", url: "/events", headers: {}, httpVersionMajor: 1, on: function () {} };
41
+ var res = _stalledRes(b.constants.BYTES.kib(64));
42
+
43
+ var capBytes = b.constants.BYTES.kib(8);
44
+ var channel = b.sse.create(req, res, {
45
+ heartbeatMs: 0, // no heartbeat noise
46
+ audit: false,
47
+ maxBufferedBytes: capBytes,
48
+ });
49
+
50
+ var threw = null;
51
+ var sends = 0;
52
+ var event = { data: "x".repeat(512) }; // ~520 bytes serialized
53
+ try {
54
+ for (var i = 0; i < 100000 && threw === null; i++) {
55
+ channel.send(event);
56
+ sends += 1;
57
+ }
58
+ } catch (e) { threw = e; }
59
+
60
+ check("#126 send() refuses once the outbound buffer blows maxBufferedBytes",
61
+ threw !== null && threw.code === "sse/backpressure");
62
+ check("#126 the slow-consumer channel is closed on eviction",
63
+ channel.closed === true);
64
+ // The buffer cap is ~8 KiB and events are ~520 B, so eviction must fire
65
+ // within a few dozen sends — never the full 100k unbounded loop.
66
+ check("#126 buffering is bounded (evicted well before the unbounded loop ended)",
67
+ sends > 0 && sends < 1000);
68
+ check("#126 res.writableLength stayed near the cap, not unbounded",
69
+ res.writableLength < capBytes + b.constants.BYTES.kib(2));
70
+
71
+ // A HEALTHY client (write() drains, writableLength stays ~0) is never evicted.
72
+ var healthyRes = new EventEmitter();
73
+ healthyRes.writableLength = 0;
74
+ healthyRes.setHeader = function () {};
75
+ healthyRes.flushHeaders = function () {};
76
+ healthyRes.write = function () { healthyRes.writableLength = 0; return true; };
77
+ healthyRes.end = function () {};
78
+ var healthy = b.sse.create(req, healthyRes, { heartbeatMs: 0, audit: false, maxBufferedBytes: capBytes });
79
+ for (var j = 0; j < 5000; j++) healthy.send(event);
80
+ check("#126 a healthy (draining) client is never evicted by the cap",
81
+ healthy.closed === false);
82
+ healthy.close();
83
+
84
+ console.log("OK — sse backpressure / bounded-buffer tests");
85
+ }
86
+
87
+ module.exports = { run: run };
88
+ if (require.main === module) {
89
+ run().then(function () { process.exit(0); })
90
+ .catch(function (err) { process.stderr.write(String(err && err.stack || err) + "\n"); process.exit(1); });
91
+ }
@@ -206,6 +206,75 @@ async function run() {
206
206
  check("checkUrl: public IP passes",
207
207
  r2 && r2.ips[0].address === "93.184.216.34");
208
208
 
209
+ // ---- cloud-metadata IPv6 matched on canonical bytes, not string ----
210
+ // The IMDS-over-IPv6 address fd00:ec2::254 has many textual spellings.
211
+ // A non-canonical / mixed-case spelling must still classify as
212
+ // cloud-metadata — a string-equality membership test caught one
213
+ // spelling only, and the DoH resolver decodes AAAA to the expanded
214
+ // fd00:ec2:0:0:0:0:0:254 form, which slipped past as "private".
215
+ check("classify fd00:ec2:0:0:0:0:0:254 → cloud-metadata (expanded zero-run)",
216
+ ssrf.classify("fd00:ec2:0:0:0:0:0:254") === "cloud-metadata");
217
+ check("classify FD00:EC2::254 → cloud-metadata (mixed-case compressed)",
218
+ ssrf.classify("FD00:EC2::254") === "cloud-metadata");
219
+ check("classify fd00:0ec2::0254 → cloud-metadata (leading-zero groups)",
220
+ ssrf.classify("fd00:0ec2::0254") === "cloud-metadata");
221
+ // A genuine ULA private address that is NOT metadata must stay "private".
222
+ check("classify fd00::1 → private (NOT metadata)",
223
+ ssrf.classify("fd00::1") === "private");
224
+
225
+ // PoC: allowInternal:true MUST NOT override the unconditional metadata
226
+ // block, even when DNS resolves to the non-canonical IMDS-over-IPv6
227
+ // spelling. Pre-fix this RESOLVED (credentials reachable); now refused.
228
+ threw = null;
229
+ try {
230
+ await ssrf.checkUrl("http://imds.attacker.example/latest/meta-data/iam/", {
231
+ allowInternal: true,
232
+ dnsLookup: _stubLookup("fd00:ec2:0:0:0:0:0:254", 6),
233
+ });
234
+ } catch (e) { threw = e; }
235
+ check("checkUrl: allowInternal:true does NOT override non-canonical IMDS-over-IPv6",
236
+ threw && threw.code === "ssrf-guard/blocked-cloud-metadata");
237
+
238
+ // Compressed + mixed-case spellings refused too (wire not reached).
239
+ threw = null;
240
+ try {
241
+ await ssrf.checkUrl("http://imds.attacker.example/", {
242
+ allowInternal: true,
243
+ dnsLookup: _stubLookup("FD00:EC2::254", 6),
244
+ });
245
+ } catch (e) { threw = e; }
246
+ check("checkUrl: allowInternal:true does NOT override mixed-case IMDS-over-IPv6",
247
+ threw && threw.code === "ssrf-guard/blocked-cloud-metadata");
248
+
249
+ // A genuine private IPv6 (fd00::1, NOT metadata) still respects
250
+ // allowInternal — the metadata gate is the only unconditional class.
251
+ threw = null;
252
+ var rPriv6 = null;
253
+ try {
254
+ rPriv6 = await ssrf.checkUrl("http://internal.mesh/", {
255
+ allowInternal: true,
256
+ dnsLookup: _stubLookup("fd00::1", 6),
257
+ });
258
+ } catch (e) { threw = e; }
259
+ check("checkUrl: genuine private IPv6 still honors allowInternal:true",
260
+ threw === null && rPriv6 && rPriv6.ips[0].address === "fd00::1");
261
+
262
+ // checkUrlTextual: the same byte-canonical gate at the textual layer.
263
+ threw = null;
264
+ try { ssrf.checkUrlTextual("http://[fd00:ec2:0:0:0:0:0:254]/x"); }
265
+ catch (e) { threw = e; }
266
+ check("checkUrlTextual: non-canonical IMDS-over-IPv6 refused",
267
+ threw && threw.code === "ssrf-guard/blocked-cloud-metadata");
268
+ threw = null;
269
+ try { ssrf.checkUrlTextual("http://[FD00:EC2::254]/x"); }
270
+ catch (e) { threw = e; }
271
+ check("checkUrlTextual: mixed-case IMDS-over-IPv6 refused",
272
+ threw && threw.code === "ssrf-guard/blocked-cloud-metadata");
273
+ // A non-metadata internal host stays accepted at the textual layer.
274
+ var txtOk = ssrf.checkUrlTextual("http://[fd00::1]/x");
275
+ check("checkUrlTextual: non-metadata IPv6 literal accepted",
276
+ txtOk && txtOk.host === "fd00::1");
277
+
209
278
  // ---- checkUrl: bad input ----
210
279
  threw = null;
211
280
  try { await ssrf.checkUrl("not-a-url"); }
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  /**
3
- * static — forceAttachmentForNonText opt-in defense for stored-XSS via
4
- * user-upload directories.
3
+ * static — forceAttachmentForNonText defense for stored-XSS via
4
+ * user-upload directories, plus the mountType typing that drives its
5
+ * default (v0.15.0: "user-content" mounts force-download by default;
6
+ * "curated" mounts keep inline render).
5
7
  *
6
8
  * Run standalone: `node test/layer-0-primitives/static.test.js`
7
9
  * Or via smoke: `node test/smoke.js`
@@ -233,6 +235,73 @@ async function testForceAttachmentPdfOptInInline() {
233
235
  }
234
236
  }
235
237
 
238
+ // mountType drives the forceAttachmentForNonText default (v0.15.0).
239
+ // A mount TYPED "user-content" forces risky inline MIMEs to download with
240
+ // NO explicit forceAttachmentForNonText (the new default-on behavior); a
241
+ // "curated" mount (or unset) keeps inline render (the opt-out preserved).
242
+ async function testMountTypeUserContentForcesDownloadByDefault() {
243
+ var ctx = await _server();
244
+ _writeFile(ctx.dir, "evil.html", "<script>alert(1)</script>");
245
+ // No forceAttachmentForNonText passed — the default comes from mountType.
246
+ var srv = await ctx.start({ contentSafety: null, mountType: "user-content" });
247
+ try {
248
+ var r = await _get(srv.port, "/evil.html");
249
+ check("mountType user-content: html forced to attachment by default",
250
+ r.statusCode === 200 &&
251
+ /^attachment;/.test(r.headers["content-disposition"] || ""));
252
+ check("mountType user-content: nosniff added by default",
253
+ r.headers["x-content-type-options"] === "nosniff");
254
+ } finally {
255
+ srv.close();
256
+ ctx.cleanup();
257
+ }
258
+ }
259
+
260
+ async function testMountTypeCuratedKeepsInline() {
261
+ var ctx = await _server();
262
+ _writeFile(ctx.dir, "evil.html", "<script>alert(1)</script>");
263
+ // Explicit "curated" is the documented opt-out — inline render preserved.
264
+ var srv = await ctx.start({ contentSafety: null, mountType: "curated" });
265
+ try {
266
+ var r = await _get(srv.port, "/evil.html");
267
+ check("mountType curated: html served inline (opt-out preserved)",
268
+ r.statusCode === 200 && !r.headers["content-disposition"]);
269
+ } finally {
270
+ srv.close();
271
+ ctx.cleanup();
272
+ }
273
+ }
274
+
275
+ async function testMountTypeExplicitOverrideWins() {
276
+ var ctx = await _server();
277
+ _writeFile(ctx.dir, "evil.html", "<script>alert(1)</script>");
278
+ // An explicit forceAttachmentForNonText:false overrides the
279
+ // user-content-derived default either way.
280
+ var srv = await ctx.start({
281
+ contentSafety: null, mountType: "user-content",
282
+ forceAttachmentForNonText: false,
283
+ });
284
+ try {
285
+ var r = await _get(srv.port, "/evil.html");
286
+ check("explicit forceAttachmentForNonText:false overrides user-content default",
287
+ r.statusCode === 200 && !r.headers["content-disposition"]);
288
+ } finally {
289
+ srv.close();
290
+ ctx.cleanup();
291
+ }
292
+ }
293
+
294
+ function testMountTypeBadValueThrows() {
295
+ var threw = null;
296
+ var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-static-mt-"));
297
+ try {
298
+ b.staticServe.create({ root: dir, mountType: "uploads" });
299
+ } catch (e) { threw = e; }
300
+ finally { fs.rmSync(dir, { recursive: true, force: true }); }
301
+ check("bad mountType value throws at config time",
302
+ threw && /mountType must be 'curated'/.test(threw.message));
303
+ }
304
+
236
305
  function testRejectsUnknownOpts() {
237
306
  var threw = null;
238
307
  var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-static-fa-"));
@@ -299,6 +368,122 @@ function testOnErrorRejectsNonFunction() {
299
368
  fs.rmSync(dir, { recursive: true, force: true });
300
369
  }
301
370
 
371
+ // Send a raw (un-normalized-by-node) request path. http.request normalizes
372
+ // "/../" on the client side, so build the request line by hand over a raw
373
+ // socket to deliver the traversal bytes verbatim to the server.
374
+ function _getRaw(port, rawPath) {
375
+ var net = require("node:net");
376
+ return new Promise(function (resolve, reject) {
377
+ var sock = net.connect(port, "127.0.0.1", function () {
378
+ sock.write("GET " + rawPath + " HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n");
379
+ });
380
+ var chunks = [];
381
+ sock.on("data", function (c) { chunks.push(c); });
382
+ sock.on("end", function () {
383
+ var raw = Buffer.concat(chunks).toString("utf8");
384
+ var statusLine = raw.split("\r\n")[0] || "";
385
+ var m = /^HTTP\/1\.\d (\d{3})/.exec(statusLine);
386
+ var sep = raw.indexOf("\r\n\r\n");
387
+ var body = sep === -1 ? "" : raw.slice(sep + 4);
388
+ resolve({ statusCode: m ? parseInt(m[1], 10) : 0, body: body, raw: raw });
389
+ });
390
+ sock.on("error", reject);
391
+ });
392
+ }
393
+
394
+ // Path-traversal refusal — a file OUTSIDE the served root must never be
395
+ // reachable via `../` escapes, percent-encoded escapes, or NUL injection.
396
+ // The secret is written to the parent of `root` so any successful escape
397
+ // would disclose it.
398
+ async function testPathTraversalRefused() {
399
+ var ctx = await _server();
400
+ // Sibling secret one level above the served root.
401
+ var parent = path.dirname(ctx.dir);
402
+ var secretName = "blamejs-static-secret-" + process.pid + ".txt";
403
+ var secretAbs = path.join(parent, secretName);
404
+ var secretBody = "TOP-SECRET-" + Date.now();
405
+ fs.writeFileSync(secretAbs, secretBody);
406
+ // A legit in-root file to prove the server is otherwise functional.
407
+ _writeFile(ctx.dir, "ok.txt", "in-root");
408
+ var srv = await ctx.start({ contentSafety: null });
409
+ try {
410
+ // Raw `../` traversal (delivered verbatim over a raw socket).
411
+ var rawTrav = await _getRaw(srv.port, "/../" + secretName);
412
+ check("traversal: raw ../ does not disclose the sibling secret",
413
+ rawTrav.body.indexOf(secretBody) === -1);
414
+ check("traversal: raw ../ refused (not 200-with-secret)",
415
+ rawTrav.statusCode !== 200 || rawTrav.body.indexOf(secretBody) === -1);
416
+
417
+ // Deeper raw traversal.
418
+ var rawDeep = await _getRaw(srv.port, "/sub/../../" + secretName);
419
+ check("traversal: nested raw ../../ does not disclose the secret",
420
+ rawDeep.body.indexOf(secretBody) === -1);
421
+
422
+ // Percent-encoded `..%2f` traversal (the server decodes, then must
423
+ // still refuse).
424
+ var encTrav = await _get(srv.port, "/%2e%2e%2f" + secretName);
425
+ check("traversal: percent-encoded %2e%2e%2f refused (404/next)",
426
+ encTrav.statusCode === 404);
427
+ check("traversal: percent-encoded escape does not disclose the secret",
428
+ encTrav.body.toString("utf8").indexOf(secretBody) === -1);
429
+
430
+ // Double percent-encoded dot-dot.
431
+ var encTrav2 = await _get(srv.port, "/%2e%2e/%2e%2e/" + secretName);
432
+ check("traversal: %2e%2e/%2e%2e/ does not disclose the secret",
433
+ encTrav2.body.toString("utf8").indexOf(secretBody) === -1);
434
+
435
+ // NUL-byte injection — refused before any fs op.
436
+ var nul = await _get(srv.port, "/ok.txt%00.png");
437
+ check("traversal: NUL-byte path refused (404/next)",
438
+ nul.statusCode === 404);
439
+
440
+ // Sanity: the legit in-root file still serves.
441
+ var ok = await _get(srv.port, "/ok.txt");
442
+ check("traversal: in-root file still serves after refusals",
443
+ ok.statusCode === 200 && ok.body.toString("utf8") === "in-root");
444
+ } finally {
445
+ srv.close();
446
+ fs.rmSync(secretAbs, { force: true });
447
+ ctx.cleanup();
448
+ }
449
+ }
450
+
451
+ // Success path — a legitimately nested file under root is served with its
452
+ // bytes intact. Confirms the confinement barrier does not over-refuse.
453
+ async function testNestedFileServed() {
454
+ var ctx = await _server();
455
+ _writeFile(ctx.dir, "assets/css/site.css", "body{color:#000}");
456
+ var srv = await ctx.start({ contentSafety: null });
457
+ try {
458
+ var r = await _get(srv.port, "/assets/css/site.css");
459
+ check("nested: deep in-root file serves 200",
460
+ r.statusCode === 200);
461
+ check("nested: bytes intact",
462
+ r.body.toString("utf8") === "body{color:#000}");
463
+ check("nested: correct content-type",
464
+ /text\/css/.test(r.headers["content-type"] || ""));
465
+ } finally {
466
+ srv.close();
467
+ ctx.cleanup();
468
+ }
469
+ }
470
+
471
+ // A directory-index request resolves to the index file inside root and
472
+ // serves it — exercises the re-confinement after the index-file join.
473
+ async function testDirectoryIndexServed() {
474
+ var ctx = await _server();
475
+ _writeFile(ctx.dir, "docs/index.html", "<h1>docs</h1>");
476
+ var srv = await ctx.start({ contentSafety: null });
477
+ try {
478
+ var r = await _get(srv.port, "/docs/");
479
+ check("dir-index: index.html served for a directory request",
480
+ r.statusCode === 200 && /docs/.test(r.body.toString("utf8")));
481
+ } finally {
482
+ srv.close();
483
+ ctx.cleanup();
484
+ }
485
+ }
486
+
302
487
  async function run() {
303
488
  await testForceAttachmentDefaultOff();
304
489
  await testForceAttachmentOnHtml();
@@ -309,10 +494,17 @@ async function run() {
309
494
  await testForceAttachmentSvgWithSanitizerInlineAllowed();
310
495
  await testForceAttachmentPdfDefaultDownload();
311
496
  await testForceAttachmentPdfOptInInline();
497
+ await testMountTypeUserContentForcesDownloadByDefault();
498
+ await testMountTypeCuratedKeepsInline();
499
+ await testMountTypeExplicitOverrideWins();
500
+ testMountTypeBadValueThrows();
312
501
  testRejectsUnknownOpts();
313
502
  await testOnErrorFiresOnRefusal();
314
503
  await testOnErrorThrowDoesNotCorruptResponse();
315
504
  testOnErrorRejectsNonFunction();
505
+ await testPathTraversalRefused();
506
+ await testNestedFileServed();
507
+ await testDirectoryIndexServed();
316
508
  }
317
509
 
318
510
  module.exports = { run: run };
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * websocket _parseExtensionHeader — Sec-WebSocket-Extensions parser
4
+ * (RFC 7692 permessage-deflate negotiation feeds off this).
5
+ *
6
+ * Focus: the extension-parameter name is client-controlled, so the
7
+ * params map is built from [name, value] pairs through Object.fromEntries
8
+ * onto a null-prototype object — never a computed-write (`params[name] =
9
+ * value`) sink. Verifies a hostile `__proto__` / `constructor` /
10
+ * `prototype` parameter name does NOT pollute Object.prototype
11
+ * (CWE-915 / CWE-1321) and that the legitimate parameter shape is
12
+ * unchanged.
13
+ *
14
+ * Run standalone: `node test/layer-0-primitives/websocket-extension-header.test.js`
15
+ * Or via smoke: `node test/smoke.js`
16
+ */
17
+
18
+ var helpers = require("../helpers");
19
+ var b = helpers.b;
20
+ var check = helpers.check;
21
+
22
+ var _parseExtensionHeader = b.websocket._parseExtensionHeader;
23
+
24
+ function testParsesParamsSuccessPath() {
25
+ var out = _parseExtensionHeader(
26
+ "permessage-deflate; client_max_window_bits=12; server_no_context_takeover, foo; bar=baz"
27
+ );
28
+ check("ext: two extensions parsed", Array.isArray(out) && out.length === 2);
29
+ check("ext: first name lower-cased", out[0].name === "permessage-deflate");
30
+ check("ext: param value parsed", out[0].params.client_max_window_bits === "12");
31
+ check("ext: valueless param is boolean true",
32
+ out[0].params.server_no_context_takeover === true);
33
+ check("ext: second extension name", out[1].name === "foo");
34
+ check("ext: second extension param", out[1].params.bar === "baz");
35
+ }
36
+
37
+ function testParamsMapHasNullPrototype() {
38
+ var out = _parseExtensionHeader("permessage-deflate; client_max_window_bits=15");
39
+ check("ext: params map has null prototype",
40
+ Object.getPrototypeOf(out[0].params) === null);
41
+ }
42
+
43
+ function testRejectsPoisonedParamNames() {
44
+ var out = _parseExtensionHeader(
45
+ "permessage-deflate; __proto__=evil; constructor=evil2; prototype=evil3; client_max_window_bits=10"
46
+ );
47
+ var params = out[0].params;
48
+ check("ext: __proto__ param dropped",
49
+ !Object.prototype.hasOwnProperty.call(params, "__proto__"));
50
+ check("ext: constructor param dropped",
51
+ !Object.prototype.hasOwnProperty.call(params, "constructor"));
52
+ check("ext: prototype param dropped",
53
+ !Object.prototype.hasOwnProperty.call(params, "prototype"));
54
+ check("ext: legitimate param still present alongside dropped ones",
55
+ params.client_max_window_bits === "10");
56
+ check("ext: Object.prototype not polluted",
57
+ ({}).evil === undefined && Object.prototype.evil === undefined);
58
+ }
59
+
60
+ function testProtoValueViaFromEntriesDoesNotEscalate() {
61
+ // Even if a future grammar change let `__proto__` reach the pair list,
62
+ // a null-prototype accumulator + Object.fromEntries materialization
63
+ // can only create an OWN `__proto__` property — it can never mutate the
64
+ // prototype chain. The current parser drops the name outright; this is
65
+ // the structural backstop.
66
+ var before = Object.prototype.toString;
67
+ _parseExtensionHeader("permessage-deflate; __proto__=x");
68
+ check("ext: Object.prototype.toString intact after parse",
69
+ Object.prototype.toString === before);
70
+ check("ext: a fresh object still inherits toString",
71
+ typeof ({}).toString === "function");
72
+ }
73
+
74
+ async function run() {
75
+ testParsesParamsSuccessPath();
76
+ testParamsMapHasNullPrototype();
77
+ testRejectsPoisonedParamNames();
78
+ testProtoValueViaFromEntriesDoesNotEscalate();
79
+ }
80
+
81
+ module.exports = { run: run };
82
+
83
+ if (require.main === module) {
84
+ run().then(
85
+ function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
86
+ function (e) { console.error("FAIL:", e.message); process.exit(1); }
87
+ );
88
+ }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ // #127: a task queued behind a timing-out (or erroring) task must not be
3
+ // dropped by slot recycling. _onTaskTimeout / _onWorkerError called
4
+ // _finishTask BEFORE marking the slot recycling. _finishTask sets slot.busy
5
+ // = false and drains the queue, so _findIdleSlot handed the freshly-queued
6
+ // task to the very slot whose worker was about to be terminated — the task's
7
+ // message went to a dying worker and came back as workerpool/worker-exit
8
+ // (or hung), even though a healthy replacement worker was about to spawn.
9
+ //
10
+ // This is deterministic (not a flaky race): _finishTask -> _drainQueue runs
11
+ // synchronously inside _onTaskTimeout, before _recycleWorker. The fix marks
12
+ // the slot recycling BEFORE _finishTask, so the drain skips the dying slot
13
+ // and the queued task waits for the replacement worker.
14
+ //
15
+ // RED on the buggy tree: the queued echo rejects with workerpool/worker-exit.
16
+ // GREEN after the fix: it runs on the replacement worker and returns its
17
+ // result.
18
+
19
+ var path = require("path");
20
+ var helpers = require("../helpers");
21
+ var b = helpers.b;
22
+ var check = helpers.check;
23
+
24
+ var FIXTURE = path.resolve(__dirname, "..", "fixtures", "worker-pool", "echo.js");
25
+
26
+ async function run() {
27
+ // Single worker so the second task MUST queue behind the first, and a
28
+ // short timeout so the busy-loop task is reaped quickly.
29
+ var pool = b.workerPool.create(FIXTURE, { size: 1, taskTimeoutMs: 400 });
30
+ try {
31
+ // Task A occupies the only worker and busy-loops until it is reaped by
32
+ // taskTimeoutMs. Its rejection (workerpool/timeout) is expected.
33
+ var aErr = null;
34
+ var aPromise = pool.run({ kind: "loop" }).catch(function (e) { aErr = e; });
35
+
36
+ // Task B queues behind A (worker busy). It is a trivial echo that must
37
+ // succeed once a healthy worker is available.
38
+ var bResult = null;
39
+ var bError = null;
40
+ var bPromise = pool.run({ kind: "echo", payload: { v: 42 } })
41
+ .then(function (r) { bResult = r; })
42
+ .catch(function (e) { bError = e; });
43
+
44
+ // Bound the wait so a hung B fails the test rather than hanging the suite.
45
+ await Promise.race([
46
+ Promise.all([aPromise, bPromise]),
47
+ helpers.waitUntil(function () { return bResult !== null || bError !== null; },
48
+ { timeoutMs: 8000, label: "#127: queued task settles after the slot recycles" }),
49
+ ]);
50
+
51
+ check("#127 the timing-out task A is reaped (timeout, as expected)",
52
+ aErr && aErr.code === "workerpool/timeout");
53
+ check("#127 the task queued behind it is NOT dropped by slot recycling",
54
+ bError === null);
55
+ check("#127 the queued task runs on the replacement worker and returns its result",
56
+ bResult && bResult.v === 42);
57
+ } finally {
58
+ pool.terminate();
59
+ }
60
+ }
61
+
62
+ module.exports = { run: run };
63
+ if (require.main === module) {
64
+ run().then(function () { process.exit(0); })
65
+ .catch(function (err) { process.stderr.write(String(err && err.stack || err) + "\n"); process.exit(1); });
66
+ }
@@ -259,6 +259,88 @@ async function testHashAlgoOptArgon2id() {
259
259
  check("argon2id: wrong secret returns null", wrong === null);
260
260
  }
261
261
 
262
+ async function testRotateOnVerifyUpgradesHashAlgo() {
263
+ // A row stored under a non-active hash algorithm is transparently
264
+ // re-hashed to the active algorithm on the next successful (leader)
265
+ // verify — the rotate-on-next-verify that credentialHash documents.
266
+ var ns = "rehash-mig";
267
+ // A store configured for argon2id issues the key (the "legacy" shape).
268
+ var legacy = b.apiKey.create({ namespace: ns, hashAlgo: "argon2id" });
269
+ var issued = await legacy.issue({ ownerId: "u1" });
270
+ var pre = await b.clusterStorage.executeOne(
271
+ "SELECT secretHash FROM _blamejs_api_keys WHERE id = ?", [ns + ":" + issued.id]);
272
+ check("rotate-on-verify: stored under argon2id before verify",
273
+ b.credentialHash.inspect(pre.secretHash).algoName === "argon2id");
274
+
275
+ // A default (shake256) store verifies the SAME key on the same table.
276
+ var active = b.apiKey.create({ namespace: ns }); // hashAlgo defaults to shake256
277
+ var record = await active.verify(issued.key);
278
+ check("rotate-on-verify: credential still verifies under the legacy hash",
279
+ record !== null && record.id === issued.id);
280
+
281
+ // The at-rest hash is now the active shake256 envelope — upgraded in place,
282
+ // with no API/return-value change for the caller.
283
+ var post = await b.clusterStorage.executeOne(
284
+ "SELECT secretHash FROM _blamejs_api_keys WHERE id = ?", [ns + ":" + issued.id]);
285
+ var info = b.credentialHash.inspect(post.secretHash);
286
+ check("rotate-on-verify: stored hash upgraded to shake256", info.algoName === "shake256");
287
+ check("rotate-on-verify: upgraded envelope no longer needs rehash",
288
+ b.credentialHash.needsRehash(post.secretHash, { algo: "shake256" }) === false);
289
+
290
+ // The original secret still verifies under the upgraded hash.
291
+ var again = await active.verify(issued.key);
292
+ check("rotate-on-verify: re-verify under the upgraded hash succeeds",
293
+ again !== null && again.id === issued.id);
294
+ }
295
+
296
+ async function testRehashOnVerifyDoesNotClobberConcurrentRotate() {
297
+ // Rotate-on-verify reads the stored hash, then writes the upgraded hash in a
298
+ // later UPDATE. If a rotate() lands between the read and the write, a
299
+ // row-id-only UPDATE would overwrite the freshly rotated secret with the OLD
300
+ // secret's re-hash — invalidating the new token while the old one keeps
301
+ // working. The upgrade write carries a compare-and-swap on the exact hash
302
+ // that was verified, so a row whose secretHash changed under it is untouched.
303
+ var credentialHashMod = require("../../lib/credential-hash");
304
+ var ns = "rehash-race";
305
+ var legacy = b.apiKey.create({ namespace: ns, hashAlgo: "argon2id" });
306
+ var issued = await legacy.issue({ ownerId: "u1" });
307
+ var rowId = ns + ":" + issued.id;
308
+
309
+ // The hash a concurrent rotate() would install mid-flight (a distinct envelope).
310
+ var rotatedHash = await credentialHashMod.hash("ab".repeat(32), { algo: "shake256" });
311
+
312
+ var active = b.apiKey.create({ namespace: ns }); // active algo shake256 → triggers the re-hash
313
+ var realHash = credentialHashMod.hash;
314
+ var injected = false;
315
+ // Land a concurrent rotation inside the re-hash's async window: overwrite the
316
+ // stored secretHash after the OLD value was read but before the upgrade UPDATE.
317
+ // api-key reads credentialHash.hash off this module object at call time, so the
318
+ // patch intercepts exactly the rotate-on-verify re-hash (issue() above ran
319
+ // before the patch and is unaffected).
320
+ credentialHashMod.hash = async function (secretHex, opts) {
321
+ var out = await realHash.call(credentialHashMod, secretHex, opts);
322
+ if (!injected) {
323
+ injected = true;
324
+ await b.clusterStorage.execute(
325
+ "UPDATE _blamejs_api_keys SET secretHash = ? WHERE id = ?", [rotatedHash, rowId]);
326
+ }
327
+ return out;
328
+ };
329
+ var record;
330
+ try {
331
+ record = await active.verify(issued.key);
332
+ } finally {
333
+ credentialHashMod.hash = realHash;
334
+ }
335
+ check("rehash-race: the credential still verifies under the legacy hash",
336
+ record !== null && record.id === issued.id);
337
+
338
+ var post = await b.clusterStorage.executeOne(
339
+ "SELECT secretHash FROM _blamejs_api_keys WHERE id = ?", [rowId]);
340
+ check("rehash-race: the concurrent rotation's hash survives — re-hash did NOT clobber it",
341
+ post.secretHash === rotatedHash);
342
+ }
343
+
262
344
  async function testGetById() {
263
345
  var keys = b.apiKey.create({ namespace: "get-test" });
264
346
  var issued = await keys.issue({ ownerId: "u1" });
@@ -690,6 +772,8 @@ async function run() {
690
772
  await testHardRotateClearsSecondary();
691
773
  await testEnvelopeFormatPersisted();
692
774
  await testHashAlgoOptArgon2id();
775
+ await testRotateOnVerifyUpgradesHashAlgo();
776
+ await testRehashOnVerifyDoesNotClobberConcurrentRotate();
693
777
  await testListForOwner();
694
778
  await testGetById();
695
779
  await testTrackLastUsedAt();