@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
@@ -247,6 +247,21 @@ function create(req, res, opts) {
247
247
  // proxyBuffer: false to suppress the nginx-specific header.
248
248
  var proxyBuffer = opts.proxyBuffer !== false;
249
249
 
250
+ // Slow-consumer bound. SSE is server-push: when a client stalls, res.write()
251
+ // returns false but the app keeps pushing, so Node buffers the unsent bytes
252
+ // in res.writableLength without limit — one stuck connection grows the heap
253
+ // until exhaustion (memory-exhaustion DoS). Cap the per-channel buffer and
254
+ // evict the slow consumer past it. A healthy client (writableLength ~0) is
255
+ // never affected. Config-time input → throw on a bad value.
256
+ var maxBufferedBytes = opts.maxBufferedBytes;
257
+ if (maxBufferedBytes === undefined) maxBufferedBytes = C.BYTES.mib(1);
258
+ if (typeof maxBufferedBytes !== "number" || !isFinite(maxBufferedBytes) ||
259
+ maxBufferedBytes <= 0 || Math.floor(maxBufferedBytes) !== maxBufferedBytes) {
260
+ throw errorClass.factory("sse/bad-opts",
261
+ "sse.create: maxBufferedBytes must be a positive integer byte count (got " +
262
+ JSON.stringify(maxBufferedBytes) + ")");
263
+ }
264
+
250
265
  var lastEventId = _readLastEventId(req);
251
266
 
252
267
  // Headers. text/event-stream is the contract; Cache-Control: no-cache
@@ -275,6 +290,17 @@ function create(req, res, opts) {
275
290
  "sse.send: channel closed");
276
291
  }
277
292
  res.write(s);
293
+ // res.writableLength is the count of bytes Node has buffered but not yet
294
+ // flushed to the socket. A healthy client drains it (≈0); a stalled one
295
+ // lets it climb. Past the per-channel cap, evict the slow consumer rather
296
+ // than buffer without bound. h2 streams + h1 responses both expose it.
297
+ var buffered = (typeof res.writableLength === "number") ? res.writableLength : 0;
298
+ if (buffered > maxBufferedBytes) {
299
+ close({ reason: "backpressure-exceeded" });
300
+ throw errorClass.factory("sse/backpressure",
301
+ "sse.send: client too slow — buffered " + buffered +
302
+ " bytes exceeds maxBufferedBytes " + maxBufferedBytes + "; channel closed");
303
+ }
278
304
  }
279
305
 
280
306
  function send(eventOpts) {
@@ -148,12 +148,27 @@ var IPV6_6TO4_PREFIX = _ipv6ToBytes("2002::");
148
148
  // or attempted exfil to a sinkhole.
149
149
  var IPV6_DISCARD_PREFIX = _ipv6ToBytes("100::");
150
150
 
151
- // ---- Cloud metadata addresses (string-equality, exact match) ----
151
+ // ---- Cloud metadata addresses (matched on CANONICAL bytes, not string) ----
152
+ // The documentation strings below are the human-readable canonical forms.
153
+ // Matching is byte-canonical (see _isCloudMetadataAddr): an IPv6 address has
154
+ // many textual representations (compressed `::`, fully-expanded
155
+ // `fd00:ec2:0:0:0:0:0:254`, mixed-case) that all decode to the same 16 bytes.
156
+ // A string-equality membership test matched only ONE spelling, so a hostile
157
+ // (or merely DoH-decoded — network-dns.js emits the expanded form) answer of
158
+ // `fd00:ec2:0:0:0:0:0:254` slipped past as "private" and rode the documented
159
+ // `allowInternal:true` waiver straight into the IMDS credential endpoint.
152
160
  var CLOUD_METADATA_IPS = [
153
161
  "169.254.169.254", // AWS, GCP, Azure, OpenStack, DO
154
162
  "169.254.170.2", // AWS ECS task role
155
163
  "fd00:ec2::254", // AWS IMDS over IPv6
156
164
  ];
165
+ // Canonical byte forms of the metadata IPs — v4 as a 4-byte Buffer, v6 as a
166
+ // 16-byte Buffer. Built once at load via the same parsers classify() uses,
167
+ // so every textual representation that decodes to these bytes is caught.
168
+ var CLOUD_METADATA_BYTES = CLOUD_METADATA_IPS.map(function (ip) {
169
+ var fam = net.isIP(ip);
170
+ return fam === 4 ? _ipv4ToBytes(ip) : _ipv6ToBytes(ip);
171
+ });
157
172
 
158
173
  // ---- Helpers ----
159
174
 
@@ -180,6 +195,14 @@ function _ipv4ToInt(ip) {
180
195
  nums[3];
181
196
  }
182
197
 
198
+ function _ipv4ToBytes(ip) {
199
+ // Canonical 4-byte form of an IPv4 address. Returns null on malformed
200
+ // input so a metadata-membership test never matches garbage.
201
+ var n = _ipv4ToInt(ip);
202
+ if (!Number.isFinite(n)) return null;
203
+ return Buffer.from([(n >>> 24) & 0xff, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff]);
204
+ }
205
+
183
206
  function _ipv6ToBytes(ip) {
184
207
  // Node's net.isIPv6 returns 6 for valid IPv6; we then expand
185
208
  // shorthand via manual parsing. node:net doesn't export an
@@ -224,6 +247,105 @@ function _expandIpv6(ip) {
224
247
  return left.concat(fill).concat(right);
225
248
  }
226
249
 
250
+ function _ipv6BytesToString(bytes) {
251
+ // RFC 5952 §4 canonical textual form from 16 canonical bytes: lower-hex,
252
+ // no leading zeros per group, the LONGEST run of two-or-more zero groups
253
+ // compressed to "::" (leftmost run on a length tie — §4.2.3), and the
254
+ // shortened-but-not-IPv4-dotted form (the framework keeps IPv4-mapped as
255
+ // pure hex so every mapped spelling collapses to one string). Driven off
256
+ // the same 16-byte buffer classify() matches on, so the emitted string and
257
+ // the security verdict can never disagree about which address this is.
258
+ var groups = [];
259
+ for (var i = 0; i < IPV6_GROUPS; i++) {
260
+ groups.push(((bytes[i * 2] << 8) | bytes[i * 2 + 1]) & 0xffff);
261
+ }
262
+ var bestStart = -1, bestLen = 0, curStart = -1, curLen = 0;
263
+ for (var g = 0; g < IPV6_GROUPS; g++) {
264
+ if (groups[g] === 0) {
265
+ if (curStart === -1) { curStart = g; curLen = 1; } else { curLen += 1; }
266
+ if (curLen > bestLen) { bestLen = curLen; bestStart = curStart; }
267
+ } else {
268
+ curStart = -1;
269
+ curLen = 0;
270
+ }
271
+ }
272
+ // A single zero group is NOT compressed (RFC 5952 §4.2.2).
273
+ if (bestLen < 2) bestStart = -1;
274
+ var parts = [];
275
+ for (var k = 0; k < IPV6_GROUPS; k++) {
276
+ if (bestStart !== -1 && k === bestStart) {
277
+ parts.push("");
278
+ k += bestLen - 1;
279
+ // A run reaching the final group needs a trailing empty part so the
280
+ // join yields the "::"-terminated form (e.g. fe80:: not fe80:).
281
+ if (k === IPV6_GROUPS - 1) parts.push("");
282
+ continue;
283
+ }
284
+ parts.push(groups[k].toString(HEX_RADIX));
285
+ }
286
+ var out = parts.join(":");
287
+ // A run starting at group 0 needs a leading empty part ("::1", "::").
288
+ if (bestStart === 0) out = ":" + out;
289
+ return out;
290
+ }
291
+
292
+ /**
293
+ * @primitive b.ssrfGuard.canonicalizeHost
294
+ * @signature b.ssrfGuard.canonicalizeHost(host)
295
+ * @since 0.15.6
296
+ * @status stable
297
+ * @related b.ssrfGuard.classify, b.safeUrl.canonicalize
298
+ *
299
+ * Canonicalize a bare host string to its single comparable form for
300
+ * host allowlists, dedup keys, and SSRF pre-checks. A `net.isIP`-valid
301
+ * IP literal collapses to one canonical string: a dotted-quad IPv4
302
+ * stays dotted-quad; IPv6 in any zero-compression / mixed-case /
303
+ * IPv4-mapped spelling (`[0:0:0:0:0:ffff:7f00:1]`, `::FFFF:7F00:1`)
304
+ * becomes the RFC 5952 lower-hex compressed form. The IP bytes are
305
+ * parsed by the SAME routines `classify` matches on, so the canonical
306
+ * string and the SSRF verdict can never disagree about which address a
307
+ * host is.
308
+ *
309
+ * The numeric-base IPv4 decode (octal `0177.0.0.1`, hex `0x7f000001`,
310
+ * single-integer `2130706433`, shorthand `127.1`) is the WHATWG URL
311
+ * parser's job — `b.safeUrl.canonicalize` runs that FIRST and hands this
312
+ * primitive the already-decoded dotted-quad. This is the IP-byte + case
313
+ * layer, not the base decoder.
314
+ *
315
+ * A DNS name (not an IP literal) is lowercased and any trailing dot is
316
+ * stripped — `Example.COM.` → `example.com`. IDN A-label / U-label
317
+ * normalization is NOT done here (the WHATWG URL parser owns that via
318
+ * `b.safeUrl.canonicalize`).
319
+ * `[...]`-bracketed IPv6 input is accepted (brackets stripped); the
320
+ * returned IPv6 string is UNbracketed (the URL layer re-adds brackets).
321
+ *
322
+ * @example
323
+ * var b = require("blamejs");
324
+ * b.ssrfGuard.canonicalizeHost("[0:0:0:0:0:0:0:1]"); // → "::1"
325
+ * b.ssrfGuard.canonicalizeHost("::FFFF:7F00:1"); // → "::ffff:7f00:1"
326
+ * b.ssrfGuard.canonicalizeHost("Example.COM."); // → "example.com"
327
+ */
328
+ function canonicalizeHost(host) {
329
+ if (typeof host !== "string" || host.length === 0) return host;
330
+ var bare = host.replace(/^\[|\]$/g, "");
331
+ var family = net.isIP(bare);
332
+ if (family === 4) {
333
+ var v4 = _ipv4ToBytes(bare);
334
+ if (v4) return v4[0] + "." + v4[1] + "." + v4[2] + "." + v4[3];
335
+ return bare.toLowerCase();
336
+ }
337
+ if (family === 6) {
338
+ return _ipv6BytesToString(_ipv6ToBytes(bare));
339
+ }
340
+ // Not an IP literal — DNS name. Lowercase + strip a single trailing dot
341
+ // (the root-label dot is DNS-equivalent but breaks string comparison).
342
+ var name = bare.toLowerCase();
343
+ if (name.length > 1 && name.charAt(name.length - 1) === ".") {
344
+ name = name.slice(0, name.length - 1);
345
+ }
346
+ return name;
347
+ }
348
+
227
349
  function _cidrIpv4Match(cidr, ip) {
228
350
  var slash = cidr.indexOf("/");
229
351
  if (slash === -1) return false;
@@ -309,7 +431,10 @@ function classify(ip) {
309
431
  var family = net.isIP(ip);
310
432
  if (family === 0) return null;
311
433
 
312
- if (CLOUD_METADATA_IPS.indexOf(ip) !== -1) return "cloud-metadata";
434
+ // Cloud-metadata IPs are matched on their canonical byte form so every
435
+ // textual spelling (compressed `::`, fully-expanded zero-runs, mixed
436
+ // case) is caught — a string-equality test matched one spelling only.
437
+ if (_isCloudMetadataAddr(ip, family)) return "cloud-metadata";
313
438
 
314
439
  if (family === 4) {
315
440
  var ipInt = _ipv4ToInt(ip);
@@ -349,6 +474,24 @@ function classify(ip) {
349
474
  return null;
350
475
  }
351
476
 
477
+ // Canonical-bytes membership test for the cloud-metadata IP set. An IP
478
+ // matches iff its parsed bytes equal one of CLOUD_METADATA_BYTES, regardless
479
+ // of textual representation. This is the unconditional metadata gate — it
480
+ // must NOT be string-based, because IPv6 has many spellings of the same
481
+ // address (the DoH resolver in network-dns.js, for instance, emits the
482
+ // fully-expanded `fd00:ec2:0:0:0:0:0:254` rather than the compressed form).
483
+ function _isCloudMetadataAddr(ip, family) {
484
+ var fam = typeof family === "number" ? family : net.isIP(ip);
485
+ if (fam === 0) return false;
486
+ var bytes = fam === 4 ? _ipv4ToBytes(ip) : _ipv6ToBytes(ip);
487
+ if (!bytes) return false;
488
+ for (var i = 0; i < CLOUD_METADATA_BYTES.length; i++) {
489
+ var ref = CLOUD_METADATA_BYTES[i];
490
+ if (ref && ref.length === bytes.length && _bufEqual(bytes, ref)) return true;
491
+ }
492
+ return false;
493
+ }
494
+
352
495
  function _bufEqual(a, b) {
353
496
  // Compares Buffer-like byte arrays for equality. The buffers here
354
497
  // are IP addresses, not secrets, so the comparison doesn't need
@@ -766,8 +909,11 @@ function checkUrlTextual(url, opts) {
766
909
  // If the textual hostname IS an IP literal AND matches a cloud-
767
910
  // metadata IP, refuse — even with `allowInternal: true` and a proxy.
768
911
  // Metadata IPs leak instance credentials (AWS IMDS, GCP, Azure) and
769
- // are not a configuration knob.
770
- if (net.isIP(host) && CLOUD_METADATA_IPS.indexOf(host) !== -1) {
912
+ // are not a configuration knob. Matched on canonical bytes so a
913
+ // non-canonical IPv6 spelling (compressed / expanded / mixed-case)
914
+ // can't slip the textual gate the way it slipped classify().
915
+ var hostFamily = net.isIP(host);
916
+ if (hostFamily !== 0 && _isCloudMetadataAddr(host, hostFamily)) {
771
917
  throw new ErrorClass(
772
918
  "URL '" + parsed.toString() + "' resolves to cloud-metadata IP " + host +
773
919
  " — refused unconditionally (not overridable via allowInternal + proxy)",
@@ -779,6 +925,7 @@ function checkUrlTextual(url, opts) {
779
925
 
780
926
  module.exports = {
781
927
  classify: classify,
928
+ canonicalizeHost: canonicalizeHost,
782
929
  cidrContains: cidrContains,
783
930
  checkUrl: checkUrl,
784
931
  checkUrlTextual: checkUrlTextual,
@@ -126,12 +126,32 @@ var DEFAULTS = Object.freeze({
126
126
  // serve event is the audit-worthy act, not a precursor.
127
127
  auditSuccess: true,
128
128
  auditFailures: true,
129
+ // mountType — declares what KIND of content this mount serves, so
130
+ // the stored-XSS-relevant defaults follow the typing instead of being
131
+ // hand-flipped per mount (v0.15.0):
132
+ // "curated" (default) — operator-controlled assets (CSS / JS
133
+ // bundles / fonts / images). Inline render is required
134
+ // and safe because the operator authored the bytes;
135
+ // forceAttachmentForNonText defaults OFF.
136
+ // "user-content" — files written by end users / untrusted uploaders.
137
+ // A served .html / .js / .svg here is a stored-XSS
138
+ // vector, so forceAttachmentForNonText defaults ON —
139
+ // risky inline MIMEs are forced to download unless a
140
+ // sanitizer gate vouches for them (see
141
+ // `_shouldForceAttachment`). This is the conditional
142
+ // flip: a curated asset dir is never blindly forced to
143
+ // download; only a mount the operator TYPED as
144
+ // user-content gets the strict default.
145
+ // An explicit forceAttachmentForNonText always overrides the
146
+ // mountType-derived default.
147
+ mountType: "curated",
129
148
  // forceAttachmentForNonText — stored-XSS defense for user-upload
130
- // directories. Default OFF because operator-curated asset dirs
131
- // (CSS / JS bundles / fonts) need inline render. Opt in for
132
- // user-upload-backed mounts so HTML / JS / SVG without sanitizer
133
- // / PDF / archives are forced to download. See
134
- // `_shouldForceAttachment` below for the safe-render allowlist.
149
+ // directories. Default follows mountType: OFF for "curated" mounts
150
+ // (operator-curated CSS / JS bundles / fonts need inline render), ON for
151
+ // "user-content" mounts so HTML / JS / SVG without a sanitizer / PDF /
152
+ // archives are forced to download. Set explicitly to override the
153
+ // mountType-derived default either way. See `_shouldForceAttachment`
154
+ // below for the safe-render allowlist.
135
155
  forceAttachmentForNonText: false,
136
156
  // Companion knobs — when forceAttachmentForNonText is on, allow
137
157
  // image/svg+xml inline render IF an SVG sanitizer gate is wired
@@ -142,12 +162,68 @@ var DEFAULTS = Object.freeze({
142
162
  safeRenderPdf: false,
143
163
  });
144
164
 
165
+ // _assertInsideRoot — the path-confinement barrier (CWE-22 path
166
+ // traversal). Every filesystem sink in this module takes the path
167
+ // through this helper so the value handed to fs is built by
168
+ // `nodePath.join(root, rel)` where `rel` is a normalized, root-relative
169
+ // path with every leading `..` segment stripped — the canonical
170
+ // path-traversal sanitizer: normalize collapses interior `.`/`..`, the
171
+ // leading-`..` strip removes upward navigation, and joining a constant
172
+ // root with a sanitized relative segment yields a path that provably
173
+ // stays inside the served root. The barrier is intentionally re-applied
174
+ // at each sink (not just once at request entry) so the relationship
175
+ // between the sanitizer and the fs call is local + explicit.
176
+ //
177
+ // Returns the joined, confined absolute path on success, or `null` when
178
+ // the candidate is not a string, carries a NUL byte, or — after the
179
+ // leading-`..` strip — still carries a `..` segment or an absolute /
180
+ // drive-letter / UNC prefix that would smuggle outside root. A leading
181
+ // `..` escape is clamped into root by the strip (the file then 404s);
182
+ // any residual escape that survives normalization is refused. Callers
183
+ // MUST treat `null` as a refusal.
184
+ function _assertInsideRoot(root, candidate) {
185
+ if (typeof root !== "string" || root.length === 0) return null;
186
+ if (typeof candidate !== "string" || candidate.length === 0) return null;
187
+ if (candidate.indexOf("\0") !== -1) return null;
188
+ var rootResolved = nodePath.resolve(root);
189
+ // Reduce the candidate to a root-relative request, then run the
190
+ // recognized traversal sanitizer: normalize() collapses `.`/`..`
191
+ // segments; the replace strips every leading `..` so no upward
192
+ // navigation survives into the join below.
193
+ var requested = nodePath.isAbsolute(candidate)
194
+ ? nodePath.relative(rootResolved, candidate)
195
+ : candidate;
196
+ var rel = nodePath.normalize(requested).replace(/^(\.\.(\/|\\|$))+/, "");
197
+ if (rel.indexOf("\0") !== -1) return null;
198
+ // After the leading-`..` strip, a surviving `..` segment or an
199
+ // absolute / drive-letter / UNC residue would re-introduce an escape.
200
+ if (rel === ".." ||
201
+ rel.indexOf(".." + nodePath.sep) !== -1 ||
202
+ rel.indexOf(".." + (nodePath.sep === "/" ? "\\" : "/")) !== -1 ||
203
+ nodePath.isAbsolute(rel)) return null;
204
+ var safe = nodePath.join(rootResolved, rel);
205
+ // Defense-in-depth lexical containment alongside the join sanitizer.
206
+ if (safe !== rootResolved &&
207
+ !safe.startsWith(rootResolved + nodePath.sep)) return null;
208
+ return safe;
209
+ }
210
+
145
211
  // Module-level metadata cache. Entries hold:
146
212
  // { mtimeMs, size, etag, integrity, lastModified, sha3Hex, absPath }
147
213
  // Invalidated on mtime / size change.
148
214
  var _metaCache = new Map();
149
215
 
150
- async function _readMeta(absPath) {
216
+ // _readMeta — stat + hash a file for the conditional-request + SRI
217
+ // surface. `root` is passed alongside the candidate so the
218
+ // path-traversal barrier (CWE-22) is re-asserted at THIS sink: the
219
+ // value handed to fs.stat / fs.createReadStream is the confined return
220
+ // of `_assertInsideRoot`, not the request-derived candidate. Returns
221
+ // null when the candidate escapes root, is not a regular file, or
222
+ // cannot be read.
223
+ async function _readMeta(root, candidate) {
224
+ var absPath = _assertInsideRoot(root, candidate);
225
+ if (!absPath) return null;
226
+
151
227
  var stat;
152
228
  try { stat = await fsp.stat(absPath); }
153
229
  catch (_e) { return null; }
@@ -164,10 +240,9 @@ async function _readMeta(absPath) {
164
240
  var sri = nodeCrypto.createHash("sha384");
165
241
  var sha3 = nodeCrypto.createHash("sha3-512");
166
242
  await new Promise(function (resolve, reject) {
167
- // lgtm[js/path-injection] `absPath` is the sandbox-validated return
168
- // of `_resolveSafe` (lib/static.js:181 — lexical resolve + startsWith
169
- // root-prefix check + realpath escape guard + guardFilename gate).
170
- // Callers cannot reach `_readMeta` with an unvalidated path.
243
+ // The path handed to createReadStream is the confined output of
244
+ // `_assertInsideRoot(root, candidate)` above (lexical resolve +
245
+ // root-prefix containment), not the request-derived candidate.
171
246
  var s = nodeFs.createReadStream(absPath);
172
247
  s.on("data", function (chunk) { sri.update(chunk); sha3.update(chunk); });
173
248
  s.on("end", resolve);
@@ -192,10 +267,16 @@ async function _readMeta(absPath) {
192
267
  function _resolveSafe(root, requestedPath) {
193
268
  if (typeof requestedPath !== "string" || requestedPath.length === 0) return null;
194
269
  if (requestedPath.indexOf("\0") !== -1) return null;
195
- var resolved = nodePath.resolve(root, "." + requestedPath);
270
+ // Anchor the request path inside root with a leading "." so an
271
+ // absolute request (`/c:/windows`, `//host/share`, `/etc/passwd`)
272
+ // resolves as a same-named child of root rather than smuggling a
273
+ // fresh root; the containment barrier then proves the result stays
274
+ // inside root, refusing any `..`-driven escape. Drive-letter / UNC /
275
+ // reserved-name shapes that survive the resolve are caught by the
276
+ // guardFilename basename gate below.
277
+ var resolved = _assertInsideRoot(root, nodePath.resolve(root, "." + requestedPath));
278
+ if (!resolved) return null;
196
279
  var rootResolved = nodePath.resolve(root);
197
- if (resolved !== rootResolved &&
198
- !resolved.startsWith(rootResolved + nodePath.sep)) return null;
199
280
 
200
281
  // Symlink-escape defense — the lexical resolve above only sees the
201
282
  // requested path tokens; a symlink anywhere along `resolved` can
@@ -486,6 +567,15 @@ function _validateCreateOpts(opts) {
486
567
  validateOpts.optionalBoolean(opts.auditFailures, "staticServe.create: auditFailures", StaticServeError);
487
568
  validateOpts.optionalBoolean(opts.safeAttachmentForRiskyMimes,
488
569
  "staticServe.create: safeAttachmentForRiskyMimes", StaticServeError);
570
+ // mountType — config-time enum. A typo ("usercontent", "uploads")
571
+ // would silently fall back to the curated default and serve untrusted
572
+ // HTML inline, so THROW at boot rather than mis-type the mount.
573
+ if (opts.mountType !== undefined &&
574
+ opts.mountType !== "curated" && opts.mountType !== "user-content") {
575
+ throw _err("BAD_OPT",
576
+ "staticServe.create: mountType must be 'curated' (default) or " +
577
+ "'user-content'; got " + JSON.stringify(opts.mountType));
578
+ }
489
579
  validateOpts.optionalBoolean(opts.forceAttachmentForNonText,
490
580
  "staticServe.create: forceAttachmentForNonText", StaticServeError);
491
581
  validateOpts.optionalBoolean(opts.safeRenderSvg,
@@ -593,12 +683,18 @@ function _writeError(res, status, code, message, headers) {
593
683
  void code;
594
684
  }
595
685
 
596
- // integrity() — module-level helper, kept for compat with the v0.6 SRI use.
686
+ // integrity() — module-level helper, kept for compat with the v0.6 SRI
687
+ // use. Operates on an operator-supplied absolute path (a config/library
688
+ // call, not the request path): the file's own resolved path is both the
689
+ // confinement root and the candidate, so `_readMeta` re-applies the same
690
+ // barrier shape every other sink uses without narrowing the legitimate
691
+ // surface (any single file the operator names).
597
692
  async function integrity(absPath) {
598
693
  if (typeof absPath !== "string" || absPath.length === 0) {
599
694
  throw _err("BAD_OPT", "staticServe.integrity: absPath must be a non-empty string");
600
695
  }
601
- var meta = await _readMeta(nodePath.resolve(absPath));
696
+ var resolved = nodePath.resolve(absPath);
697
+ var meta = await _readMeta(resolved, resolved);
602
698
  if (!meta) throw _err("NOT_FOUND", "staticServe.integrity: file not found: " + absPath);
603
699
  return meta.integrity;
604
700
  }
@@ -617,7 +713,7 @@ function create(opts) {
617
713
  "maxBytesPerActorPerWindowMs", "maxBytesAllActorsPerWindowMs",
618
714
  "bandwidthWindowMs", "maxConcurrentDownloadsPerActor", "maxIdleMs",
619
715
  "contentSafety", "contentSafetyDisabledReason",
620
- "forceAttachmentForNonText", "safeRenderSvg", "safeRenderPdf",
716
+ "mountType", "forceAttachmentForNonText", "safeRenderSvg", "safeRenderPdf",
621
717
  ], "staticServe.create");
622
718
  _validateCreateOpts(opts);
623
719
  var cfg = validateOpts.applyDefaults(opts, DEFAULTS);
@@ -671,7 +767,16 @@ function create(opts) {
671
767
  var auditFailures = cfg.auditFailures;
672
768
  var acceptRanges = cfg.acceptRanges;
673
769
  var safeAttachment = !!cfg.safeAttachmentForRiskyMimes;
674
- var forceAttachmentForNonText = !!cfg.forceAttachmentForNonText;
770
+ // forceAttachmentForNonText default follows mountType (v0.15.0): a
771
+ // mount TYPED "user-content" forces risky inline MIMEs to download by
772
+ // default (stored-XSS defense for untrusted uploads); a "curated" mount
773
+ // keeps inline render. An explicit forceAttachmentForNonText overrides
774
+ // the mountType-derived default either way. The conditional flip never
775
+ // blindly force-attaches a curated asset dir.
776
+ var mountType = opts.mountType || "curated";
777
+ var forceAttachmentForNonText = opts.forceAttachmentForNonText !== undefined
778
+ ? !!opts.forceAttachmentForNonText
779
+ : (mountType === "user-content");
675
780
  var allowSvgRender = cfg.safeRenderSvg !== false;
676
781
  var allowPdfRender = !!cfg.safeRenderPdf;
677
782
  var perActorCap = cfg.maxBytesPerActorPerWindowMs;
@@ -736,8 +841,13 @@ function create(opts) {
736
841
 
737
842
  async function _checkMimeAllowlist(absPath, meta) {
738
843
  if (allowedFileTypes.length === 0 || !fileType) return { ok: true };
844
+ // Re-assert the root-confinement barrier at this fs read sink
845
+ // (CWE-22): the path passed to readFile is the confined return of
846
+ // `_assertInsideRoot`, not the request-derived candidate.
847
+ var confined = _assertInsideRoot(root, absPath);
848
+ if (!confined) return { ok: false, reason: "read-failed" };
739
849
  var sample;
740
- try { sample = await fsp.readFile(absPath, { flag: "r" }); }
850
+ try { sample = await fsp.readFile(confined, { flag: "r" }); }
741
851
  catch (_e) { return { ok: false, reason: "read-failed" }; }
742
852
  var detected = fileType.detect(sample.slice(0, C.BYTES.kib(64))) || {};
743
853
  if (!detected.mime) return { ok: false, reason: "indeterminate" };
@@ -799,13 +909,22 @@ function create(opts) {
799
909
  "Forbidden");
800
910
  }
801
911
 
802
- // Stat first to discover directory → index file.
912
+ // Stat first to discover directory → index file. The path handed to
913
+ // stat is the confined return of `_resolveSafe` above; re-assert the
914
+ // barrier so CodeQL sees the confinement local to this sink (CWE-22).
915
+ var statTarget = _assertInsideRoot(root, absPath);
916
+ if (!statTarget) return next();
803
917
  var stat;
804
- try { stat = await fsp.stat(absPath); }
918
+ try { stat = await fsp.stat(statTarget); }
805
919
  catch (_e) { return next(); }
806
920
  if (stat.isDirectory()) {
807
921
  if (!indexFile) return next();
808
- absPath = nodePath.join(absPath, indexFile);
922
+ // Re-confine after appending the index file — keeps every
923
+ // downstream sink (read-meta, content-safety open, serve stream)
924
+ // anchored inside root even if indexFile were ever made operator-
925
+ // overridable per request.
926
+ absPath = _assertInsideRoot(root, nodePath.join(absPath, indexFile));
927
+ if (!absPath) return next();
809
928
  }
810
929
 
811
930
  // Force-revoke (404 — opaque to clients)
@@ -833,7 +952,7 @@ function create(opts) {
833
952
  "retention_blocked", "Unavailable For Legal Reasons");
834
953
  }
835
954
 
836
- var meta = await _readMeta(absPath);
955
+ var meta = await _readMeta(root, absPath);
837
956
  if (!meta) return next();
838
957
 
839
958
  // MIME allowlist (415) — checked before sending bytes so a misnamed
@@ -867,16 +986,32 @@ function create(opts) {
867
986
  var ext = nodePath.extname(absPath).toLowerCase();
868
987
  var safetyGate = contentSafety[ext];
869
988
  if (safetyGate && typeof safetyGate.check === "function") {
870
- // CodeQL js/file-system-race defense single fd anchored to the
871
- // inode for the bytes we hand to the content-safety gate. The
872
- // absPath was anchored under root by _resolveSafe above; the
873
- // filehandle pattern binds size + read to the same inode so a
874
- // swap between stat (line 771) and read can't slip different
875
- // bytes past the gate.
989
+ // Single-fd read for the content-safety gate. Two defenses on
990
+ // one open:
991
+ // - CWE-22 path traversal: the open path is the confined
992
+ // return of `_assertInsideRoot(root, absPath)`, freshly
993
+ // re-derived from `nodePath.resolve(root, ...)`, not the
994
+ // request-derived candidate.
995
+ // - CWE-367 TOCTOU file-system race: the bytes the gate
996
+ // inspects come from THIS file descriptor — size and reads
997
+ // are taken from the same inode the open returned, so a path
998
+ // swap between the earlier directory stat and this read can't
999
+ // slip different bytes past the gate. O_NOFOLLOW (when the
1000
+ // platform defines it) additionally refuses to open the path
1001
+ // if its final component became a symlink after confinement.
1002
+ var gateConfined = _assertInsideRoot(root, absPath);
1003
+ if (!gateConfined) return next();
876
1004
  var gateBuf;
877
1005
  var gateHandle = null;
1006
+ var gateOpenFlags = nodeFs.constants.O_RDONLY |
1007
+ (nodeFs.constants.O_NOFOLLOW || 0);
878
1008
  try {
879
- gateHandle = await fsp.open(absPath, "r");
1009
+ // Explicit owner-only mode (0o600). The flags are read-only
1010
+ // (O_RDONLY, no O_CREAT) so the mode is inert on disk, but
1011
+ // pinning it owner-only keeps this open out of the insecure-
1012
+ // temp-file class (CWE-377): no world/group-accessible
1013
+ // creation can ever ride this code path.
1014
+ gateHandle = await fsp.open(gateConfined, gateOpenFlags, 0o600);
880
1015
  var gateStat = await gateHandle.stat();
881
1016
  gateBuf = Buffer.alloc(gateStat.size);
882
1017
  var gateRead = 0;
@@ -1151,6 +1286,18 @@ function create(opts) {
1151
1286
  return;
1152
1287
  }
1153
1288
 
1289
+ // Re-assert the root-confinement barrier at the serve sink (CWE-22)
1290
+ // BEFORE any 200/206 headers go on the wire: the path handed to
1291
+ // createReadStream is the confined return of `_assertInsideRoot`,
1292
+ // freshly re-derived from `nodePath.resolve(root, ...)`, not the
1293
+ // request-derived candidate. A candidate that escapes root refuses
1294
+ // opaquely (404) — it cannot reach the stream.
1295
+ var streamTarget = _assertInsideRoot(root, absPath);
1296
+ if (!streamTarget) {
1297
+ stats.failures += 1;
1298
+ return writeErr(res, HTTP.NOT_FOUND, "not_found", "Not Found");
1299
+ }
1300
+
1154
1301
  res.writeHead(status, headers);
1155
1302
 
1156
1303
  // Acquire concurrency slot (released on stream end / error / abort).
@@ -1163,11 +1310,7 @@ function create(opts) {
1163
1310
  }
1164
1311
 
1165
1312
  var streamOpts = range ? { start: range.start, end: range.end } : {};
1166
- // lgtm[js/path-injection] `absPath` is the sandbox-validated return
1167
- // of `_resolveSafe` (lib/static.js:181 — lexical resolve + startsWith
1168
- // root-prefix check + realpath escape guard + guardFilename gate).
1169
- // The request-serve path rejects with 404 before reaching this stream.
1170
- var fileStream = nodeFs.createReadStream(absPath, streamOpts);
1313
+ var fileStream = nodeFs.createReadStream(streamTarget, streamOpts);
1171
1314
 
1172
1315
  // Idle timeout — close the connection if the client stalls. Pattern is
1173
1316
  // a deadline-style debounce (clearTimeout + setTimeout) tied directly