@blamejs/blamejs-shop 0.0.44

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 (1220) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +17 -0
  3. package/README.md +117 -0
  4. package/SECURITY.md +139 -0
  5. package/lib/admin.js +952 -0
  6. package/lib/analytics.js +267 -0
  7. package/lib/cart.js +279 -0
  8. package/lib/catalog-import.js +344 -0
  9. package/lib/catalog.js +769 -0
  10. package/lib/checkout.js +320 -0
  11. package/lib/config.js +151 -0
  12. package/lib/customers.js +322 -0
  13. package/lib/email.js +242 -0
  14. package/lib/externaldb-d1.js +283 -0
  15. package/lib/index.js +57 -0
  16. package/lib/inventory-alerts.js +198 -0
  17. package/lib/newsletter.js +142 -0
  18. package/lib/order.js +380 -0
  19. package/lib/payment.js +318 -0
  20. package/lib/pricing.js +185 -0
  21. package/lib/r2-bridge.js +169 -0
  22. package/lib/shipping.js +185 -0
  23. package/lib/storefront.js +2160 -0
  24. package/lib/subscriptions.js +410 -0
  25. package/lib/tax.js +161 -0
  26. package/lib/theme.js +194 -0
  27. package/lib/vendor/MANIFEST.json +19 -0
  28. package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +23 -0
  29. package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +34 -0
  30. package/lib/vendor/blamejs/.clusterfuzzlite/project.yaml +16 -0
  31. package/lib/vendor/blamejs/.dockerignore +45 -0
  32. package/lib/vendor/blamejs/.gitattributes +42 -0
  33. package/lib/vendor/blamejs/.github/CODEOWNERS +4 -0
  34. package/lib/vendor/blamejs/.github/FUNDING.yml +2 -0
  35. package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/bug_report.md +58 -0
  36. package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/config.yml +8 -0
  37. package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/feature_request.md +99 -0
  38. package/lib/vendor/blamejs/.github/PULL_REQUEST_TEMPLATE.md +77 -0
  39. package/lib/vendor/blamejs/.github/dependabot.yml +37 -0
  40. package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +148 -0
  41. package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +107 -0
  42. package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +122 -0
  43. package/lib/vendor/blamejs/.github/workflows/ci.yml +511 -0
  44. package/lib/vendor/blamejs/.github/workflows/codeql.yml +50 -0
  45. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +655 -0
  46. package/lib/vendor/blamejs/.github/workflows/release-container.yml +406 -0
  47. package/lib/vendor/blamejs/.github/workflows/scorecard.yml +101 -0
  48. package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +134 -0
  49. package/lib/vendor/blamejs/.gitignore +102 -0
  50. package/lib/vendor/blamejs/.gitleaks.toml +166 -0
  51. package/lib/vendor/blamejs/.hadolint.yaml +18 -0
  52. package/lib/vendor/blamejs/.npmrc +5 -0
  53. package/lib/vendor/blamejs/.pinact.yaml +17 -0
  54. package/lib/vendor/blamejs/ARCHITECTURE.md +158 -0
  55. package/lib/vendor/blamejs/CHANGELOG.md +1351 -0
  56. package/lib/vendor/blamejs/CODE_OF_CONDUCT.md +86 -0
  57. package/lib/vendor/blamejs/CONTRIBUTING.md +156 -0
  58. package/lib/vendor/blamejs/GOVERNANCE.md +201 -0
  59. package/lib/vendor/blamejs/LICENSE +201 -0
  60. package/lib/vendor/blamejs/LTS-CALENDAR.md +29 -0
  61. package/lib/vendor/blamejs/MIGRATING.md +29 -0
  62. package/lib/vendor/blamejs/NOTICE +81 -0
  63. package/lib/vendor/blamejs/README.md +304 -0
  64. package/lib/vendor/blamejs/SECURITY.md +432 -0
  65. package/lib/vendor/blamejs/api-snapshot.json +48709 -0
  66. package/lib/vendor/blamejs/assets/BlameJS_Logo.png +0 -0
  67. package/lib/vendor/blamejs/assets/BlameJS_Logo.svg +129 -0
  68. package/lib/vendor/blamejs/bench/README.md +77 -0
  69. package/lib/vendor/blamejs/bench/_helpers.js +70 -0
  70. package/lib/vendor/blamejs/bench/baseline.json +183 -0
  71. package/lib/vendor/blamejs/bench/crypto-hash.bench.js +19 -0
  72. package/lib/vendor/blamejs/bench/crypto-symmetric.bench.js +28 -0
  73. package/lib/vendor/blamejs/bench/run.js +140 -0
  74. package/lib/vendor/blamejs/bench/safe-json.bench.js +31 -0
  75. package/lib/vendor/blamejs/bin/blamejs.js +13 -0
  76. package/lib/vendor/blamejs/docker/caddy/Caddyfile +46 -0
  77. package/lib/vendor/blamejs/docker/coredns/Corefile +37 -0
  78. package/lib/vendor/blamejs/docker/haproxy/haproxy.cfg +52 -0
  79. package/lib/vendor/blamejs/docker/init/generate-certs.sh +118 -0
  80. package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +87 -0
  81. package/lib/vendor/blamejs/docker/mitmproxy/config.yaml +16 -0
  82. package/lib/vendor/blamejs/docker/mongo/init-tls.sh +17 -0
  83. package/lib/vendor/blamejs/docker/mysql/my.cnf +12 -0
  84. package/lib/vendor/blamejs/docker/nats/nats.conf +33 -0
  85. package/lib/vendor/blamejs/docker/postgres/init-tls.sh +17 -0
  86. package/lib/vendor/blamejs/docker/postgres/postgresql.conf +18 -0
  87. package/lib/vendor/blamejs/docker/rabbitmq/rabbitmq.conf +18 -0
  88. package/lib/vendor/blamejs/docker/redis/redis.conf +15 -0
  89. package/lib/vendor/blamejs/docker/squid/squid.conf +24 -0
  90. package/lib/vendor/blamejs/docker/syslog/syslog-ng.conf +34 -0
  91. package/lib/vendor/blamejs/docker-compose.test.yml +545 -0
  92. package/lib/vendor/blamejs/docs/cis-postgres-crosswalk.md +102 -0
  93. package/lib/vendor/blamejs/docs/cis-sqlite-equivalent.md +92 -0
  94. package/lib/vendor/blamejs/eslint.config.mjs +204 -0
  95. package/lib/vendor/blamejs/examples/wiki/Caddyfile +40 -0
  96. package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +218 -0
  97. package/lib/vendor/blamejs/examples/wiki/Dockerfile +120 -0
  98. package/lib/vendor/blamejs/examples/wiki/README.md +157 -0
  99. package/lib/vendor/blamejs/examples/wiki/cli-snapshot.json +250 -0
  100. package/lib/vendor/blamejs/examples/wiki/docker-compose.prod.yml +231 -0
  101. package/lib/vendor/blamejs/examples/wiki/docker-compose.yml +166 -0
  102. package/lib/vendor/blamejs/examples/wiki/env-snapshot.json +217 -0
  103. package/lib/vendor/blamejs/examples/wiki/lib/auto-site-entries.js +139 -0
  104. package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +555 -0
  105. package/lib/vendor/blamejs/examples/wiki/lib/harvest-cli.js +507 -0
  106. package/lib/vendor/blamejs/examples/wiki/lib/harvest-env-vars.js +435 -0
  107. package/lib/vendor/blamejs/examples/wiki/lib/harvest-errors.js +282 -0
  108. package/lib/vendor/blamejs/examples/wiki/lib/harvest-vendored-deps.js +321 -0
  109. package/lib/vendor/blamejs/examples/wiki/lib/nav.js +15 -0
  110. package/lib/vendor/blamejs/examples/wiki/lib/opts-resolver.js +75 -0
  111. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +508 -0
  112. package/lib/vendor/blamejs/examples/wiki/lib/section.js +276 -0
  113. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +587 -0
  114. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +318 -0
  115. package/lib/vendor/blamejs/examples/wiki/lib/symbol-index.js +122 -0
  116. package/lib/vendor/blamejs/examples/wiki/migrations/0001-pages-schema.js +74 -0
  117. package/lib/vendor/blamejs/examples/wiki/package.json +18 -0
  118. package/lib/vendor/blamejs/examples/wiki/public/img/blamejs-logo.png +0 -0
  119. package/lib/vendor/blamejs/examples/wiki/public/img/blamejs-logo.svg +129 -0
  120. package/lib/vendor/blamejs/examples/wiki/public/robots.txt +5 -0
  121. package/lib/vendor/blamejs/examples/wiki/public/vendor/MANIFEST.json +30 -0
  122. package/lib/vendor/blamejs/examples/wiki/public/vendor/prism.css +1 -0
  123. package/lib/vendor/blamejs/examples/wiki/public/vendor/prism.js +15 -0
  124. package/lib/vendor/blamejs/examples/wiki/public/wiki.css +1250 -0
  125. package/lib/vendor/blamejs/examples/wiki/routes/admin.js +366 -0
  126. package/lib/vendor/blamejs/examples/wiki/routes/integration.js +230 -0
  127. package/lib/vendor/blamejs/examples/wiki/routes/pages.js +266 -0
  128. package/lib/vendor/blamejs/examples/wiki/scripts/backfill-module-metadata.js +214 -0
  129. package/lib/vendor/blamejs/examples/wiki/seeders/prod/0001-default-pages.js +35 -0
  130. package/lib/vendor/blamejs/examples/wiki/seeders/prod/pages/_index.js +34 -0
  131. package/lib/vendor/blamejs/examples/wiki/seeders/prod/pages/api.js +76 -0
  132. package/lib/vendor/blamejs/examples/wiki/server.js +129 -0
  133. package/lib/vendor/blamejs/examples/wiki/site.config.js +197 -0
  134. package/lib/vendor/blamejs/examples/wiki/snippets/README.md +38 -0
  135. package/lib/vendor/blamejs/examples/wiki/snippets/auth/password-hash.example.js +15 -0
  136. package/lib/vendor/blamejs/examples/wiki/src/editor.js +103 -0
  137. package/lib/vendor/blamejs/examples/wiki/src/wiki.js +349 -0
  138. package/lib/vendor/blamejs/examples/wiki/test/AUDIT.md +155 -0
  139. package/lib/vendor/blamejs/examples/wiki/test/codebase-patterns.test.js +594 -0
  140. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +741 -0
  141. package/lib/vendor/blamejs/examples/wiki/test/find-missing-pages.js +254 -0
  142. package/lib/vendor/blamejs/examples/wiki/test/integration.js +391 -0
  143. package/lib/vendor/blamejs/examples/wiki/test/validate-cli-snapshot.js +379 -0
  144. package/lib/vendor/blamejs/examples/wiki/test/validate-env-snapshot.js +346 -0
  145. package/lib/vendor/blamejs/examples/wiki/test/validate-nav-coverage.js +212 -0
  146. package/lib/vendor/blamejs/examples/wiki/test/validate-site-coverage.js +252 -0
  147. package/lib/vendor/blamejs/examples/wiki/test/validate-source-comment-blocks.js +107 -0
  148. package/lib/vendor/blamejs/examples/wiki/views/_layout.html +115 -0
  149. package/lib/vendor/blamejs/examples/wiki/views/admin/api-keys.html +51 -0
  150. package/lib/vendor/blamejs/examples/wiki/views/admin/dashboard.html +22 -0
  151. package/lib/vendor/blamejs/examples/wiki/views/admin/edit.html +17 -0
  152. package/lib/vendor/blamejs/examples/wiki/views/home.html +85 -0
  153. package/lib/vendor/blamejs/examples/wiki/views/login.html +18 -0
  154. package/lib/vendor/blamejs/examples/wiki/views/page.html +5 -0
  155. package/lib/vendor/blamejs/examples/wiki/views/partials/nav.html +13 -0
  156. package/lib/vendor/blamejs/examples/wiki/views/search.html +19 -0
  157. package/lib/vendor/blamejs/examples/wiki/wiki.config.js +15 -0
  158. package/lib/vendor/blamejs/fuzz/README.md +137 -0
  159. package/lib/vendor/blamejs/fuzz/_expected.js +35 -0
  160. package/lib/vendor/blamejs/fuzz/guard-agent-registry.fuzz.js +22 -0
  161. package/lib/vendor/blamejs/fuzz/guard-csv.fuzz.js +16 -0
  162. package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/01-basic.csv +3 -0
  163. package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/02-formula.csv +1 -0
  164. package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/03-hyperlink.csv +1 -0
  165. package/lib/vendor/blamejs/fuzz/guard-dsn.fuzz.js +22 -0
  166. package/lib/vendor/blamejs/fuzz/guard-email.fuzz.js +16 -0
  167. package/lib/vendor/blamejs/fuzz/guard-email_seed_corpus/01-basic.eml +5 -0
  168. package/lib/vendor/blamejs/fuzz/guard-envelope.fuzz.js +24 -0
  169. package/lib/vendor/blamejs/fuzz/guard-event-bus-payload.fuzz.js +24 -0
  170. package/lib/vendor/blamejs/fuzz/guard-event-bus-topic.fuzz.js +20 -0
  171. package/lib/vendor/blamejs/fuzz/guard-html.fuzz.js +16 -0
  172. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/01-basic.html +1 -0
  173. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/02-script.html +1 -0
  174. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/03-event.html +1 -0
  175. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/04-jsurl.html +1 -0
  176. package/lib/vendor/blamejs/fuzz/guard-idempotency-key.fuzz.js +20 -0
  177. package/lib/vendor/blamejs/fuzz/guard-imap-command.fuzz.js +35 -0
  178. package/lib/vendor/blamejs/fuzz/guard-jmap.fuzz.js +41 -0
  179. package/lib/vendor/blamejs/fuzz/guard-json.fuzz.js +16 -0
  180. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/01-basic.json +1 -0
  181. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/02-proto.json +1 -0
  182. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/03-dupkey.json +1 -0
  183. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/04-nan.json +1 -0
  184. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/05-bom.json +1 -0
  185. package/lib/vendor/blamejs/fuzz/guard-list-id.fuzz.js +21 -0
  186. package/lib/vendor/blamejs/fuzz/guard-list-unsubscribe.fuzz.js +25 -0
  187. package/lib/vendor/blamejs/fuzz/guard-mail-compose.fuzz.js +22 -0
  188. package/lib/vendor/blamejs/fuzz/guard-mail-move.fuzz.js +22 -0
  189. package/lib/vendor/blamejs/fuzz/guard-mail-query.fuzz.js +27 -0
  190. package/lib/vendor/blamejs/fuzz/guard-mail-reply.fuzz.js +23 -0
  191. package/lib/vendor/blamejs/fuzz/guard-mail-sieve.fuzz.js +36 -0
  192. package/lib/vendor/blamejs/fuzz/guard-managesieve-command.fuzz.js +26 -0
  193. package/lib/vendor/blamejs/fuzz/guard-markdown.fuzz.js +16 -0
  194. package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/01-basic.md +2 -0
  195. package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/02-jsurl.md +1 -0
  196. package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/03-jsimg.md +1 -0
  197. package/lib/vendor/blamejs/fuzz/guard-message-id.fuzz.js +26 -0
  198. package/lib/vendor/blamejs/fuzz/guard-pop3-command.fuzz.js +23 -0
  199. package/lib/vendor/blamejs/fuzz/guard-posture-chain.fuzz.js +22 -0
  200. package/lib/vendor/blamejs/fuzz/guard-saga-config.fuzz.js +32 -0
  201. package/lib/vendor/blamejs/fuzz/guard-smtp-command.fuzz.js +27 -0
  202. package/lib/vendor/blamejs/fuzz/guard-snapshot-envelope.fuzz.js +22 -0
  203. package/lib/vendor/blamejs/fuzz/guard-stream-args.fuzz.js +22 -0
  204. package/lib/vendor/blamejs/fuzz/guard-svg.fuzz.js +16 -0
  205. package/lib/vendor/blamejs/fuzz/guard-svg_seed_corpus/01-basic.svg +1 -0
  206. package/lib/vendor/blamejs/fuzz/guard-svg_seed_corpus/02-script.svg +1 -0
  207. package/lib/vendor/blamejs/fuzz/guard-tenant-id.fuzz.js +20 -0
  208. package/lib/vendor/blamejs/fuzz/guard-trace-context.fuzz.js +30 -0
  209. package/lib/vendor/blamejs/fuzz/guard-xml.fuzz.js +16 -0
  210. package/lib/vendor/blamejs/fuzz/guard-xml_seed_corpus/01-basic.xml +1 -0
  211. package/lib/vendor/blamejs/fuzz/guard-xml_seed_corpus/02-xxe.xml +1 -0
  212. package/lib/vendor/blamejs/fuzz/guard-yaml.fuzz.js +16 -0
  213. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/01-basic.yaml +2 -0
  214. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/02-anchor.yaml +2 -0
  215. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/03-norway.yaml +1 -0
  216. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/04-multidoc.yaml +4 -0
  217. package/lib/vendor/blamejs/fuzz/parsers__safe-ini.fuzz.js +16 -0
  218. package/lib/vendor/blamejs/fuzz/parsers__safe-ini_seed_corpus/01-basic.ini +2 -0
  219. package/lib/vendor/blamejs/fuzz/parsers__safe-toml.fuzz.js +16 -0
  220. package/lib/vendor/blamejs/fuzz/parsers__safe-toml_seed_corpus/01-basic.toml +4 -0
  221. package/lib/vendor/blamejs/fuzz/parsers__safe-xml.fuzz.js +16 -0
  222. package/lib/vendor/blamejs/fuzz/parsers__safe-xml_seed_corpus/01-basic.xml +1 -0
  223. package/lib/vendor/blamejs/fuzz/parsers__safe-yaml.fuzz.js +16 -0
  224. package/lib/vendor/blamejs/fuzz/parsers__safe-yaml_seed_corpus/01-basic.yaml +4 -0
  225. package/lib/vendor/blamejs/fuzz/safe-decompress.fuzz.js +49 -0
  226. package/lib/vendor/blamejs/fuzz/safe-dns.fuzz.js +29 -0
  227. package/lib/vendor/blamejs/fuzz/safe-ical.fuzz.js +16 -0
  228. package/lib/vendor/blamejs/fuzz/safe-icap.fuzz.js +42 -0
  229. package/lib/vendor/blamejs/fuzz/safe-json.fuzz.js +25 -0
  230. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/01-object.txt +1 -0
  231. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/02-array.txt +1 -0
  232. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/03-string.txt +1 -0
  233. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/04-proto.txt +1 -0
  234. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/05-deep.txt +1 -0
  235. package/lib/vendor/blamejs/fuzz/safe-jsonpath.fuzz.js +16 -0
  236. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/01-basic.txt +1 -0
  237. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/02-filter.txt +1 -0
  238. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/03-deepscan.txt +1 -0
  239. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/04-slice.txt +1 -0
  240. package/lib/vendor/blamejs/fuzz/safe-mime.fuzz.js +27 -0
  241. package/lib/vendor/blamejs/fuzz/safe-mount-info.fuzz.js +33 -0
  242. package/lib/vendor/blamejs/fuzz/safe-sieve.fuzz.js +28 -0
  243. package/lib/vendor/blamejs/fuzz/safe-smtp.fuzz.js +64 -0
  244. package/lib/vendor/blamejs/fuzz/safe-url.fuzz.js +16 -0
  245. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/01-basic.txt +1 -0
  246. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/02-userinfo.txt +1 -0
  247. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/03-dangerous.txt +1 -0
  248. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/04-data.txt +1 -0
  249. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/05-ipv6.txt +1 -0
  250. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/06-idn.txt +1 -0
  251. package/lib/vendor/blamejs/fuzz/safe-vcard.fuzz.js +16 -0
  252. package/lib/vendor/blamejs/index.js +678 -0
  253. package/lib/vendor/blamejs/keys/release-pqc-pub.json +7 -0
  254. package/lib/vendor/blamejs/lib/_test/crypto-fixtures.js +67 -0
  255. package/lib/vendor/blamejs/lib/a2a-tasks.js +598 -0
  256. package/lib/vendor/blamejs/lib/a2a.js +407 -0
  257. package/lib/vendor/blamejs/lib/acme.js +1448 -0
  258. package/lib/vendor/blamejs/lib/agent-audit.js +45 -0
  259. package/lib/vendor/blamejs/lib/agent-event-bus.js +382 -0
  260. package/lib/vendor/blamejs/lib/agent-idempotency.js +497 -0
  261. package/lib/vendor/blamejs/lib/agent-orchestrator.js +717 -0
  262. package/lib/vendor/blamejs/lib/agent-posture-chain.js +366 -0
  263. package/lib/vendor/blamejs/lib/agent-saga.js +321 -0
  264. package/lib/vendor/blamejs/lib/agent-snapshot.js +676 -0
  265. package/lib/vendor/blamejs/lib/agent-stream.js +269 -0
  266. package/lib/vendor/blamejs/lib/agent-tenant.js +632 -0
  267. package/lib/vendor/blamejs/lib/agent-trace.js +281 -0
  268. package/lib/vendor/blamejs/lib/ai-adverse-decision.js +184 -0
  269. package/lib/vendor/blamejs/lib/ai-content-detect.js +268 -0
  270. package/lib/vendor/blamejs/lib/ai-input.js +201 -0
  271. package/lib/vendor/blamejs/lib/ai-model-manifest.js +363 -0
  272. package/lib/vendor/blamejs/lib/ai-pref.js +340 -0
  273. package/lib/vendor/blamejs/lib/api-key.js +721 -0
  274. package/lib/vendor/blamejs/lib/api-snapshot.js +458 -0
  275. package/lib/vendor/blamejs/lib/app-shutdown.js +557 -0
  276. package/lib/vendor/blamejs/lib/app.js +365 -0
  277. package/lib/vendor/blamejs/lib/archive.js +547 -0
  278. package/lib/vendor/blamejs/lib/arg-parser.js +697 -0
  279. package/lib/vendor/blamejs/lib/argon2-builtin.js +173 -0
  280. package/lib/vendor/blamejs/lib/asn1-der.js +424 -0
  281. package/lib/vendor/blamejs/lib/asyncapi-bindings.js +160 -0
  282. package/lib/vendor/blamejs/lib/asyncapi-traits.js +143 -0
  283. package/lib/vendor/blamejs/lib/asyncapi.js +575 -0
  284. package/lib/vendor/blamejs/lib/atomic-file.js +1023 -0
  285. package/lib/vendor/blamejs/lib/audit-chain.js +266 -0
  286. package/lib/vendor/blamejs/lib/audit-daily-review.js +389 -0
  287. package/lib/vendor/blamejs/lib/audit-sign.js +751 -0
  288. package/lib/vendor/blamejs/lib/audit-tools.js +1113 -0
  289. package/lib/vendor/blamejs/lib/audit.js +1671 -0
  290. package/lib/vendor/blamejs/lib/auth/aal.js +169 -0
  291. package/lib/vendor/blamejs/lib/auth/access-lock.js +220 -0
  292. package/lib/vendor/blamejs/lib/auth/acr-vocabulary.js +265 -0
  293. package/lib/vendor/blamejs/lib/auth/ato-kill-switch.js +112 -0
  294. package/lib/vendor/blamejs/lib/auth/auth-time-tracker.js +111 -0
  295. package/lib/vendor/blamejs/lib/auth/bot-challenge.js +573 -0
  296. package/lib/vendor/blamejs/lib/auth/ciba.js +637 -0
  297. package/lib/vendor/blamejs/lib/auth/dpop.js +516 -0
  298. package/lib/vendor/blamejs/lib/auth/elevation-grant.js +306 -0
  299. package/lib/vendor/blamejs/lib/auth/fal.js +229 -0
  300. package/lib/vendor/blamejs/lib/auth/fido-mds3.js +681 -0
  301. package/lib/vendor/blamejs/lib/auth/jwt-external.js +519 -0
  302. package/lib/vendor/blamejs/lib/auth/jwt.js +430 -0
  303. package/lib/vendor/blamejs/lib/auth/lockout.js +449 -0
  304. package/lib/vendor/blamejs/lib/auth/oauth.js +2141 -0
  305. package/lib/vendor/blamejs/lib/auth/oid4vci.js +657 -0
  306. package/lib/vendor/blamejs/lib/auth/oid4vp.js +531 -0
  307. package/lib/vendor/blamejs/lib/auth/openid-federation.js +600 -0
  308. package/lib/vendor/blamejs/lib/auth/passkey.js +676 -0
  309. package/lib/vendor/blamejs/lib/auth/password.js +693 -0
  310. package/lib/vendor/blamejs/lib/auth/saml.js +2109 -0
  311. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-disclosure.js +95 -0
  312. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +225 -0
  313. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-issuer.js +197 -0
  314. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +728 -0
  315. package/lib/vendor/blamejs/lib/auth/status-list.js +272 -0
  316. package/lib/vendor/blamejs/lib/auth/step-up-policy.js +335 -0
  317. package/lib/vendor/blamejs/lib/auth/step-up.js +454 -0
  318. package/lib/vendor/blamejs/lib/auth-bot-challenge.js +505 -0
  319. package/lib/vendor/blamejs/lib/auth-header.js +148 -0
  320. package/lib/vendor/blamejs/lib/backup/bundle.js +265 -0
  321. package/lib/vendor/blamejs/lib/backup/crypto.js +176 -0
  322. package/lib/vendor/blamejs/lib/backup/index.js +1001 -0
  323. package/lib/vendor/blamejs/lib/backup/manifest.js +443 -0
  324. package/lib/vendor/blamejs/lib/boot-gates.js +174 -0
  325. package/lib/vendor/blamejs/lib/breach-deadline.js +272 -0
  326. package/lib/vendor/blamejs/lib/break-glass.js +1753 -0
  327. package/lib/vendor/blamejs/lib/budr.js +205 -0
  328. package/lib/vendor/blamejs/lib/bundler.js +461 -0
  329. package/lib/vendor/blamejs/lib/cache-redis.js +256 -0
  330. package/lib/vendor/blamejs/lib/cache-status.js +288 -0
  331. package/lib/vendor/blamejs/lib/cache.js +1331 -0
  332. package/lib/vendor/blamejs/lib/calendar.js +1240 -0
  333. package/lib/vendor/blamejs/lib/canonical-json.js +143 -0
  334. package/lib/vendor/blamejs/lib/cdn-cache-control.js +473 -0
  335. package/lib/vendor/blamejs/lib/cert.js +763 -0
  336. package/lib/vendor/blamejs/lib/chain-writer.js +259 -0
  337. package/lib/vendor/blamejs/lib/circuit-breaker.js +101 -0
  338. package/lib/vendor/blamejs/lib/cli-helpers.js +237 -0
  339. package/lib/vendor/blamejs/lib/cli.js +2328 -0
  340. package/lib/vendor/blamejs/lib/client-hints.js +318 -0
  341. package/lib/vendor/blamejs/lib/cloud-events.js +277 -0
  342. package/lib/vendor/blamejs/lib/cluster-provider-db.js +317 -0
  343. package/lib/vendor/blamejs/lib/cluster-storage.js +351 -0
  344. package/lib/vendor/blamejs/lib/cluster.js +1017 -0
  345. package/lib/vendor/blamejs/lib/cms-codec.js +826 -0
  346. package/lib/vendor/blamejs/lib/codepoint-class.js +262 -0
  347. package/lib/vendor/blamejs/lib/compliance-ai-act-logging.js +190 -0
  348. package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +205 -0
  349. package/lib/vendor/blamejs/lib/compliance-ai-act-risk.js +189 -0
  350. package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +200 -0
  351. package/lib/vendor/blamejs/lib/compliance-ai-act.js +821 -0
  352. package/lib/vendor/blamejs/lib/compliance-eaa.js +204 -0
  353. package/lib/vendor/blamejs/lib/compliance-sanctions-aliases.js +167 -0
  354. package/lib/vendor/blamejs/lib/compliance-sanctions-fetcher.js +206 -0
  355. package/lib/vendor/blamejs/lib/compliance-sanctions-fuzzy.js +297 -0
  356. package/lib/vendor/blamejs/lib/compliance-sanctions.js +569 -0
  357. package/lib/vendor/blamejs/lib/compliance.js +1558 -0
  358. package/lib/vendor/blamejs/lib/config-drift.js +426 -0
  359. package/lib/vendor/blamejs/lib/config.js +446 -0
  360. package/lib/vendor/blamejs/lib/consent.js +369 -0
  361. package/lib/vendor/blamejs/lib/constants.js +209 -0
  362. package/lib/vendor/blamejs/lib/content-credentials.js +704 -0
  363. package/lib/vendor/blamejs/lib/cookies.js +560 -0
  364. package/lib/vendor/blamejs/lib/cra-report.js +299 -0
  365. package/lib/vendor/blamejs/lib/credential-hash.js +394 -0
  366. package/lib/vendor/blamejs/lib/crypto-field.js +1017 -0
  367. package/lib/vendor/blamejs/lib/crypto-hpke-pq.js +187 -0
  368. package/lib/vendor/blamejs/lib/crypto-hpke.js +256 -0
  369. package/lib/vendor/blamejs/lib/crypto.js +1908 -0
  370. package/lib/vendor/blamejs/lib/csp.js +271 -0
  371. package/lib/vendor/blamejs/lib/csv.js +418 -0
  372. package/lib/vendor/blamejs/lib/daemon.js +481 -0
  373. package/lib/vendor/blamejs/lib/dark-patterns.js +488 -0
  374. package/lib/vendor/blamejs/lib/data-act.js +328 -0
  375. package/lib/vendor/blamejs/lib/db-collection.js +587 -0
  376. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +267 -0
  377. package/lib/vendor/blamejs/lib/db-declare-view.js +420 -0
  378. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +333 -0
  379. package/lib/vendor/blamejs/lib/db-query.js +802 -0
  380. package/lib/vendor/blamejs/lib/db-role-context.js +50 -0
  381. package/lib/vendor/blamejs/lib/db-schema.js +322 -0
  382. package/lib/vendor/blamejs/lib/db.js +3111 -0
  383. package/lib/vendor/blamejs/lib/dbsc.js +299 -0
  384. package/lib/vendor/blamejs/lib/ddl-change-control.js +523 -0
  385. package/lib/vendor/blamejs/lib/deprecate.js +377 -0
  386. package/lib/vendor/blamejs/lib/dev.js +405 -0
  387. package/lib/vendor/blamejs/lib/dora.js +402 -0
  388. package/lib/vendor/blamejs/lib/dr-runbook.js +368 -0
  389. package/lib/vendor/blamejs/lib/dsr.js +1188 -0
  390. package/lib/vendor/blamejs/lib/dual-control.js +526 -0
  391. package/lib/vendor/blamejs/lib/early-hints.js +212 -0
  392. package/lib/vendor/blamejs/lib/error-page.js +420 -0
  393. package/lib/vendor/blamejs/lib/events.js +214 -0
  394. package/lib/vendor/blamejs/lib/external-db-migrate.js +659 -0
  395. package/lib/vendor/blamejs/lib/external-db.js +1877 -0
  396. package/lib/vendor/blamejs/lib/fapi2.js +394 -0
  397. package/lib/vendor/blamejs/lib/fda-21cfr11.js +395 -0
  398. package/lib/vendor/blamejs/lib/fdx.js +370 -0
  399. package/lib/vendor/blamejs/lib/fedcm.js +264 -0
  400. package/lib/vendor/blamejs/lib/file-type.js +360 -0
  401. package/lib/vendor/blamejs/lib/file-upload.js +1256 -0
  402. package/lib/vendor/blamejs/lib/flag-cache.js +136 -0
  403. package/lib/vendor/blamejs/lib/flag-evaluation-context.js +135 -0
  404. package/lib/vendor/blamejs/lib/flag-providers.js +279 -0
  405. package/lib/vendor/blamejs/lib/flag-targeting.js +210 -0
  406. package/lib/vendor/blamejs/lib/flag.js +346 -0
  407. package/lib/vendor/blamejs/lib/forms.js +525 -0
  408. package/lib/vendor/blamejs/lib/framework-error.js +724 -0
  409. package/lib/vendor/blamejs/lib/framework-schema.js +845 -0
  410. package/lib/vendor/blamejs/lib/framework-sha1-hibp.js +34 -0
  411. package/lib/vendor/blamejs/lib/fsm.js +469 -0
  412. package/lib/vendor/blamejs/lib/gate-contract.js +1661 -0
  413. package/lib/vendor/blamejs/lib/gdpr-ropa.js +261 -0
  414. package/lib/vendor/blamejs/lib/graphql-federation.js +234 -0
  415. package/lib/vendor/blamejs/lib/guard-agent-registry.js +179 -0
  416. package/lib/vendor/blamejs/lib/guard-all.js +555 -0
  417. package/lib/vendor/blamejs/lib/guard-archive.js +901 -0
  418. package/lib/vendor/blamejs/lib/guard-auth.js +451 -0
  419. package/lib/vendor/blamejs/lib/guard-cidr.js +676 -0
  420. package/lib/vendor/blamejs/lib/guard-csv.js +1176 -0
  421. package/lib/vendor/blamejs/lib/guard-domain.js +814 -0
  422. package/lib/vendor/blamejs/lib/guard-dsn.js +382 -0
  423. package/lib/vendor/blamejs/lib/guard-email.js +951 -0
  424. package/lib/vendor/blamejs/lib/guard-envelope.js +294 -0
  425. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +217 -0
  426. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +150 -0
  427. package/lib/vendor/blamejs/lib/guard-filename.js +956 -0
  428. package/lib/vendor/blamejs/lib/guard-graphql.js +731 -0
  429. package/lib/vendor/blamejs/lib/guard-html-wcag-aria.js +164 -0
  430. package/lib/vendor/blamejs/lib/guard-html-wcag-forms.js +144 -0
  431. package/lib/vendor/blamejs/lib/guard-html-wcag-tables.js +154 -0
  432. package/lib/vendor/blamejs/lib/guard-html-wcag-tagwalk.js +44 -0
  433. package/lib/vendor/blamejs/lib/guard-html-wcag.js +470 -0
  434. package/lib/vendor/blamejs/lib/guard-html.js +1209 -0
  435. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +151 -0
  436. package/lib/vendor/blamejs/lib/guard-image.js +584 -0
  437. package/lib/vendor/blamejs/lib/guard-imap-command.js +337 -0
  438. package/lib/vendor/blamejs/lib/guard-jmap.js +321 -0
  439. package/lib/vendor/blamejs/lib/guard-json.js +935 -0
  440. package/lib/vendor/blamejs/lib/guard-jsonpath.js +512 -0
  441. package/lib/vendor/blamejs/lib/guard-jwt.js +772 -0
  442. package/lib/vendor/blamejs/lib/guard-list-id.js +318 -0
  443. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +412 -0
  444. package/lib/vendor/blamejs/lib/guard-mail-compose.js +282 -0
  445. package/lib/vendor/blamejs/lib/guard-mail-move.js +202 -0
  446. package/lib/vendor/blamejs/lib/guard-mail-query.js +310 -0
  447. package/lib/vendor/blamejs/lib/guard-mail-reply.js +172 -0
  448. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +207 -0
  449. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +566 -0
  450. package/lib/vendor/blamejs/lib/guard-markdown.js +768 -0
  451. package/lib/vendor/blamejs/lib/guard-message-id.js +267 -0
  452. package/lib/vendor/blamejs/lib/guard-mime.js +609 -0
  453. package/lib/vendor/blamejs/lib/guard-oauth.js +650 -0
  454. package/lib/vendor/blamejs/lib/guard-pdf.js +569 -0
  455. package/lib/vendor/blamejs/lib/guard-pop3-command.js +317 -0
  456. package/lib/vendor/blamejs/lib/guard-posture-chain.js +201 -0
  457. package/lib/vendor/blamejs/lib/guard-regex.js +632 -0
  458. package/lib/vendor/blamejs/lib/guard-saga-config.js +157 -0
  459. package/lib/vendor/blamejs/lib/guard-shell.js +522 -0
  460. package/lib/vendor/blamejs/lib/guard-smtp-command.js +594 -0
  461. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +168 -0
  462. package/lib/vendor/blamejs/lib/guard-stream-args.js +166 -0
  463. package/lib/vendor/blamejs/lib/guard-svg.js +1163 -0
  464. package/lib/vendor/blamejs/lib/guard-template.js +490 -0
  465. package/lib/vendor/blamejs/lib/guard-tenant-id.js +138 -0
  466. package/lib/vendor/blamejs/lib/guard-time.js +586 -0
  467. package/lib/vendor/blamejs/lib/guard-trace-context.js +172 -0
  468. package/lib/vendor/blamejs/lib/guard-uuid.js +548 -0
  469. package/lib/vendor/blamejs/lib/guard-xml.js +666 -0
  470. package/lib/vendor/blamejs/lib/guard-yaml.js +726 -0
  471. package/lib/vendor/blamejs/lib/hal.js +125 -0
  472. package/lib/vendor/blamejs/lib/handlers.js +350 -0
  473. package/lib/vendor/blamejs/lib/honeytoken.js +168 -0
  474. package/lib/vendor/blamejs/lib/html-balance.js +347 -0
  475. package/lib/vendor/blamejs/lib/http-client-cache.js +923 -0
  476. package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +519 -0
  477. package/lib/vendor/blamejs/lib/http-client.js +2152 -0
  478. package/lib/vendor/blamejs/lib/http-message-signature.js +589 -0
  479. package/lib/vendor/blamejs/lib/http2-teardown.js +34 -0
  480. package/lib/vendor/blamejs/lib/i18n-messageformat.js +398 -0
  481. package/lib/vendor/blamejs/lib/i18n.js +931 -0
  482. package/lib/vendor/blamejs/lib/iab-mspa.js +257 -0
  483. package/lib/vendor/blamejs/lib/iab-tcf.js +461 -0
  484. package/lib/vendor/blamejs/lib/importmap-integrity.js +90 -0
  485. package/lib/vendor/blamejs/lib/inbox.js +435 -0
  486. package/lib/vendor/blamejs/lib/incident-report.js +314 -0
  487. package/lib/vendor/blamejs/lib/ip-utils.js +102 -0
  488. package/lib/vendor/blamejs/lib/jobs.js +185 -0
  489. package/lib/vendor/blamejs/lib/jose-jwe-experimental.js +228 -0
  490. package/lib/vendor/blamejs/lib/jsonapi.js +230 -0
  491. package/lib/vendor/blamejs/lib/keychain.js +865 -0
  492. package/lib/vendor/blamejs/lib/lazy-require.js +48 -0
  493. package/lib/vendor/blamejs/lib/legal-hold.js +374 -0
  494. package/lib/vendor/blamejs/lib/local-db-thin.js +321 -0
  495. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +369 -0
  496. package/lib/vendor/blamejs/lib/log-stream-local.js +146 -0
  497. package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +410 -0
  498. package/lib/vendor/blamejs/lib/log-stream-otlp.js +286 -0
  499. package/lib/vendor/blamejs/lib/log-stream-syslog.js +310 -0
  500. package/lib/vendor/blamejs/lib/log-stream-webhook.js +199 -0
  501. package/lib/vendor/blamejs/lib/log-stream.js +584 -0
  502. package/lib/vendor/blamejs/lib/log.js +625 -0
  503. package/lib/vendor/blamejs/lib/lro.js +200 -0
  504. package/lib/vendor/blamejs/lib/mail-agent.js +786 -0
  505. package/lib/vendor/blamejs/lib/mail-arc-sign.js +417 -0
  506. package/lib/vendor/blamejs/lib/mail-arf.js +343 -0
  507. package/lib/vendor/blamejs/lib/mail-auth.js +2144 -0
  508. package/lib/vendor/blamejs/lib/mail-bimi.js +1047 -0
  509. package/lib/vendor/blamejs/lib/mail-bounce.js +955 -0
  510. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +1286 -0
  511. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +789 -0
  512. package/lib/vendor/blamejs/lib/mail-crypto.js +108 -0
  513. package/lib/vendor/blamejs/lib/mail-dav.js +1224 -0
  514. package/lib/vendor/blamejs/lib/mail-deploy.js +1119 -0
  515. package/lib/vendor/blamejs/lib/mail-dkim.js +1250 -0
  516. package/lib/vendor/blamejs/lib/mail-greylist.js +448 -0
  517. package/lib/vendor/blamejs/lib/mail-helo.js +473 -0
  518. package/lib/vendor/blamejs/lib/mail-journal.js +435 -0
  519. package/lib/vendor/blamejs/lib/mail-mdn.js +424 -0
  520. package/lib/vendor/blamejs/lib/mail-rbl.js +392 -0
  521. package/lib/vendor/blamejs/lib/mail-require-tls.js +198 -0
  522. package/lib/vendor/blamejs/lib/mail-scan.js +502 -0
  523. package/lib/vendor/blamejs/lib/mail-send-deliver.js +629 -0
  524. package/lib/vendor/blamejs/lib/mail-server-imap.js +1858 -0
  525. package/lib/vendor/blamejs/lib/mail-server-jmap.js +1565 -0
  526. package/lib/vendor/blamejs/lib/mail-server-managesieve.js +908 -0
  527. package/lib/vendor/blamejs/lib/mail-server-mx.js +969 -0
  528. package/lib/vendor/blamejs/lib/mail-server-pop3.js +915 -0
  529. package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +315 -0
  530. package/lib/vendor/blamejs/lib/mail-server-registry.js +378 -0
  531. package/lib/vendor/blamejs/lib/mail-server-submission.js +1396 -0
  532. package/lib/vendor/blamejs/lib/mail-server-tls.js +445 -0
  533. package/lib/vendor/blamejs/lib/mail-sieve.js +557 -0
  534. package/lib/vendor/blamejs/lib/mail-spam-score.js +284 -0
  535. package/lib/vendor/blamejs/lib/mail-srs.js +248 -0
  536. package/lib/vendor/blamejs/lib/mail-store-fts.js +394 -0
  537. package/lib/vendor/blamejs/lib/mail-store.js +929 -0
  538. package/lib/vendor/blamejs/lib/mail-unsubscribe.js +400 -0
  539. package/lib/vendor/blamejs/lib/mail.js +1971 -0
  540. package/lib/vendor/blamejs/lib/mcp-tool-registry.js +473 -0
  541. package/lib/vendor/blamejs/lib/mcp.js +950 -0
  542. package/lib/vendor/blamejs/lib/metrics.js +1503 -0
  543. package/lib/vendor/blamejs/lib/middleware/age-gate.js +177 -0
  544. package/lib/vendor/blamejs/lib/middleware/ai-act-disclosure.js +203 -0
  545. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +981 -0
  546. package/lib/vendor/blamejs/lib/middleware/assetlinks.js +137 -0
  547. package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +171 -0
  548. package/lib/vendor/blamejs/lib/middleware/attach-user.js +220 -0
  549. package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +293 -0
  550. package/lib/vendor/blamejs/lib/middleware/body-parser.js +1519 -0
  551. package/lib/vendor/blamejs/lib/middleware/bot-disclose.js +183 -0
  552. package/lib/vendor/blamejs/lib/middleware/bot-guard.js +217 -0
  553. package/lib/vendor/blamejs/lib/middleware/clear-site-data.js +122 -0
  554. package/lib/vendor/blamejs/lib/middleware/compose-pipeline.js +355 -0
  555. package/lib/vendor/blamejs/lib/middleware/compression.js +489 -0
  556. package/lib/vendor/blamejs/lib/middleware/cookies.js +130 -0
  557. package/lib/vendor/blamejs/lib/middleware/cors.js +386 -0
  558. package/lib/vendor/blamejs/lib/middleware/csp-nonce.js +388 -0
  559. package/lib/vendor/blamejs/lib/middleware/csp-report.js +167 -0
  560. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +499 -0
  561. package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +243 -0
  562. package/lib/vendor/blamejs/lib/middleware/db-role-for.js +304 -0
  563. package/lib/vendor/blamejs/lib/middleware/dpop.js +402 -0
  564. package/lib/vendor/blamejs/lib/middleware/error-handler.js +69 -0
  565. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +168 -0
  566. package/lib/vendor/blamejs/lib/middleware/flag-context.js +110 -0
  567. package/lib/vendor/blamejs/lib/middleware/gpc.js +153 -0
  568. package/lib/vendor/blamejs/lib/middleware/headers.js +242 -0
  569. package/lib/vendor/blamejs/lib/middleware/health.js +438 -0
  570. package/lib/vendor/blamejs/lib/middleware/host-allowlist.js +189 -0
  571. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +964 -0
  572. package/lib/vendor/blamejs/lib/middleware/index.js +183 -0
  573. package/lib/vendor/blamejs/lib/middleware/nel.js +214 -0
  574. package/lib/vendor/blamejs/lib/middleware/network-allowlist.js +237 -0
  575. package/lib/vendor/blamejs/lib/middleware/no-cache.js +106 -0
  576. package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +177 -0
  577. package/lib/vendor/blamejs/lib/middleware/protected-resource-metadata.js +277 -0
  578. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +556 -0
  579. package/lib/vendor/blamejs/lib/middleware/request-id.js +79 -0
  580. package/lib/vendor/blamejs/lib/middleware/request-log.js +205 -0
  581. package/lib/vendor/blamejs/lib/middleware/require-aal.js +138 -0
  582. package/lib/vendor/blamejs/lib/middleware/require-auth.js +144 -0
  583. package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +290 -0
  584. package/lib/vendor/blamejs/lib/middleware/require-content-type.js +113 -0
  585. package/lib/vendor/blamejs/lib/middleware/require-methods.js +97 -0
  586. package/lib/vendor/blamejs/lib/middleware/require-mtls.js +212 -0
  587. package/lib/vendor/blamejs/lib/middleware/require-step-up.js +226 -0
  588. package/lib/vendor/blamejs/lib/middleware/scim-server.js +375 -0
  589. package/lib/vendor/blamejs/lib/middleware/security-headers.js +285 -0
  590. package/lib/vendor/blamejs/lib/middleware/security-txt.js +170 -0
  591. package/lib/vendor/blamejs/lib/middleware/span-http-server.js +280 -0
  592. package/lib/vendor/blamejs/lib/middleware/speculation-rules.js +323 -0
  593. package/lib/vendor/blamejs/lib/middleware/sse.js +200 -0
  594. package/lib/vendor/blamejs/lib/middleware/trace-log-correlation.js +167 -0
  595. package/lib/vendor/blamejs/lib/middleware/trace-propagate.js +148 -0
  596. package/lib/vendor/blamejs/lib/middleware/tus-upload.js +749 -0
  597. package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +164 -0
  598. package/lib/vendor/blamejs/lib/migration-files.js +37 -0
  599. package/lib/vendor/blamejs/lib/migrations.js +385 -0
  600. package/lib/vendor/blamejs/lib/mime-parse.js +198 -0
  601. package/lib/vendor/blamejs/lib/money.js +699 -0
  602. package/lib/vendor/blamejs/lib/mtls-ca.js +572 -0
  603. package/lib/vendor/blamejs/lib/mtls-engine-default.js +501 -0
  604. package/lib/vendor/blamejs/lib/network-byte-quota.js +308 -0
  605. package/lib/vendor/blamejs/lib/network-dns-resolver.js +533 -0
  606. package/lib/vendor/blamejs/lib/network-dns.js +1930 -0
  607. package/lib/vendor/blamejs/lib/network-heartbeat.js +425 -0
  608. package/lib/vendor/blamejs/lib/network-nts.js +574 -0
  609. package/lib/vendor/blamejs/lib/network-proxy.js +265 -0
  610. package/lib/vendor/blamejs/lib/network-smtp-policy.js +836 -0
  611. package/lib/vendor/blamejs/lib/network-tls.js +3126 -0
  612. package/lib/vendor/blamejs/lib/network.js +346 -0
  613. package/lib/vendor/blamejs/lib/nis2-report.js +181 -0
  614. package/lib/vendor/blamejs/lib/nist-crosswalk.js +293 -0
  615. package/lib/vendor/blamejs/lib/nonce-store.js +177 -0
  616. package/lib/vendor/blamejs/lib/notify.js +683 -0
  617. package/lib/vendor/blamejs/lib/ntp-check.js +458 -0
  618. package/lib/vendor/blamejs/lib/numeric-bounds.js +111 -0
  619. package/lib/vendor/blamejs/lib/numeric-checks.js +40 -0
  620. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +349 -0
  621. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +488 -0
  622. package/lib/vendor/blamejs/lib/object-store/gcs-bucket-ops.js +351 -0
  623. package/lib/vendor/blamejs/lib/object-store/gcs.js +515 -0
  624. package/lib/vendor/blamejs/lib/object-store/http-put.js +153 -0
  625. package/lib/vendor/blamejs/lib/object-store/http-request.js +38 -0
  626. package/lib/vendor/blamejs/lib/object-store/index.js +197 -0
  627. package/lib/vendor/blamejs/lib/object-store/local.js +163 -0
  628. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +1133 -0
  629. package/lib/vendor/blamejs/lib/object-store/sigv4.js +957 -0
  630. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +420 -0
  631. package/lib/vendor/blamejs/lib/observability-tracer.js +395 -0
  632. package/lib/vendor/blamejs/lib/observability.js +720 -0
  633. package/lib/vendor/blamejs/lib/openapi-paths-builder.js +248 -0
  634. package/lib/vendor/blamejs/lib/openapi-schema-walk.js +192 -0
  635. package/lib/vendor/blamejs/lib/openapi-security.js +169 -0
  636. package/lib/vendor/blamejs/lib/openapi-yaml.js +154 -0
  637. package/lib/vendor/blamejs/lib/openapi.js +489 -0
  638. package/lib/vendor/blamejs/lib/otel-export.js +278 -0
  639. package/lib/vendor/blamejs/lib/outbox.js +547 -0
  640. package/lib/vendor/blamejs/lib/pagination.js +542 -0
  641. package/lib/vendor/blamejs/lib/parsers/index.js +91 -0
  642. package/lib/vendor/blamejs/lib/parsers/safe-env.js +642 -0
  643. package/lib/vendor/blamejs/lib/parsers/safe-ini.js +293 -0
  644. package/lib/vendor/blamejs/lib/parsers/safe-toml.js +784 -0
  645. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +390 -0
  646. package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +1015 -0
  647. package/lib/vendor/blamejs/lib/permissions.js +793 -0
  648. package/lib/vendor/blamejs/lib/pick.js +105 -0
  649. package/lib/vendor/blamejs/lib/pqc-agent.js +351 -0
  650. package/lib/vendor/blamejs/lib/pqc-gate.js +279 -0
  651. package/lib/vendor/blamejs/lib/pqc-software.js +271 -0
  652. package/lib/vendor/blamejs/lib/problem-details.js +482 -0
  653. package/lib/vendor/blamejs/lib/process-spawn.js +196 -0
  654. package/lib/vendor/blamejs/lib/promise-pool.js +162 -0
  655. package/lib/vendor/blamejs/lib/protobuf-encoder.js +190 -0
  656. package/lib/vendor/blamejs/lib/protocol-dispatcher.js +161 -0
  657. package/lib/vendor/blamejs/lib/public-suffix.js +403 -0
  658. package/lib/vendor/blamejs/lib/pubsub-cluster.js +154 -0
  659. package/lib/vendor/blamejs/lib/pubsub-redis.js +167 -0
  660. package/lib/vendor/blamejs/lib/pubsub.js +463 -0
  661. package/lib/vendor/blamejs/lib/queue-local.js +476 -0
  662. package/lib/vendor/blamejs/lib/queue-redis.js +745 -0
  663. package/lib/vendor/blamejs/lib/queue-sqs.js +319 -0
  664. package/lib/vendor/blamejs/lib/queue.js +1016 -0
  665. package/lib/vendor/blamejs/lib/redact.js +1007 -0
  666. package/lib/vendor/blamejs/lib/redis-client.js +520 -0
  667. package/lib/vendor/blamejs/lib/render.js +285 -0
  668. package/lib/vendor/blamejs/lib/request-helpers.js +767 -0
  669. package/lib/vendor/blamejs/lib/resource-access-lock.js +116 -0
  670. package/lib/vendor/blamejs/lib/restore-bundle.js +340 -0
  671. package/lib/vendor/blamejs/lib/restore-rollback.js +365 -0
  672. package/lib/vendor/blamejs/lib/restore.js +409 -0
  673. package/lib/vendor/blamejs/lib/retention.js +640 -0
  674. package/lib/vendor/blamejs/lib/retry.js +523 -0
  675. package/lib/vendor/blamejs/lib/router.js +1289 -0
  676. package/lib/vendor/blamejs/lib/safe-async.js +1184 -0
  677. package/lib/vendor/blamejs/lib/safe-buffer.js +562 -0
  678. package/lib/vendor/blamejs/lib/safe-decompress.js +297 -0
  679. package/lib/vendor/blamejs/lib/safe-dns.js +665 -0
  680. package/lib/vendor/blamejs/lib/safe-ical.js +634 -0
  681. package/lib/vendor/blamejs/lib/safe-icap.js +502 -0
  682. package/lib/vendor/blamejs/lib/safe-json.js +946 -0
  683. package/lib/vendor/blamejs/lib/safe-jsonpath.js +285 -0
  684. package/lib/vendor/blamejs/lib/safe-mime.js +831 -0
  685. package/lib/vendor/blamejs/lib/safe-mount-info.js +306 -0
  686. package/lib/vendor/blamejs/lib/safe-path.js +254 -0
  687. package/lib/vendor/blamejs/lib/safe-redirect.js +106 -0
  688. package/lib/vendor/blamejs/lib/safe-schema.js +1810 -0
  689. package/lib/vendor/blamejs/lib/safe-sieve.js +684 -0
  690. package/lib/vendor/blamejs/lib/safe-smtp.js +185 -0
  691. package/lib/vendor/blamejs/lib/safe-sql.js +363 -0
  692. package/lib/vendor/blamejs/lib/safe-url.js +428 -0
  693. package/lib/vendor/blamejs/lib/safe-vcard.js +473 -0
  694. package/lib/vendor/blamejs/lib/sandbox-worker.js +135 -0
  695. package/lib/vendor/blamejs/lib/sandbox.js +358 -0
  696. package/lib/vendor/blamejs/lib/scheduler.js +827 -0
  697. package/lib/vendor/blamejs/lib/sd-notify.js +269 -0
  698. package/lib/vendor/blamejs/lib/sec-cyber.js +214 -0
  699. package/lib/vendor/blamejs/lib/security-assert.js +395 -0
  700. package/lib/vendor/blamejs/lib/seeders.js +620 -0
  701. package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +309 -0
  702. package/lib/vendor/blamejs/lib/self-update.js +804 -0
  703. package/lib/vendor/blamejs/lib/server-timing.js +174 -0
  704. package/lib/vendor/blamejs/lib/session-device-binding.js +431 -0
  705. package/lib/vendor/blamejs/lib/session-stores.js +138 -0
  706. package/lib/vendor/blamejs/lib/session.js +1162 -0
  707. package/lib/vendor/blamejs/lib/slug.js +381 -0
  708. package/lib/vendor/blamejs/lib/sse.js +349 -0
  709. package/lib/vendor/blamejs/lib/ssrf-guard.js +792 -0
  710. package/lib/vendor/blamejs/lib/standard-webhooks.js +183 -0
  711. package/lib/vendor/blamejs/lib/static.js +1249 -0
  712. package/lib/vendor/blamejs/lib/storage.js +1272 -0
  713. package/lib/vendor/blamejs/lib/stream-throttle.js +235 -0
  714. package/lib/vendor/blamejs/lib/structured-fields.js +244 -0
  715. package/lib/vendor/blamejs/lib/subject.js +667 -0
  716. package/lib/vendor/blamejs/lib/tcpa-10dlc.js +175 -0
  717. package/lib/vendor/blamejs/lib/template.js +931 -0
  718. package/lib/vendor/blamejs/lib/tenant-quota.js +545 -0
  719. package/lib/vendor/blamejs/lib/test-harness.js +275 -0
  720. package/lib/vendor/blamejs/lib/testing.js +1185 -0
  721. package/lib/vendor/blamejs/lib/time.js +578 -0
  722. package/lib/vendor/blamejs/lib/tls-exporter.js +239 -0
  723. package/lib/vendor/blamejs/lib/totp.js +318 -0
  724. package/lib/vendor/blamejs/lib/tracing.js +546 -0
  725. package/lib/vendor/blamejs/lib/uuid.js +207 -0
  726. package/lib/vendor/blamejs/lib/validate-opts.js +381 -0
  727. package/lib/vendor/blamejs/lib/vault/index.js +638 -0
  728. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +311 -0
  729. package/lib/vendor/blamejs/lib/vault/passphrase-source.js +198 -0
  730. package/lib/vendor/blamejs/lib/vault/rotate.js +803 -0
  731. package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +471 -0
  732. package/lib/vendor/blamejs/lib/vault/wrap.js +296 -0
  733. package/lib/vendor/blamejs/lib/vault-aad.js +259 -0
  734. package/lib/vendor/blamejs/lib/vendor/.vendor-data-pubkey +4 -0
  735. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +161 -0
  736. package/lib/vendor/blamejs/lib/vendor/bimi-trust-anchors.data.js +68 -0
  737. package/lib/vendor/blamejs/lib/vendor/bimi-trust-anchors.pem +33 -0
  738. package/lib/vendor/blamejs/lib/vendor/common-passwords-top-10000.data.js +1325 -0
  739. package/lib/vendor/blamejs/lib/vendor/common-passwords-top-10000.txt +10002 -0
  740. package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +9 -0
  741. package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +18 -0
  742. package/lib/vendor/blamejs/lib/vendor/pki.cjs +181 -0
  743. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +16382 -0
  744. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5881 -0
  745. package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +328 -0
  746. package/lib/vendor/blamejs/lib/vendor/vendor-data-pubkey.js +16 -0
  747. package/lib/vendor/blamejs/lib/vendor-data.js +520 -0
  748. package/lib/vendor/blamejs/lib/vex.js +630 -0
  749. package/lib/vendor/blamejs/lib/watcher.js +608 -0
  750. package/lib/vendor/blamejs/lib/web-push-vapid.js +322 -0
  751. package/lib/vendor/blamejs/lib/webhook.js +977 -0
  752. package/lib/vendor/blamejs/lib/websocket-channels.js +327 -0
  753. package/lib/vendor/blamejs/lib/websocket.js +1561 -0
  754. package/lib/vendor/blamejs/lib/wiki-concepts.js +338 -0
  755. package/lib/vendor/blamejs/lib/worker-pool.js +464 -0
  756. package/lib/vendor/blamejs/lib/ws-client.js +978 -0
  757. package/lib/vendor/blamejs/lib/xml-c14n.js +506 -0
  758. package/lib/vendor/blamejs/memory/specs/node-26-map-getorinsert-migration.md +164 -0
  759. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +19 -0
  760. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/README.md +88 -0
  761. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/build.sh +26 -0
  762. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/project.yaml +28 -0
  763. package/lib/vendor/blamejs/package.json +81 -0
  764. package/lib/vendor/blamejs/release-notes/v0.0.x.json +310 -0
  765. package/lib/vendor/blamejs/release-notes/v0.1.x.json +1798 -0
  766. package/lib/vendor/blamejs/release-notes/v0.10.x.json +1288 -0
  767. package/lib/vendor/blamejs/release-notes/v0.11.x.json +2551 -0
  768. package/lib/vendor/blamejs/release-notes/v0.12.0.json +64 -0
  769. package/lib/vendor/blamejs/release-notes/v0.12.1.json +32 -0
  770. package/lib/vendor/blamejs/release-notes/v0.12.2.json +45 -0
  771. package/lib/vendor/blamejs/release-notes/v0.2.x.json +706 -0
  772. package/lib/vendor/blamejs/release-notes/v0.3.x.json +786 -0
  773. package/lib/vendor/blamejs/release-notes/v0.4.x.json +588 -0
  774. package/lib/vendor/blamejs/release-notes/v0.5.x.json +390 -0
  775. package/lib/vendor/blamejs/release-notes/v0.6.x.json +1947 -0
  776. package/lib/vendor/blamejs/release-notes/v0.7.x.json +3811 -0
  777. package/lib/vendor/blamejs/release-notes/v0.8.x.json +3318 -0
  778. package/lib/vendor/blamejs/release-notes/v0.9.x.json +2257 -0
  779. package/lib/vendor/blamejs/scripts/build-vendored-sbom.js +325 -0
  780. package/lib/vendor/blamejs/scripts/check-api-snapshot.js +62 -0
  781. package/lib/vendor/blamejs/scripts/check-changelog-extract.js +108 -0
  782. package/lib/vendor/blamejs/scripts/check-pack-against-gitignore.js +83 -0
  783. package/lib/vendor/blamejs/scripts/check-services.js +483 -0
  784. package/lib/vendor/blamejs/scripts/check-vendor-currency.js +349 -0
  785. package/lib/vendor/blamejs/scripts/consolidate-release-notes.js +216 -0
  786. package/lib/vendor/blamejs/scripts/gen-migrating.js +275 -0
  787. package/lib/vendor/blamejs/scripts/generate-changelog-entry.js +577 -0
  788. package/lib/vendor/blamejs/scripts/generate-release-signing-key.js +79 -0
  789. package/lib/vendor/blamejs/scripts/publish-dep-confusion-placeholder.sh +101 -0
  790. package/lib/vendor/blamejs/scripts/refresh-api-snapshot.js +31 -0
  791. package/lib/vendor/blamejs/scripts/refresh-vendor-manifest.js +132 -0
  792. package/lib/vendor/blamejs/scripts/release.js +652 -0
  793. package/lib/vendor/blamejs/scripts/sha3-digest.js +62 -0
  794. package/lib/vendor/blamejs/scripts/sign-release-artifact.js +92 -0
  795. package/lib/vendor/blamejs/scripts/test-integration.js +181 -0
  796. package/lib/vendor/blamejs/scripts/test-wiki-integration.js +126 -0
  797. package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +77 -0
  798. package/lib/vendor/blamejs/scripts/vendor-data-gen.js +186 -0
  799. package/lib/vendor/blamejs/scripts/vendor-data-keygen.js +101 -0
  800. package/lib/vendor/blamejs/scripts/vendor-update.sh +278 -0
  801. package/lib/vendor/blamejs/test/00-primitives.js +19075 -0
  802. package/lib/vendor/blamejs/test/10-state.js +622 -0
  803. package/lib/vendor/blamejs/test/20-db.js +561 -0
  804. package/lib/vendor/blamejs/test/30-chain.js +2110 -0
  805. package/lib/vendor/blamejs/test/40-consumers.js +2453 -0
  806. package/lib/vendor/blamejs/test/50-integration.js +486 -0
  807. package/lib/vendor/blamejs/test/_helpers.js +10 -0
  808. package/lib/vendor/blamejs/test/_smoke-worker.js +69 -0
  809. package/lib/vendor/blamejs/test/fixtures/exploit-corpus/corpus.json +368 -0
  810. package/lib/vendor/blamejs/test/fixtures/http-client-stream-payload.txt +2 -0
  811. package/lib/vendor/blamejs/test/fixtures/worker-pool/echo.js +52 -0
  812. package/lib/vendor/blamejs/test/helpers/_codebase-shingle-worker.js +24 -0
  813. package/lib/vendor/blamejs/test/helpers/_codebase-shingle.js +203 -0
  814. package/lib/vendor/blamejs/test/helpers/_shape-match.js +513 -0
  815. package/lib/vendor/blamejs/test/helpers/check.js +36 -0
  816. package/lib/vendor/blamejs/test/helpers/cluster.js +70 -0
  817. package/lib/vendor/blamejs/test/helpers/db.js +143 -0
  818. package/lib/vendor/blamejs/test/helpers/drivers.js +207 -0
  819. package/lib/vendor/blamejs/test/helpers/fs-watch.js +101 -0
  820. package/lib/vendor/blamejs/test/helpers/http.js +14 -0
  821. package/lib/vendor/blamejs/test/helpers/index.js +93 -0
  822. package/lib/vendor/blamejs/test/helpers/json-round-trip.js +120 -0
  823. package/lib/vendor/blamejs/test/helpers/mocks.js +20 -0
  824. package/lib/vendor/blamejs/test/helpers/otel.js +13 -0
  825. package/lib/vendor/blamejs/test/helpers/services.js +380 -0
  826. package/lib/vendor/blamejs/test/helpers/wait.js +206 -0
  827. package/lib/vendor/blamejs/test/integration/cache.test.js +235 -0
  828. package/lib/vendor/blamejs/test/integration/cluster-provider-mysql.test.js +174 -0
  829. package/lib/vendor/blamejs/test/integration/federation-auth.test.js +611 -0
  830. package/lib/vendor/blamejs/test/integration/http-client.test.js +129 -0
  831. package/lib/vendor/blamejs/test/integration/log-stream.test.js +219 -0
  832. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +181 -0
  833. package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +152 -0
  834. package/lib/vendor/blamejs/test/integration/mail-smtp.test.js +161 -0
  835. package/lib/vendor/blamejs/test/integration/mtls-ca.test.js +289 -0
  836. package/lib/vendor/blamejs/test/integration/network-dns.test.js +123 -0
  837. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +101 -0
  838. package/lib/vendor/blamejs/test/integration/ntp-check.test.js +89 -0
  839. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +403 -0
  840. package/lib/vendor/blamejs/test/integration/pqc-pkcs8-forward-compat.test.js +271 -0
  841. package/lib/vendor/blamejs/test/integration/pubsub.test.js +137 -0
  842. package/lib/vendor/blamejs/test/integration/queue-redis.test.js +352 -0
  843. package/lib/vendor/blamejs/test/integration/redis-client-tls.test.js +96 -0
  844. package/lib/vendor/blamejs/test/integration/ssrf-guard.test.js +98 -0
  845. package/lib/vendor/blamejs/test/integration/websocket-permessage-deflate.test.js +261 -0
  846. package/lib/vendor/blamejs/test/integration/ws-client-roundtrip.test.js +230 -0
  847. package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +211 -0
  848. package/lib/vendor/blamejs/test/layer-0-primitives/a2a.test.js +59 -0
  849. package/lib/vendor/blamejs/test/layer-0-primitives/access-lock.test.js +136 -0
  850. package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +219 -0
  851. package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +69 -0
  852. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +266 -0
  853. package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +262 -0
  854. package/lib/vendor/blamejs/test/layer-0-primitives/agent-orchestrator.test.js +390 -0
  855. package/lib/vendor/blamejs/test/layer-0-primitives/agent-posture-chain.test.js +174 -0
  856. package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +279 -0
  857. package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +322 -0
  858. package/lib/vendor/blamejs/test/layer-0-primitives/agent-stream.test.js +227 -0
  859. package/lib/vendor/blamejs/test/layer-0-primitives/agent-tenant.test.js +302 -0
  860. package/lib/vendor/blamejs/test/layer-0-primitives/agent-trace.test.js +150 -0
  861. package/lib/vendor/blamejs/test/layer-0-primitives/ai-adverse-decision.test.js +44 -0
  862. package/lib/vendor/blamejs/test/layer-0-primitives/ai-content-detect.test.js +150 -0
  863. package/lib/vendor/blamejs/test/layer-0-primitives/ai-input.test.js +50 -0
  864. package/lib/vendor/blamejs/test/layer-0-primitives/ai-model-manifest.test.js +96 -0
  865. package/lib/vendor/blamejs/test/layer-0-primitives/ai-pref.test.js +76 -0
  866. package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +1080 -0
  867. package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +311 -0
  868. package/lib/vendor/blamejs/test/layer-0-primitives/archive-zip-stream.test.js +291 -0
  869. package/lib/vendor/blamejs/test/layer-0-primitives/archive.test.js +140 -0
  870. package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +267 -0
  871. package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +108 -0
  872. package/lib/vendor/blamejs/test/layer-0-primitives/asyncapi.test.js +929 -0
  873. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-conflict-path.test.js +80 -0
  874. package/lib/vendor/blamejs/test/layer-0-primitives/audit-cve-defensive.test.js +176 -0
  875. package/lib/vendor/blamejs/test/layer-0-primitives/audit-daily-review.test.js +132 -0
  876. package/lib/vendor/blamejs/test/layer-0-primitives/audit-export-cadf.test.js +97 -0
  877. package/lib/vendor/blamejs/test/layer-0-primitives/audit-framework-namespaces.test.js +141 -0
  878. package/lib/vendor/blamejs/test/layer-0-primitives/audit-segregation.test.js +115 -0
  879. package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-ml-dsa-65.test.js +163 -0
  880. package/lib/vendor/blamejs/test/layer-0-primitives/audit-use-store.test.js +246 -0
  881. package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge-verifier.test.js +485 -0
  882. package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge.test.js +331 -0
  883. package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +352 -0
  884. package/lib/vendor/blamejs/test/layer-0-primitives/auth-lockout.test.js +572 -0
  885. package/lib/vendor/blamejs/test/layer-0-primitives/auth-password-audit.test.js +61 -0
  886. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-bucket-ops.test.js +258 -0
  887. package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +105 -0
  888. package/lib/vendor/blamejs/test/layer-0-primitives/backup-worker.test.js +34 -0
  889. package/lib/vendor/blamejs/test/layer-0-primitives/bearer-auth.test.js +107 -0
  890. package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-chunked-malformed.test.js +131 -0
  891. package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-smuggling.test.js +118 -0
  892. package/lib/vendor/blamejs/test/layer-0-primitives/boot-gates.test.js +85 -0
  893. package/lib/vendor/blamejs/test/layer-0-primitives/breach-deadline.test.js +38 -0
  894. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +861 -0
  895. package/lib/vendor/blamejs/test/layer-0-primitives/budr.test.js +55 -0
  896. package/lib/vendor/blamejs/test/layer-0-primitives/bundler-engine.test.js +209 -0
  897. package/lib/vendor/blamejs/test/layer-0-primitives/cache-status.test.js +129 -0
  898. package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +871 -0
  899. package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +891 -0
  900. package/lib/vendor/blamejs/test/layer-0-primitives/canonical-json-jcs.test.js +43 -0
  901. package/lib/vendor/blamejs/test/layer-0-primitives/cdn-cache-control.test.js +243 -0
  902. package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +550 -0
  903. package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +107 -0
  904. package/lib/vendor/blamejs/test/layer-0-primitives/cli-api-key.test.js +147 -0
  905. package/lib/vendor/blamejs/test/layer-0-primitives/cli-audit-verify-chain.test.js +104 -0
  906. package/lib/vendor/blamejs/test/layer-0-primitives/cli-backup.test.js +135 -0
  907. package/lib/vendor/blamejs/test/layer-0-primitives/cli-config-drift.test.js +67 -0
  908. package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +75 -0
  909. package/lib/vendor/blamejs/test/layer-0-primitives/cli-file-type.test.js +98 -0
  910. package/lib/vendor/blamejs/test/layer-0-primitives/cli-helpers.test.js +145 -0
  911. package/lib/vendor/blamejs/test/layer-0-primitives/cli-mtls.test.js +133 -0
  912. package/lib/vendor/blamejs/test/layer-0-primitives/cli-password.test.js +97 -0
  913. package/lib/vendor/blamejs/test/layer-0-primitives/cli-restore.test.js +160 -0
  914. package/lib/vendor/blamejs/test/layer-0-primitives/cli-retention.test.js +84 -0
  915. package/lib/vendor/blamejs/test/layer-0-primitives/cli-security.test.js +69 -0
  916. package/lib/vendor/blamejs/test/layer-0-primitives/cli-vault.test.js +142 -0
  917. package/lib/vendor/blamejs/test/layer-0-primitives/client-hints.test.js +133 -0
  918. package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +237 -0
  919. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +9600 -0
  920. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +575 -0
  921. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-cascade.test.js +89 -0
  922. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-eaa.test.js +36 -0
  923. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +712 -0
  924. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +278 -0
  925. package/lib/vendor/blamejs/test/layer-0-primitives/config-drift.test.js +97 -0
  926. package/lib/vendor/blamejs/test/layer-0-primitives/config.test.js +424 -0
  927. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +94 -0
  928. package/lib/vendor/blamejs/test/layer-0-primitives/cors.test.js +357 -0
  929. package/lib/vendor/blamejs/test/layer-0-primitives/cra-report.test.js +31 -0
  930. package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +226 -0
  931. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-base64url.test.js +86 -0
  932. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-envelope.test.js +85 -0
  933. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +193 -0
  934. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +98 -0
  935. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hpke-pq.test.js +132 -0
  936. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hpke.test.js +155 -0
  937. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-mlkem768-x25519.test.js +129 -0
  938. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-namespace-hash.test.js +0 -0
  939. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-random-int.test.js +72 -0
  940. package/lib/vendor/blamejs/test/layer-0-primitives/csp-builder.test.js +96 -0
  941. package/lib/vendor/blamejs/test/layer-0-primitives/csp-nonce.test.js +401 -0
  942. package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +34 -0
  943. package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +180 -0
  944. package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +210 -0
  945. package/lib/vendor/blamejs/test/layer-0-primitives/daily-byte-quota.test.js +153 -0
  946. package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +66 -0
  947. package/lib/vendor/blamejs/test/layer-0-primitives/data-act.test.js +74 -0
  948. package/lib/vendor/blamejs/test/layer-0-primitives/db-collection-extensions.test.js +226 -0
  949. package/lib/vendor/blamejs/test/layer-0-primitives/db-collection.test.js +136 -0
  950. package/lib/vendor/blamejs/test/layer-0-primitives/db-init-extensions.test.js +165 -0
  951. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +150 -0
  952. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-extensions.test.js +191 -0
  953. package/lib/vendor/blamejs/test/layer-0-primitives/db-role-for.test.js +228 -0
  954. package/lib/vendor/blamejs/test/layer-0-primitives/db-vacuum.test.js +55 -0
  955. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +89 -0
  956. package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +184 -0
  957. package/lib/vendor/blamejs/test/layer-0-primitives/declare-row-policy.test.js +203 -0
  958. package/lib/vendor/blamejs/test/layer-0-primitives/declare-view.test.js +303 -0
  959. package/lib/vendor/blamejs/test/layer-0-primitives/dns-dnssec-algorithm.test.js +163 -0
  960. package/lib/vendor/blamejs/test/layer-0-primitives/dns-null-mx.test.js +39 -0
  961. package/lib/vendor/blamejs/test/layer-0-primitives/dora.test.js +165 -0
  962. package/lib/vendor/blamejs/test/layer-0-primitives/dr-runbook.test.js +59 -0
  963. package/lib/vendor/blamejs/test/layer-0-primitives/dsr-state-rules.test.js +55 -0
  964. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +786 -0
  965. package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +105 -0
  966. package/lib/vendor/blamejs/test/layer-0-primitives/early-hints.test.js +147 -0
  967. package/lib/vendor/blamejs/test/layer-0-primitives/events.test.js +105 -0
  968. package/lib/vendor/blamejs/test/layer-0-primitives/exploit-replay.test.js +243 -0
  969. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +181 -0
  970. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +190 -0
  971. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-routing.test.js +531 -0
  972. package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +118 -0
  973. package/lib/vendor/blamejs/test/layer-0-primitives/fapi2.test.js +89 -0
  974. package/lib/vendor/blamejs/test/layer-0-primitives/fda-21cfr11.test.js +156 -0
  975. package/lib/vendor/blamejs/test/layer-0-primitives/fdx.test.js +79 -0
  976. package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +216 -0
  977. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +434 -0
  978. package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +432 -0
  979. package/lib/vendor/blamejs/test/layer-0-primitives/file-type.test.js +81 -0
  980. package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +887 -0
  981. package/lib/vendor/blamejs/test/layer-0-primitives/forensic-snapshot.test.js +51 -0
  982. package/lib/vendor/blamejs/test/layer-0-primitives/fsm.test.js +375 -0
  983. package/lib/vendor/blamejs/test/layer-0-primitives/gcs-bucket-ops.test.js +321 -0
  984. package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +41 -0
  985. package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +32 -0
  986. package/lib/vendor/blamejs/test/layer-0-primitives/guard-agent-registry.test.js +87 -0
  987. package/lib/vendor/blamejs/test/layer-0-primitives/guard-all.test.js +328 -0
  988. package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +339 -0
  989. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +694 -0
  990. package/lib/vendor/blamejs/test/layer-0-primitives/guard-dsn.test.js +296 -0
  991. package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +234 -0
  992. package/lib/vendor/blamejs/test/layer-0-primitives/guard-envelope.test.js +192 -0
  993. package/lib/vendor/blamejs/test/layer-0-primitives/guard-event-bus-payload.test.js +89 -0
  994. package/lib/vendor/blamejs/test/layer-0-primitives/guard-event-bus-topic.test.js +71 -0
  995. package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +386 -0
  996. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html-wcag.test.js +859 -0
  997. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +357 -0
  998. package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +92 -0
  999. package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
  1000. package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +174 -0
  1001. package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +317 -0
  1002. package/lib/vendor/blamejs/test/layer-0-primitives/guard-list-id.test.js +199 -0
  1003. package/lib/vendor/blamejs/test/layer-0-primitives/guard-list-unsubscribe.test.js +214 -0
  1004. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-compose.test.js +111 -0
  1005. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-move.test.js +110 -0
  1006. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-query.test.js +112 -0
  1007. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-reply.test.js +86 -0
  1008. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-sieve.test.js +92 -0
  1009. package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +301 -0
  1010. package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +265 -0
  1011. package/lib/vendor/blamejs/test/layer-0-primitives/guard-message-id.test.js +0 -0
  1012. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +161 -0
  1013. package/lib/vendor/blamejs/test/layer-0-primitives/guard-posture-chain.test.js +100 -0
  1014. package/lib/vendor/blamejs/test/layer-0-primitives/guard-saga-config.test.js +79 -0
  1015. package/lib/vendor/blamejs/test/layer-0-primitives/guard-smtp-command.test.js +269 -0
  1016. package/lib/vendor/blamejs/test/layer-0-primitives/guard-snapshot-envelope.test.js +89 -0
  1017. package/lib/vendor/blamejs/test/layer-0-primitives/guard-stream-args.test.js +78 -0
  1018. package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +288 -0
  1019. package/lib/vendor/blamejs/test/layer-0-primitives/guard-tenant-id.test.js +69 -0
  1020. package/lib/vendor/blamejs/test/layer-0-primitives/guard-trace-context.test.js +102 -0
  1021. package/lib/vendor/blamejs/test/layer-0-primitives/guard-xml.test.js +202 -0
  1022. package/lib/vendor/blamejs/test/layer-0-primitives/guard-yaml.test.js +203 -0
  1023. package/lib/vendor/blamejs/test/layer-0-primitives/hal.test.js +51 -0
  1024. package/lib/vendor/blamejs/test/layer-0-primitives/honeytoken.test.js +50 -0
  1025. package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +37 -0
  1026. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +692 -0
  1027. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +280 -0
  1028. package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +225 -0
  1029. package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +203 -0
  1030. package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +991 -0
  1031. package/lib/vendor/blamejs/test/layer-0-primitives/iab-mspa.test.js +63 -0
  1032. package/lib/vendor/blamejs/test/layer-0-primitives/iab-tcf.test.js +73 -0
  1033. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +612 -0
  1034. package/lib/vendor/blamejs/test/layer-0-primitives/importmap-integrity.test.js +56 -0
  1035. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +166 -0
  1036. package/lib/vendor/blamejs/test/layer-0-primitives/incident-report.test.js +29 -0
  1037. package/lib/vendor/blamejs/test/layer-0-primitives/jose-jwe-experimental.test.js +121 -0
  1038. package/lib/vendor/blamejs/test/layer-0-primitives/json-api.test.js +58 -0
  1039. package/lib/vendor/blamejs/test/layer-0-primitives/json-round-trip-helper.test.js +110 -0
  1040. package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +159 -0
  1041. package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
  1042. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +118 -0
  1043. package/lib/vendor/blamejs/test/layer-0-primitives/local-db-thin.test.js +150 -0
  1044. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-cloudwatch.test.js +489 -0
  1045. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-otlp-grpc.test.js +207 -0
  1046. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-otlp.test.js +283 -0
  1047. package/lib/vendor/blamejs/test/layer-0-primitives/lro.test.js +65 -0
  1048. package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +417 -0
  1049. package/lib/vendor/blamejs/test/layer-0-primitives/mail-arf.test.js +208 -0
  1050. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +910 -0
  1051. package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +502 -0
  1052. package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +680 -0
  1053. package/lib/vendor/blamejs/test/layer-0-primitives/mail-canspam.test.js +128 -0
  1054. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +149 -0
  1055. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +323 -0
  1056. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +297 -0
  1057. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +514 -0
  1058. package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy-tlsrpt.test.js +369 -0
  1059. package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +199 -0
  1060. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +627 -0
  1061. package/lib/vendor/blamejs/test/layer-0-primitives/mail-feedback-id.test.js +56 -0
  1062. package/lib/vendor/blamejs/test/layer-0-primitives/mail-greylist.test.js +217 -0
  1063. package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +283 -0
  1064. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +217 -0
  1065. package/lib/vendor/blamejs/test/layer-0-primitives/mail-mdn.test.js +334 -0
  1066. package/lib/vendor/blamejs/test/layer-0-primitives/mail-rbl.test.js +271 -0
  1067. package/lib/vendor/blamejs/test/layer-0-primitives/mail-require-tls.test.js +128 -0
  1068. package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +215 -0
  1069. package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +336 -0
  1070. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +732 -0
  1071. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +840 -0
  1072. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +130 -0
  1073. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +285 -0
  1074. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +74 -0
  1075. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +112 -0
  1076. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +229 -0
  1077. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +394 -0
  1078. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-tls.test.js +147 -0
  1079. package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +151 -0
  1080. package/lib/vendor/blamejs/test/layer-0-primitives/mail-spam-score.test.js +204 -0
  1081. package/lib/vendor/blamejs/test/layer-0-primitives/mail-srs.test.js +152 -0
  1082. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +279 -0
  1083. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +323 -0
  1084. package/lib/vendor/blamejs/test/layer-0-primitives/mail-unsubscribe.test.js +165 -0
  1085. package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +439 -0
  1086. package/lib/vendor/blamejs/test/layer-0-primitives/mcp-tool-registry.test.js +202 -0
  1087. package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +155 -0
  1088. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-shadow-registry.test.js +112 -0
  1089. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +224 -0
  1090. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-compose-pipeline.test.js +278 -0
  1091. package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +376 -0
  1092. package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-paths.test.js +89 -0
  1093. package/lib/vendor/blamejs/test/layer-0-primitives/nel.test.js +200 -0
  1094. package/lib/vendor/blamejs/test/layer-0-primitives/network-allowlist.test.js +106 -0
  1095. package/lib/vendor/blamejs/test/layer-0-primitives/network-byte-quota.test.js +133 -0
  1096. package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +372 -0
  1097. package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +635 -0
  1098. package/lib/vendor/blamejs/test/layer-0-primitives/network-heartbeat-passive.test.js +128 -0
  1099. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls-build-options.test.js +130 -0
  1100. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls-ct-inclusion.test.js +179 -0
  1101. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +447 -0
  1102. package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +369 -0
  1103. package/lib/vendor/blamejs/test/layer-0-primitives/nis2-report.test.js +21 -0
  1104. package/lib/vendor/blamejs/test/layer-0-primitives/nist-crosswalk.test.js +42 -0
  1105. package/lib/vendor/blamejs/test/layer-0-primitives/no-cache.test.js +98 -0
  1106. package/lib/vendor/blamejs/test/layer-0-primitives/notify.test.js +707 -0
  1107. package/lib/vendor/blamejs/test/layer-0-primitives/numeric-bounds.test.js +142 -0
  1108. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +72 -0
  1109. package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +597 -0
  1110. package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +190 -0
  1111. package/lib/vendor/blamejs/test/layer-0-primitives/openapi.test.js +877 -0
  1112. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +257 -0
  1113. package/lib/vendor/blamejs/test/layer-0-primitives/pagination.test.js +522 -0
  1114. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +216 -0
  1115. package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +324 -0
  1116. package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +546 -0
  1117. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +153 -0
  1118. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-software.test.js +94 -0
  1119. package/lib/vendor/blamejs/test/layer-0-primitives/problem-details.test.js +195 -0
  1120. package/lib/vendor/blamejs/test/layer-0-primitives/process-spawn.test.js +62 -0
  1121. package/lib/vendor/blamejs/test/layer-0-primitives/promise-pool.test.js +93 -0
  1122. package/lib/vendor/blamejs/test/layer-0-primitives/protected-resource-metadata.test.js +68 -0
  1123. package/lib/vendor/blamejs/test/layer-0-primitives/protobuf-encoder.test.js +138 -0
  1124. package/lib/vendor/blamejs/test/layer-0-primitives/protocol-dispatcher.test.js +174 -0
  1125. package/lib/vendor/blamejs/test/layer-0-primitives/public-suffix.test.js +197 -0
  1126. package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +232 -0
  1127. package/lib/vendor/blamejs/test/layer-0-primitives/queue-dlq-extend-lease.test.js +178 -0
  1128. package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +322 -0
  1129. package/lib/vendor/blamejs/test/layer-0-primitives/queue-priority-rate-progress.test.js +266 -0
  1130. package/lib/vendor/blamejs/test/layer-0-primitives/queue-sqs.test.js +300 -0
  1131. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-cluster.test.js +338 -0
  1132. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-registry.test.js +75 -0
  1133. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +246 -0
  1134. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +130 -0
  1135. package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +335 -0
  1136. package/lib/vendor/blamejs/test/layer-0-primitives/request-log.test.js +170 -0
  1137. package/lib/vendor/blamejs/test/layer-0-primitives/require-auth-cache-control.test.js +93 -0
  1138. package/lib/vendor/blamejs/test/layer-0-primitives/require-mtls.test.js +34 -0
  1139. package/lib/vendor/blamejs/test/layer-0-primitives/resource-access-lock.test.js +52 -0
  1140. package/lib/vendor/blamejs/test/layer-0-primitives/retention-floor.test.js +67 -0
  1141. package/lib/vendor/blamejs/test/layer-0-primitives/retry.test.js +535 -0
  1142. package/lib/vendor/blamejs/test/layer-0-primitives/router-cross-origin-redirect.test.js +0 -0
  1143. package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +128 -0
  1144. package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-loops.test.js +163 -0
  1145. package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-parallel.test.js +170 -0
  1146. package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +248 -0
  1147. package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +451 -0
  1148. package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +289 -0
  1149. package/lib/vendor/blamejs/test/layer-0-primitives/safe-icap.test.js +206 -0
  1150. package/lib/vendor/blamejs/test/layer-0-primitives/safe-jsonpath.test.js +104 -0
  1151. package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +339 -0
  1152. package/lib/vendor/blamejs/test/layer-0-primitives/safe-mount-info.test.js +180 -0
  1153. package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +78 -0
  1154. package/lib/vendor/blamejs/test/layer-0-primitives/safe-sieve.test.js +123 -0
  1155. package/lib/vendor/blamejs/test/layer-0-primitives/safe-smtp.test.js +95 -0
  1156. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-idn-homograph.test.js +77 -0
  1157. package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +257 -0
  1158. package/lib/vendor/blamejs/test/layer-0-primitives/saml-slo.test.js +249 -0
  1159. package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +228 -0
  1160. package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-exactly-once.test.js +238 -0
  1161. package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +92 -0
  1162. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +700 -0
  1163. package/lib/vendor/blamejs/test/layer-0-primitives/sd-notify.test.js +67 -0
  1164. package/lib/vendor/blamejs/test/layer-0-primitives/sec-cyber.test.js +85 -0
  1165. package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +107 -0
  1166. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +175 -0
  1167. package/lib/vendor/blamejs/test/layer-0-primitives/seeders.test.js +816 -0
  1168. package/lib/vendor/blamejs/test/layer-0-primitives/self-update-standalone-verifier.test.js +168 -0
  1169. package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +302 -0
  1170. package/lib/vendor/blamejs/test/layer-0-primitives/server-timing.test.js +93 -0
  1171. package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +247 -0
  1172. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +295 -0
  1173. package/lib/vendor/blamejs/test/layer-0-primitives/shape-match.test.js +142 -0
  1174. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +952 -0
  1175. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-multipart-sse.test.js +441 -0
  1176. package/lib/vendor/blamejs/test/layer-0-primitives/slug.test.js +330 -0
  1177. package/lib/vendor/blamejs/test/layer-0-primitives/smtp-policy.test.js +233 -0
  1178. package/lib/vendor/blamejs/test/layer-0-primitives/source-comment-blocks.test.js +105 -0
  1179. package/lib/vendor/blamejs/test/layer-0-primitives/speculation-rules.test.js +319 -0
  1180. package/lib/vendor/blamejs/test/layer-0-primitives/sse.test.js +148 -0
  1181. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +283 -0
  1182. package/lib/vendor/blamejs/test/layer-0-primitives/standard-webhooks.test.js +67 -0
  1183. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +266 -0
  1184. package/lib/vendor/blamejs/test/layer-0-primitives/step-up.test.js +487 -0
  1185. package/lib/vendor/blamejs/test/layer-0-primitives/storage-chunk-scratch.test.js +0 -0
  1186. package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +773 -0
  1187. package/lib/vendor/blamejs/test/layer-0-primitives/stream-throttle.test.js +173 -0
  1188. package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields.test.js +180 -0
  1189. package/lib/vendor/blamejs/test/layer-0-primitives/tcpa-10dlc.test.js +66 -0
  1190. package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +89 -0
  1191. package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +571 -0
  1192. package/lib/vendor/blamejs/test/layer-0-primitives/test-harness.test.js +190 -0
  1193. package/lib/vendor/blamejs/test/layer-0-primitives/testing-request.test.js +119 -0
  1194. package/lib/vendor/blamejs/test/layer-0-primitives/testing.test.js +522 -0
  1195. package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +151 -0
  1196. package/lib/vendor/blamejs/test/layer-0-primitives/tls-exporter.test.js +168 -0
  1197. package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-ct.test.js +275 -0
  1198. package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-verify.test.js +105 -0
  1199. package/lib/vendor/blamejs/test/layer-0-primitives/tls-pinset-drift.test.js +35 -0
  1200. package/lib/vendor/blamejs/test/layer-0-primitives/tls-preferred-groups.test.js +81 -0
  1201. package/lib/vendor/blamejs/test/layer-0-primitives/tracing.test.js +280 -0
  1202. package/lib/vendor/blamejs/test/layer-0-primitives/uuid.test.js +93 -0
  1203. package/lib/vendor/blamejs/test/layer-0-primitives/vault-aad.test.js +277 -0
  1204. package/lib/vendor/blamejs/test/layer-0-primitives/vault-seal-pem-file.test.js +252 -0
  1205. package/lib/vendor/blamejs/test/layer-0-primitives/vendor-data.test.js +149 -0
  1206. package/lib/vendor/blamejs/test/layer-0-primitives/vendor-manifest.test.js +92 -0
  1207. package/lib/vendor/blamejs/test/layer-0-primitives/vex.test.js +661 -0
  1208. package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +308 -0
  1209. package/lib/vendor/blamejs/test/layer-0-primitives/web-push-vapid.test.js +144 -0
  1210. package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +674 -0
  1211. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-channels.test.js +360 -0
  1212. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool.test.js +302 -0
  1213. package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +349 -0
  1214. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +717 -0
  1215. package/lib/vendor/blamejs/test/layer-5-integration/bundler-output.test.js +444 -0
  1216. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +597 -0
  1217. package/lib/vendor/blamejs/test/layer-5-integration/security-chaos.test.js +308 -0
  1218. package/lib/vendor/blamejs/test/smoke.js +431 -0
  1219. package/lib/webhooks.js +305 -0
  1220. package/package.json +43 -0
@@ -0,0 +1,2144 @@
1
+ "use strict";
2
+ /**
3
+ * b.mail.spf + b.mail.dmarc + b.mail.arc — inbound mail authentication
4
+ * verification family. Counterpart to the existing outbound DKIM
5
+ * signer in lib/mail-dkim.js.
6
+ *
7
+ * Operators receiving mail (incoming webhooks, customer-support
8
+ * inboxes, mailing-list ingestion, .eml uploads) need this to evaluate
9
+ * sender authenticity and decide on accept / quarantine / reject.
10
+ *
11
+ * Surface:
12
+ * b.mail.spf.verify({ ip, mailFrom, helo, dnsLookup }) → result
13
+ * b.mail.dmarc.evaluate({ from, spf, dkim, dnsLookup }) → result
14
+ * b.mail.arc.verify(rfc822, opts) → chain status
15
+ *
16
+ * SPF (RFC 7208) — ip4 / ip6 / a / mx / include / all / redirect=
17
+ * mechanisms.
18
+ * Mechanism limit: 10 DNS lookups per RFC 7208 §4.6.4 (with the
19
+ * void-lookup sub-limit at 2). The `a` and `mx` arms honor RFC
20
+ * §5.3 / §5.4 dual-cidr-length syntax (`a:foo.com/24//64`).
21
+ *
22
+ * Deferred mechanisms (each carries an explicit Re-open condition
23
+ * in the dispatch arm in this file):
24
+ * - exists: requires macro-string expansion (§7) to be useful;
25
+ * re-opens when macros land OR an operator surfaces a
26
+ * real macro-less `exists:` policy.
27
+ * - ptr: "strongly discouraged" by §5.5; re-opens when an
28
+ * operator surfaces a legitimate ptr-only sender.
29
+ * - macro-string expansion (§7) itself — separate slice tracked
30
+ * under blamejs-roadmap.md.
31
+ *
32
+ * DMARC (RFC 7489) — TXT record at _dmarc.<domain>; alignment check
33
+ * between From-header domain and DKIM-d / SPF-from-domain;
34
+ * policy resolution (none / quarantine / reject) per the published
35
+ * record. The org-domain extraction uses an operator-supplied
36
+ * `dnsLookup` callback (the framework doesn't ship the Public Suffix
37
+ * List).
38
+ *
39
+ * ARC (RFC 8617) — chain-of-custody verification. The framework parses
40
+ * the existing chain headers, recomputes the per-hop signatures, and
41
+ * reports validity by composing `lib/mail-dkim.js` (which carries
42
+ * the actual signature-verification surface).
43
+ */
44
+
45
+ var zlib = require("node:zlib");
46
+ var net = require("node:net");
47
+ var nodeCrypto = require("node:crypto");
48
+ var lazyRequire = require("./lazy-require");
49
+ var validateOpts = require("./validate-opts");
50
+ var bCrypto = require("./crypto");
51
+ var C = require("./constants");
52
+ var dkim = require("./mail-dkim");
53
+ var safeXml = require("./parsers/safe-xml");
54
+ var ipUtils = require("./ip-utils");
55
+ var publicSuffix = require("./public-suffix");
56
+ var networkDnsResolver = lazyRequire(function () { return require("./network-dns-resolver"); });
57
+ var { MailAuthError } = require("./framework-error");
58
+
59
+ var observability = lazyRequire(function () { return require("./observability"); });
60
+ void observability;
61
+
62
+ // SPF DNS-lookup ceiling per RFC 7208 §4.6.4. Operators with high-
63
+ // fan-out include chains hit this; the verify path returns "permerror"
64
+ // when crossed, matching mainstream MTAs.
65
+ var SPF_DNS_LOOKUP_LIMIT = 10;
66
+
67
+ // RFC 7208 §4.6.4 — "void lookup" cap. A void lookup is a successful
68
+ // DNS query whose answer is empty (NXDOMAIN, no-data response, or
69
+ // zero records returned). The SPF spec caps void lookups at 2; beyond
70
+ // that the policy MUST permerror. Attackers chain misconfigured
71
+ // `include:`s pointing at non-existent domains to amplify recursive
72
+ // resolver work without tripping the 10-lookup ceiling.
73
+ var SPF_VOID_LOOKUP_LIMIT = 2; // allow:raw-byte-literal — RFC 7208 §4.6.4 void-lookup ceiling
74
+
75
+ // RFC 7208 §3.3 — each SPF TXT record MUST NOT exceed 450 bytes when
76
+ // concatenated across multi-string TXT chunks. The spec lifts a
77
+ // receiver MUST-refuse on >450-byte records to bound parse work.
78
+ var SPF_RECORD_MAX_BYTES = 450; // allow:raw-byte-literal — RFC 7208 §3.3 record ceiling
79
+
80
+ // SPF redirect= modifier (RFC 7208 §6.1) recursion cap. The modifier
81
+ // re-evaluates against a different domain; a chain of redirect= cycles
82
+ // MUST terminate. We bound at the same depth as the lookup ceiling
83
+ // minus current count (the redirect itself counts as one lookup); the
84
+ // hard cap below is an additional belt-and-braces against malformed
85
+ // upstream policies that would otherwise spin until the lookup cap
86
+ // alone tripped.
87
+ var SPF_REDIRECT_DEPTH_LIMIT = 10; // allow:raw-byte-literal — same shape as RFC 7208 §4.6.4 lookup ceiling
88
+
89
+ // Shared safe-DNS TXT/A/AAAA/MX/PTR lookup. Operator-supplied
90
+ // `dnsLookup(qname, type)` is honored for every type when present:
91
+ // TXT → [[ "v=spf1 ...", ... ], ...] (array of TXT-string-arrays)
92
+ // A → [ "192.0.2.1", ... ] (flat IPv4 string array)
93
+ // AAAA → [ "2001:db8::1", ... ] (flat IPv6 string array)
94
+ // MX → [ { exchange, preference }, ...] (or [ "mx1.example.", ... ]
95
+ // when operator omits preference)
96
+ // PTR → [ "host.example.", ... ] (flat PTR-name array)
97
+ // When no operator callback is supplied, requests route through
98
+ // `b.network.dns.resolver` (DoH by default per v0.7.23). CVE-2008-1447
99
+ // (Kaminsky) + CVE-2022-3204 (NRDelegationAttack) class — the encrypted
100
+ // DoH transport plus b.safeDns parse caps defend transport and parse-
101
+ // side. Earlier shape fell back to `node:dns.promises.resolveTxt`
102
+ // directly, which sent plaintext UDP/53 to whatever the system
103
+ // resolver was — every downstream finding inherited that exposure.
104
+ var _defaultResolver = null;
105
+ function _getDefaultResolver() {
106
+ if (_defaultResolver) return _defaultResolver;
107
+ _defaultResolver = networkDnsResolver().create();
108
+ return _defaultResolver;
109
+ }
110
+
111
+ async function _safeResolveTxt(qname, operatorLookup) {
112
+ if (operatorLookup) return operatorLookup(qname, "TXT");
113
+ var r = await _getDefaultResolver().queryTxt(qname);
114
+ var out = [];
115
+ for (var i = 0; i < r.rrs.length; i += 1) {
116
+ var rr = r.rrs[i];
117
+ if (rr && rr.type === 16) { // allow:raw-byte-literal — IANA DNS qtype TXT
118
+ out.push(Array.isArray(rr.decoded) ? rr.decoded : [String(rr.decoded)]);
119
+ }
120
+ }
121
+ if (out.length === 0) {
122
+ var err = new Error("no TXT records for " + qname);
123
+ err.code = "ENODATA";
124
+ throw err;
125
+ }
126
+ return out;
127
+ }
128
+
129
+ async function _safeResolveA(qname, family /* 4|6 */, operatorLookup) {
130
+ // Pre-v0.11.3 the operatorLookup parameter wasn't threaded here, so
131
+ // the documented `dnsLookup` shape for A/AAAA was unhonored — SPF a/
132
+ // mx mechanism tests had no operator-mockable path. The function
133
+ // signature now matches the docstring contract above. Operator
134
+ // returns a flat string array of IP literals.
135
+ if (operatorLookup) {
136
+ var resp = await operatorLookup(qname, family === 6 ? "AAAA" : "A");
137
+ if (!Array.isArray(resp) || resp.length === 0) {
138
+ var aerr = new Error("no " + (family === 6 ? "AAAA" : "A") + " records for " + qname);
139
+ aerr.code = "ENODATA";
140
+ throw aerr;
141
+ }
142
+ return resp.map(function (x) { return String(x); });
143
+ }
144
+ var r = await _getDefaultResolver().query(qname, family === 6 ? "AAAA" : "A");
145
+ var out = [];
146
+ for (var i = 0; i < r.rrs.length; i += 1) {
147
+ var rr = r.rrs[i];
148
+ var wantType = family === 6 ? 28 : 1; // allow:raw-byte-literal — IANA DNS qtype AAAA / A
149
+ if (rr && rr.type === wantType) out.push(rr.decoded);
150
+ }
151
+ if (out.length === 0) {
152
+ var err = new Error("no " + (family === 6 ? "AAAA" : "A") + " records for " + qname);
153
+ err.code = "ENODATA";
154
+ throw err;
155
+ }
156
+ return out;
157
+ }
158
+
159
+ // RFC 1035 §3.3.9 MX record: { preference, exchange }. Returns array of
160
+ // exchange hostnames sorted by preference (lowest first). Operator-
161
+ // supplied dnsLookup callback may return either:
162
+ // - [ { exchange, preference }, ... ] — full shape (preferred)
163
+ // - [ "mx1.example.", ... ] — exchanges only (preference
164
+ // treated as 0 → first-served)
165
+ async function _safeResolveMx(qname, operatorLookup) {
166
+ if (operatorLookup) {
167
+ var resp = await operatorLookup(qname, "MX");
168
+ if (!Array.isArray(resp) || resp.length === 0) {
169
+ var merr = new Error("no MX records for " + qname);
170
+ merr.code = "ENODATA";
171
+ throw merr;
172
+ }
173
+ var normalized = resp.map(function (entry) {
174
+ if (typeof entry === "string") return { exchange: entry.replace(/\.$/, ""), preference: 0 };
175
+ var ex = entry && entry.exchange;
176
+ var pref = (entry && typeof entry.preference === "number") ? entry.preference : 0;
177
+ return { exchange: String(ex || "").replace(/\.$/, ""), preference: pref };
178
+ }).filter(function (e) { return e.exchange.length > 0; });
179
+ normalized.sort(function (a, b) { return a.preference - b.preference; });
180
+ return normalized.map(function (e) { return e.exchange; });
181
+ }
182
+ var r = await _getDefaultResolver().query(qname, "MX");
183
+ var entries = [];
184
+ for (var i = 0; i < r.rrs.length; i += 1) {
185
+ var rr = r.rrs[i];
186
+ if (rr && rr.type === 15) { // allow:raw-byte-literal — IANA DNS qtype MX
187
+ var d = rr.decoded || {};
188
+ if (d.exchange) {
189
+ entries.push({ exchange: String(d.exchange).replace(/\.$/, ""),
190
+ preference: typeof d.preference === "number" ? d.preference : 0 });
191
+ }
192
+ }
193
+ }
194
+ if (entries.length === 0) {
195
+ var err = new Error("no MX records for " + qname);
196
+ err.code = "ENODATA";
197
+ throw err;
198
+ }
199
+ entries.sort(function (a, b) { return a.preference - b.preference; });
200
+ return entries.map(function (e) { return e.exchange; });
201
+ }
202
+
203
+ async function _safeReverse(ip) {
204
+ // PTR query against the reverse-arpa name. IPv4: a.b.c.d.in-addr.arpa
205
+ // (reversed octets); IPv6: nibble-reversed under ip6.arpa.
206
+ var qname = _ipToReverseArpa(ip);
207
+ if (qname === null) {
208
+ var err = new Error("invalid IP literal: " + ip);
209
+ err.code = "ENOTFOUND";
210
+ throw err;
211
+ }
212
+ var r = await _getDefaultResolver().query(qname, "PTR");
213
+ var out = [];
214
+ for (var i = 0; i < r.rrs.length; i += 1) {
215
+ var rr = r.rrs[i];
216
+ if (rr && rr.type === 12) { // allow:raw-byte-literal — IANA DNS qtype PTR
217
+ // Strip trailing dot if present (PTR rdata is FQDN with root dot).
218
+ var name = String(rr.decoded || "").replace(/\.$/, "");
219
+ if (name.length > 0) out.push(name);
220
+ }
221
+ }
222
+ if (out.length === 0) {
223
+ var e2 = new Error("no PTR records for " + ip);
224
+ e2.code = "ENODATA";
225
+ throw e2;
226
+ }
227
+ return out;
228
+ }
229
+
230
+ function _ipToReverseArpa(ip) {
231
+ if (typeof ip !== "string") return null;
232
+ if (net.isIPv4(ip)) {
233
+ var p = ip.split(".");
234
+ if (p.length !== 4) return null; // allow:raw-byte-literal — IPv4 octet count
235
+ return p[3] + "." + p[2] + "." + p[1] + "." + p[0] + ".in-addr.arpa";
236
+ }
237
+ if (net.isIPv6(ip)) {
238
+ var groups = ipUtils.expandIpv6Groups(ip);
239
+ if (!groups) return null;
240
+ var hex = "";
241
+ for (var i = 0; i < groups.length; i += 1) {
242
+ var s = groups[i].toString(16); // allow:raw-byte-literal — hex radix
243
+ while (s.length < 4) s = "0" + s; // allow:raw-byte-literal — IPv6 group nibble count
244
+ hex += s;
245
+ }
246
+ var rev = hex.split("").reverse().join(".");
247
+ return rev + ".ip6.arpa";
248
+ }
249
+ return null;
250
+ }
251
+
252
+ // ---- Helpers ----
253
+
254
+ function _ipv4ToInt(ip) {
255
+ var parts = ip.split(".");
256
+ if (parts.length !== 4) return null; // allow:raw-byte-literal — IPv4 octet count
257
+ var n = 0;
258
+ for (var i = 0; i < 4; i += 1) { // allow:raw-byte-literal — IPv4 octet count
259
+ var p = parseInt(parts[i], 10);
260
+ if (!isFinite(p) || p < 0 || p > 255) return null; // allow:raw-byte-literal — IPv4 octet range
261
+ n = (n * 256) + p; // allow:raw-byte-literal — IPv4 octet base
262
+ }
263
+ return n;
264
+ }
265
+
266
+ // Expand an IPv6 string (which may carry `::` shorthand) into 8 16-bit
267
+ // groups. Returns null on malformed input.
268
+ function _ipv6Expand(ip) {
269
+ // Compose the shared lib/ip-utils helper so the same IPv6 parse
270
+ // path is shared across mail-auth / mail-rbl / mail-greylist.
271
+ return ipUtils.expandIpv6Groups(ip);
272
+ }
273
+
274
+ function _ipv6InCidr(ip, cidr) {
275
+ var slash = cidr.indexOf("/");
276
+ var net = slash === -1 ? cidr : cidr.slice(0, slash);
277
+ var mask = slash === -1 ? 128 : parseInt(cidr.slice(slash + 1), 10); // allow:raw-byte-literal — IPv6 max prefix
278
+ if (!isFinite(mask) || mask < 0 || mask > 128) return false; // allow:raw-byte-literal — IPv6 max prefix
279
+ var ipGroups = _ipv6Expand(ip);
280
+ var netGroups = _ipv6Expand(net);
281
+ if (!ipGroups || !netGroups) return false;
282
+ if (mask === 0) return true;
283
+ // Compare group-by-group up to the prefix boundary.
284
+ var fullGroups = Math.floor(mask / 16); // allow:raw-byte-literal — bits per group
285
+ var remainBits = mask - fullGroups * 16; // allow:raw-byte-literal — bits per group
286
+ for (var g = 0; g < fullGroups; g += 1) {
287
+ if (ipGroups[g] !== netGroups[g]) return false;
288
+ }
289
+ if (remainBits > 0 && fullGroups < 8) { // allow:raw-byte-literal — IPv6 group count
290
+ var groupMask = (0xffff << (16 - remainBits)) & 0xffff; // allow:raw-byte-literal — bits per group
291
+ if ((ipGroups[fullGroups] & groupMask) !== (netGroups[fullGroups] & groupMask)) return false;
292
+ }
293
+ return true;
294
+ }
295
+
296
+ function _ipv4InCidr(ip, cidr) {
297
+ var slash = cidr.indexOf("/");
298
+ var net = slash === -1 ? cidr : cidr.slice(0, slash);
299
+ var mask = slash === -1 ? 32 : parseInt(cidr.slice(slash + 1), 10); // allow:raw-byte-literal — IPv4 max prefix
300
+ if (mask < 0 || mask > 32) return false; // allow:raw-byte-literal — IPv4 max prefix
301
+ var ipInt = _ipv4ToInt(ip);
302
+ var netInt = _ipv4ToInt(net);
303
+ if (ipInt === null || netInt === null) return false;
304
+ if (mask === 0) return true;
305
+ var bits = 32 - mask; // allow:raw-byte-literal — IPv4 max prefix
306
+ // Use BigInt to avoid 32-bit signed-int wrap.
307
+ var maskInt = (BigInt("0xFFFFFFFF") << BigInt(bits)) & BigInt("0xFFFFFFFF");
308
+ return (BigInt(ipInt) & maskInt) === (BigInt(netInt) & maskInt);
309
+ }
310
+
311
+ // Parse an SPF record into mechanisms.
312
+ function _parseSpfRecord(text) {
313
+ var trimmed = text.trim();
314
+ if (trimmed.indexOf("v=spf1") !== 0) {
315
+ throw new MailAuthError("mail-auth/spf-bad-version",
316
+ "SPF record must start with 'v=spf1', got " +
317
+ JSON.stringify(trimmed.slice(0, C.BYTES.bytes(32))));
318
+ }
319
+ var parts = trimmed.split(/\s+/);
320
+ var mechanisms = [];
321
+ var modifiers = [];
322
+ for (var i = 1; i < parts.length; i += 1) {
323
+ var p = parts[i];
324
+ if (p.length === 0) continue;
325
+ // RFC 7208 §4.6 distinguishes mechanisms (with optional qualifier
326
+ // prefix) from modifiers (name=value, no qualifier; e.g.
327
+ // `redirect=` and `exp=`). Pre-v0.8.32 the framework treated
328
+ // `redirect=` like a mechanism, surfacing a permerror under the
329
+ // generic "out of scope" arm. Handle modifiers separately:
330
+ // redirect= triggers re-evaluation against the target domain;
331
+ // exp= is operator-facing only (we record it).
332
+ var eqAt = p.indexOf("=");
333
+ if (eqAt !== -1 && /^[a-z]+$/i.test(p.slice(0, eqAt))) {
334
+ modifiers.push({ name: p.slice(0, eqAt).toLowerCase(), value: p.slice(eqAt + 1) });
335
+ continue;
336
+ }
337
+ var qualifier = "+";
338
+ if (p.charAt(0) === "+" || p.charAt(0) === "-" ||
339
+ p.charAt(0) === "~" || p.charAt(0) === "?") {
340
+ qualifier = p.charAt(0);
341
+ p = p.slice(1);
342
+ }
343
+ var colonAt = p.indexOf(":");
344
+ var slashAt = p.indexOf("/");
345
+ var sep = (colonAt !== -1 && (slashAt === -1 || colonAt < slashAt))
346
+ ? colonAt : slashAt;
347
+ var mech = sep === -1 ? p : p.slice(0, sep);
348
+ var arg = sep === -1 ? null : p.slice(sep + 1);
349
+ // `raw` preserves the full mechanism+arg token after qualifier-
350
+ // strip. The a/mx dispatch arm reparses this directly because
351
+ // RFC 7208 §5.3/§5.4 allow `dual-cidr-length` after the optional
352
+ // domain-spec (e.g. `a:example.com/24//64`); the simple `arg`
353
+ // field above splits on the first separator and loses the
354
+ // information about whether that separator was `:` or `/`.
355
+ mechanisms.push({ qualifier: qualifier, mechanism: mech.toLowerCase(), arg: arg, raw: p });
356
+ }
357
+ // Surface modifiers via a non-enumerable property so callers that
358
+ // don't expect them don't see them in JSON-serialized records but
359
+ // _spfEvaluateDomain can react.
360
+ Object.defineProperty(mechanisms, "modifiers", { value: modifiers });
361
+ return mechanisms;
362
+ }
363
+
364
+ // Fetch the SPF TXT record for a domain. Returns:
365
+ // { kind: "found", record: "<text>" } — exactly one v=spf1 record
366
+ // { kind: "none" } — zero v=spf1 records
367
+ // { kind: "permerror", reason: "<msg>" } — multiple v=spf1 records
368
+ // (RFC 7208 §4.5 — domain
369
+ // MUST publish at most one)
370
+ async function _fetchSpfRecord(domain, dnsLookup) {
371
+ var records;
372
+ try {
373
+ records = await _safeResolveTxt(domain, dnsLookup);
374
+ } catch (e) {
375
+ if (e && (e.code === "ENOTFOUND" || e.code === "ENODATA")) return { kind: "none" };
376
+ throw new MailAuthError("mail-auth/spf-lookup-failed",
377
+ "SPF TXT lookup for " + domain + " failed: " +
378
+ ((e && e.message) || String(e)));
379
+ }
380
+ if (!Array.isArray(records)) return { kind: "none" };
381
+ var matches = [];
382
+ for (var i = 0; i < records.length; i += 1) {
383
+ var rec = Array.isArray(records[i]) ? records[i].join("") : records[i];
384
+ if (typeof rec === "string" && rec.indexOf("v=spf1") === 0) matches.push(rec);
385
+ }
386
+ if (matches.length === 0) return { kind: "none" };
387
+ if (matches.length > 1) {
388
+ return { kind: "permerror",
389
+ reason: "domain " + domain + " publishes " + matches.length +
390
+ " v=spf1 records; RFC 7208 §4.5 requires at most one" };
391
+ }
392
+ // RFC 7208 §3.3 — the SPF record (concatenated across multi-string
393
+ // TXT chunks) MUST NOT exceed 450 bytes. Receivers MUST refuse
394
+ // larger records (permerror) so a malformed-large policy can't
395
+ // amplify parser work.
396
+ if (matches[0].length > SPF_RECORD_MAX_BYTES) {
397
+ return { kind: "permerror",
398
+ reason: "domain " + domain + " SPF record is " + matches[0].length +
399
+ " bytes; RFC 7208 §3.3 caps at " + SPF_RECORD_MAX_BYTES };
400
+ }
401
+ return { kind: "found", record: matches[0] };
402
+ }
403
+
404
+ // RFC 7208 §5.3 / §5.4 — `a [ ":" domain-spec ] [ dual-cidr-length ]`
405
+ // and `mx [ ":" domain-spec ] [ dual-cidr-length ]`. dual-cidr-length
406
+ // is `[ "/" ip4-cidr ] [ "//" ip6-cidr ]`. Returns the parsed target
407
+ // domain plus per-family prefix lengths (32 / 128 when omitted).
408
+ //
409
+ // `raw` is the post-qualifier token (e.g. "a", "a:foo.com", "a/24",
410
+ // "a//64", "a:foo.com/24//64"). Throws MailAuthError on bad cidr.
411
+ function _parseADualCidr(raw, mech, defaultDomain) {
412
+ var rest = raw.slice(mech.length);
413
+ var domain = defaultDomain;
414
+ var v4Mask = 32; // allow:raw-byte-literal — IPv4 max prefix
415
+ var v6Mask = 128; // allow:raw-byte-literal — IPv6 max prefix
416
+
417
+ if (rest.charAt(0) === ":") {
418
+ rest = rest.slice(1);
419
+ var slashAt = rest.indexOf("/");
420
+ if (slashAt === -1) { domain = rest; rest = ""; }
421
+ else { domain = rest.slice(0, slashAt); rest = rest.slice(slashAt); }
422
+ }
423
+
424
+ if (rest.length > 0) {
425
+ // rest is now "" | "/v4" | "//v6" | "/v4//v6".
426
+ var dblSlash = rest.indexOf("//");
427
+ var v4Part = "";
428
+ var v6Part = "";
429
+ if (dblSlash !== -1) {
430
+ v4Part = rest.slice(0, dblSlash); // "" or "/24"
431
+ v6Part = rest.slice(dblSlash + 2); // "64"
432
+ } else {
433
+ v4Part = rest; // "/24"
434
+ }
435
+ if (v4Part.length > 0) {
436
+ if (v4Part.charAt(0) !== "/") {
437
+ throw new MailAuthError("mail-auth/spf-bad-cidr",
438
+ "SPF " + mech + " dual-cidr malformed: " + JSON.stringify(raw));
439
+ }
440
+ var v4Str = v4Part.slice(1);
441
+ // RFC 7208 §5.3 / §5.4 — `ip4-cidr-length = "/" 1*DIGIT`. An
442
+ // empty digit segment (`a/`, `mx/`) is malformed grammar; the
443
+ // receiver MUST permerror. Pre-fix this silently kept the
444
+ // default /32 and would authorize the connecting IP under any
445
+ // A record of the target, which can over-authorize senders
446
+ // publishing `v=spf1 a/ -all` (would match every IP in the
447
+ // /32 of every A record).
448
+ if (v4Str.length === 0) {
449
+ throw new MailAuthError("mail-auth/spf-bad-cidr",
450
+ "SPF " + mech + " v4 cidr-length is empty (RFC 7208 §5.3/§5.4 grammar requires 1*DIGIT): " +
451
+ JSON.stringify(raw));
452
+ }
453
+ var v4n = parseInt(v4Str, 10);
454
+ if (!isFinite(v4n) || v4n < 0 || v4n > 32 || String(v4n) !== v4Str) { // allow:raw-byte-literal — IPv4 max prefix
455
+ throw new MailAuthError("mail-auth/spf-bad-cidr",
456
+ "SPF " + mech + " v4 cidr-length invalid: " + JSON.stringify(raw));
457
+ }
458
+ v4Mask = v4n;
459
+ }
460
+ // RFC 7208 §5.3 / §5.4 — `ip6-cidr-length = "/" 1*DIGIT` (after
461
+ // the "//" separator). When the `//` separator IS present (i.e.
462
+ // the raw token contained `//`) the digit segment MUST be 1*DIGIT.
463
+ // Empty (`a//`, `a/24//`, `mx//`) is malformed grammar; permerror.
464
+ if (dblSlash !== -1) {
465
+ if (v6Part.length === 0) {
466
+ throw new MailAuthError("mail-auth/spf-bad-cidr",
467
+ "SPF " + mech + " v6 cidr-length is empty (RFC 7208 §5.3/§5.4 grammar requires 1*DIGIT): " +
468
+ JSON.stringify(raw));
469
+ }
470
+ var v6n = parseInt(v6Part, 10);
471
+ if (!isFinite(v6n) || v6n < 0 || v6n > 128 || String(v6n) !== v6Part) { // allow:raw-byte-literal — IPv6 max prefix
472
+ throw new MailAuthError("mail-auth/spf-bad-cidr",
473
+ "SPF " + mech + " v6 cidr-length invalid: " + JSON.stringify(raw));
474
+ }
475
+ v6Mask = v6n;
476
+ }
477
+ }
478
+
479
+ if (!domain || domain.length === 0) {
480
+ throw new MailAuthError("mail-auth/spf-bad-cidr",
481
+ "SPF " + mech + " has no target domain (current-domain unavailable)");
482
+ }
483
+ return { domain: domain.toLowerCase(), v4Mask: v4Mask, v6Mask: v6Mask };
484
+ }
485
+
486
+ // RFC 7208 §5.3 / §5.4 — `a` and `mx` mechanism evaluation. Both
487
+ // resolve the target domain (or the current SPF-evaluating domain when
488
+ // arg omitted) to a set of IP addresses; the connecting IP matches if
489
+ // it falls inside any of those addresses under the parsed cidr prefix.
490
+ //
491
+ // Lookup accounting per §4.6.4:
492
+ // - `a`: the outer evaluator has already counted this as one DNS-
493
+ // touching mechanism. The single A/AAAA query is THAT one
494
+ // lookup; no additional increment here.
495
+ // - `mx`: the outer evaluator has counted the MX query itself.
496
+ // EACH MX hostname's A/AAAA expansion adds an additional
497
+ // lookup; total expansion is capped at 10 MX hostnames per
498
+ // §4.6.4 (the explicit "MX limit"). Crossing the global
499
+ // 10-lookup ceiling at any expansion step permerrors.
500
+ //
501
+ // Returns one of:
502
+ // { match: true } — connecting IP matched
503
+ // { match: false } — no IP matched / record absent
504
+ // { error: "temperror", reason: "..." } — transient DNS failure
505
+ // { error: "permerror", reason: "..." } — over-limit / bad CIDR / bad MX count
506
+ async function _spfMatchAMx(mech, raw, ip, isIpv6, defaultDomain, dnsLookup, lookups) {
507
+ var parsed;
508
+ try { parsed = _parseADualCidr(raw, mech, defaultDomain); }
509
+ catch (e) { return { error: "permerror", reason: e.message }; }
510
+
511
+ var mask = isIpv6 ? parsed.v6Mask : parsed.v4Mask;
512
+ var family = isIpv6 ? 6 : 4; // allow:raw-byte-literal — IP family marker
513
+
514
+ var targetIps = [];
515
+ if (mech === "a") {
516
+ try { targetIps = await _safeResolveA(parsed.domain, family, dnsLookup); }
517
+ catch (e) {
518
+ var code = e && e.code;
519
+ if (code === "ENOTFOUND" || code === "ENODATA") return { match: false };
520
+ return { error: "temperror",
521
+ reason: "SPF a:" + parsed.domain + " lookup failed: " +
522
+ ((e && e.message) || String(e)) };
523
+ }
524
+ } else { // mech === "mx"
525
+ var mxHosts;
526
+ try { mxHosts = await _safeResolveMx(parsed.domain, dnsLookup); }
527
+ catch (e) {
528
+ var mcode = e && e.code;
529
+ if (mcode === "ENOTFOUND" || mcode === "ENODATA") return { match: false };
530
+ return { error: "temperror",
531
+ reason: "SPF mx:" + parsed.domain + " MX lookup failed: " +
532
+ ((e && e.message) || String(e)) };
533
+ }
534
+ // RFC 7208 §4.6.4 — the MX expansion is capped at 10 hostnames.
535
+ // Crossing this is a permerror; receivers MUST NOT silently
536
+ // truncate, since a misconfigured sender publishing 20 MX hosts
537
+ // would otherwise have only the first 10 contribute to authz.
538
+ if (mxHosts.length > 10) { // allow:raw-byte-literal — RFC 7208 §4.6.4 MX limit
539
+ return { error: "permerror",
540
+ reason: "SPF mx:" + parsed.domain + " resolved " + mxHosts.length +
541
+ " MX hosts (RFC 7208 §4.6.4 caps at 10)" };
542
+ }
543
+ for (var mi = 0; mi < mxHosts.length; mi += 1) {
544
+ lookups.count += 1;
545
+ if (lookups.count > lookups.limit) {
546
+ return { error: "permerror",
547
+ reason: "DNS lookup limit exceeded (RFC 7208 §4.6.4) during mx:" +
548
+ parsed.domain + " expansion" };
549
+ }
550
+ try {
551
+ var hostIps = await _safeResolveA(mxHosts[mi], family, dnsLookup);
552
+ for (var hi = 0; hi < hostIps.length; hi += 1) targetIps.push(hostIps[hi]);
553
+ } catch (e) {
554
+ var hcode = e && e.code;
555
+ if (hcode === "ENOTFOUND" || hcode === "ENODATA") {
556
+ // Void lookup — counts toward §4.6.4 ceiling for the MX
557
+ // expansion (the MX hostname has no A/AAAA in the relevant
558
+ // family). Some hosts are v4-only and won't have AAAA; we
559
+ // skip the host but charge the void slot.
560
+ lookups.void = (lookups.void || 0) + 1;
561
+ if (lookups.void > SPF_VOID_LOOKUP_LIMIT) {
562
+ return { error: "permerror",
563
+ reason: "SPF void-lookup limit exceeded (RFC 7208 §4.6.4) during mx expansion" };
564
+ }
565
+ continue;
566
+ }
567
+ return { error: "temperror",
568
+ reason: "SPF mx host " + mxHosts[mi] + " A/AAAA lookup failed: " +
569
+ ((e && e.message) || String(e)) };
570
+ }
571
+ }
572
+ }
573
+
574
+ for (var ti = 0; ti < targetIps.length; ti += 1) {
575
+ var cidr = targetIps[ti] + "/" + mask;
576
+ if (isIpv6) { if (_ipv6InCidr(ip, cidr)) return { match: true }; }
577
+ else { if (_ipv4InCidr(ip, cidr)) return { match: true }; }
578
+ }
579
+ return { match: false };
580
+ }
581
+
582
+ // SPF verify — recursive include resolution + ip4 / ip6 / a / mx /
583
+ // include / all / redirect=. The `exists` and `ptr` mechanisms +
584
+ // macro-string expansion remain deferred (see the mechanism dispatch
585
+ // arm for the Re-open condition + operator escape hatch).
586
+ async function spfVerify(opts) {
587
+ opts = opts || {};
588
+ validateOpts(opts, ["ip", "mailFrom", "helo", "dnsLookup"], "mail.spf.verify");
589
+ if (typeof opts.ip !== "string") {
590
+ throw new MailAuthError("mail-auth/spf-bad-ip",
591
+ "spf.verify: ip must be a string");
592
+ }
593
+ var domain = opts.mailFrom
594
+ ? String(opts.mailFrom).split("@")[1]
595
+ : opts.helo;
596
+ if (typeof domain !== "string" || domain.length === 0) {
597
+ throw new MailAuthError("mail-auth/spf-bad-domain",
598
+ "spf.verify: mailFrom or helo is required");
599
+ }
600
+
601
+ var lookups = { count: 0, limit: SPF_DNS_LOOKUP_LIMIT, void: 0 };
602
+ // RFC 7208 §4.6.4 — the initial query for the sender domain's SPF
603
+ // record itself does NOT count toward the 10-lookup limit. Only
604
+ // include / a / mx / ptr / exists / redirect mechanisms count.
605
+ // Pre-v0.8.17 this was off-by-one — senders at the spec ceiling
606
+ // got false permerror.
607
+ var result = await _spfEvaluateDomain(domain.toLowerCase(), opts.ip,
608
+ opts.dnsLookup, lookups,
609
+ { isInitial: true });
610
+ return {
611
+ result: result.verdict, // pass | fail | softfail | neutral | none | temperror | permerror
612
+ domain: domain,
613
+ explanation: result.explanation,
614
+ lookupCount: lookups.count,
615
+ };
616
+ }
617
+
618
+ async function _spfEvaluateDomain(domain, ip, dnsLookup, lookups, ctx) {
619
+ ctx = ctx || {};
620
+ if (lookups.count > lookups.limit) {
621
+ return { verdict: "permerror", explanation: "DNS lookup limit exceeded (RFC 7208 §4.6.4)" };
622
+ }
623
+ // RFC 7208 §4.6.4 — void-lookup ceiling. Each successful query that
624
+ // returns 0 records (NXDOMAIN, no-data) counts. Beyond 2, permerror.
625
+ if ((lookups.void || 0) > SPF_VOID_LOOKUP_LIMIT) {
626
+ return { verdict: "permerror",
627
+ explanation: "SPF void-lookup limit exceeded (RFC 7208 §4.6.4)" };
628
+ }
629
+ // RFC 7208 §6.1 — redirect= recursion bound. Per-evaluation
630
+ // re-entries via redirect MUST terminate. The lookup limit also
631
+ // catches pathological chains; this bound is the belt-and-braces.
632
+ if ((ctx.redirectDepth || 0) > SPF_REDIRECT_DEPTH_LIMIT) {
633
+ return { verdict: "permerror",
634
+ explanation: "SPF redirect= recursion limit exceeded (RFC 7208 §6.1)" };
635
+ }
636
+ // Initial query for the sender's SPF record doesn't count (RFC 7208
637
+ // §4.6.4); only include / a / mx / ptr / exists / redirect do.
638
+ if (!ctx.isInitial) lookups.count += 1;
639
+
640
+ var fetched;
641
+ try { fetched = await _fetchSpfRecord(domain, dnsLookup); }
642
+ catch (e) {
643
+ return { verdict: "temperror", explanation: e.message };
644
+ }
645
+ if (fetched.kind === "permerror") {
646
+ return { verdict: "permerror", explanation: fetched.reason };
647
+ }
648
+ if (fetched.kind === "none") {
649
+ // Void lookup — count toward §4.6.4 ceiling. Initial query
650
+ // doesn't count as a "lookup" but DOES count as void if the
651
+ // sender has no SPF (mirrors the spec's intent: a misconfigured
652
+ // sender that publishes no record still consumes a slot).
653
+ lookups.void = (lookups.void || 0) + 1;
654
+ return { verdict: "none", explanation: "no SPF record at " + domain };
655
+ }
656
+
657
+ var mechanisms;
658
+ try { mechanisms = _parseSpfRecord(fetched.record); }
659
+ catch (e) {
660
+ return { verdict: "permerror", explanation: e.message };
661
+ }
662
+
663
+ var isIpv6 = ip.indexOf(":") !== -1;
664
+ for (var i = 0; i < mechanisms.length; i += 1) {
665
+ var m = mechanisms[i];
666
+ var match = false;
667
+ if (m.mechanism === "all") match = true;
668
+ else if (!isIpv6 && (m.mechanism === "ip4" || m.mechanism === "ipv4")) {
669
+ if (m.arg && _ipv4InCidr(ip, m.arg)) match = true;
670
+ } else if (isIpv6 && (m.mechanism === "ip6" || m.mechanism === "ipv6")) {
671
+ if (m.arg && _ipv6InCidr(ip, m.arg)) match = true;
672
+ } else if (m.mechanism === "include") {
673
+ if (!m.arg) continue;
674
+ var inner = await _spfEvaluateDomain(m.arg.toLowerCase(), ip, dnsLookup, lookups);
675
+ if (inner.verdict === "pass") match = true;
676
+ else if (inner.verdict === "permerror" || inner.verdict === "temperror") {
677
+ return inner;
678
+ }
679
+ // RFC 7208 §5.2 — when the included domain has no SPF record at
680
+ // all, the include itself MUST permerror (the included policy is
681
+ // missing, the operator's intent is unverifiable). Without this
682
+ // check `include:gone-domain.example` silently authorizes whatever
683
+ // mechanism follows, including `+all`.
684
+ else if (inner.verdict === "none") {
685
+ return { verdict: "permerror",
686
+ explanation: "include:" + m.arg + " has no SPF record (RFC 7208 §5.2)" };
687
+ }
688
+ } else if (m.mechanism === "a" || m.mechanism === "mx") {
689
+ // RFC 7208 §5.3 / §5.4. The mechanism itself counts as one DNS
690
+ // lookup per §4.6.4 (already incremented by the outer loop's
691
+ // `lookups.count += 1` for non-initial domains; ip4/ip6/all are
692
+ // overcounted as a result, but only by mechanisms whose lookup
693
+ // budget the spec doesn't care about — they're not DNS-touching).
694
+ // The `a` / `mx` arms additionally expand per RFC §4.6.4 (each
695
+ // MX hostname adds another lookup); the helper handles that
696
+ // accounting.
697
+ lookups.count += 1;
698
+ if (lookups.count > lookups.limit) {
699
+ return { verdict: "permerror",
700
+ explanation: "DNS lookup limit exceeded (RFC 7208 §4.6.4) at " +
701
+ m.mechanism };
702
+ }
703
+ var amRes = await _spfMatchAMx(m.mechanism, m.raw, ip, isIpv6,
704
+ domain, dnsLookup, lookups);
705
+ if (amRes.error === "permerror") {
706
+ return { verdict: "permerror", explanation: amRes.reason };
707
+ }
708
+ if (amRes.error === "temperror") {
709
+ return { verdict: "temperror", explanation: amRes.reason };
710
+ }
711
+ if (amRes.match) match = true;
712
+ } else if (m.mechanism === "exists" || m.mechanism === "ptr") {
713
+ // RFC 7208 §5.7 (exists) + §5.5 (ptr) — deferred from v0.11.3.
714
+ //
715
+ // exists: requires macro-string expansion (RFC 7208 §7) to be
716
+ // useful in practice; almost every published `exists:` policy
717
+ // uses macros like `exists:%{l}.%{d}._spf.example.com` to do
718
+ // per-recipient or per-IP lookups. A non-macro `exists:` is
719
+ // technically valid but vanishingly rare in published policies.
720
+ //
721
+ // ptr: RFC 7208 §5.5 explicitly says "use of this mechanism
722
+ // is strongly discouraged" — the receiver does reverse-DNS +
723
+ // forward-confirm per query, doubling DNS load and tying the
724
+ // sender's authz to whoever controls their PTR zone. Despite
725
+ // this discouragement, a small minority of legacy senders
726
+ // still publish `+ptr -all` policies as their only SPF stance.
727
+ //
728
+ // Re-open conditions:
729
+ // - exists: macro-string expansion lands in the framework (a
730
+ // standalone slice; tracked under blamejs-roadmap.md), OR an
731
+ // operator surfaces a real `exists:` policy without macros
732
+ // and asks for the simple A-existence form.
733
+ // - ptr: an operator surfaces a legitimate sender whose
734
+ // ONLY SPF stance is `ptr` and needs the framework to
735
+ // evaluate it (rather than the operator's MTA already doing
736
+ // iprev via `b.mail.auth.iprev`).
737
+ //
738
+ // Operator escape hatch today:
739
+ // - exists: senders almost universally have a non-`exists:`
740
+ // mechanism alongside; the framework returns "permerror"
741
+ // here, surfacing the gap, but legitimate mail flow that
742
+ // ALSO carries a passing ip4/ip6/include path is unaffected.
743
+ // - ptr: operators evaluating a ptr-only sender wire
744
+ // `b.mail.auth.iprev(ip)` and treat fcrdns=true the same as
745
+ // SPF pass for that domain.
746
+ return {
747
+ verdict: "permerror",
748
+ explanation: "SPF mechanism '" + m.mechanism + "' is not yet implemented (RFC 7208 §" +
749
+ (m.mechanism === "exists" ? "5.7 + §7 macros" : "5.5") +
750
+ "); senders typically publish ip4 / ip6 / a / mx / include alongside",
751
+ };
752
+ }
753
+ if (match) {
754
+ var qualifier = m.qualifier;
755
+ var verdict = qualifier === "+" ? "pass" :
756
+ qualifier === "-" ? "fail" :
757
+ qualifier === "~" ? "softfail" :
758
+ qualifier === "?" ? "neutral" : "neutral";
759
+ return { verdict: verdict, explanation: "matched " + m.mechanism +
760
+ (m.arg ? ":" + m.arg : "") };
761
+ }
762
+ }
763
+
764
+ // RFC 7208 §6.1 — `redirect=<domain>` modifier: when no mechanism
765
+ // matched, fall through to the target domain's policy. The redirect
766
+ // is ignored if an `all` mechanism is present (since `all` matches
767
+ // unconditionally, the redirect is unreachable by construction).
768
+ // Pre-this-patch the redirect= modifier was silently dropped — a
769
+ // domain whose only policy was `v=spf1 redirect=_spf.example.com`
770
+ // returned "neutral" instead of the redirected verdict, leaving
771
+ // every legitimate sender unauthenticated.
772
+ var mods = mechanisms.modifiers || [];
773
+ for (var rmi = 0; rmi < mods.length; rmi += 1) {
774
+ if (mods[rmi].name === "redirect" && mods[rmi].value) {
775
+ // Redirect counts as one DNS-mechanism per §4.6.4.
776
+ var redirected = await _spfEvaluateDomain(
777
+ mods[rmi].value.toLowerCase(), ip, dnsLookup, lookups,
778
+ { redirectDepth: (ctx.redirectDepth || 0) + 1 });
779
+ // RFC 7208 §6.1 — if the redirect target has no SPF record,
780
+ // permerror (the operator's intent is unverifiable).
781
+ if (redirected.verdict === "none") {
782
+ return { verdict: "permerror",
783
+ explanation: "redirect=" + mods[rmi].value +
784
+ " has no SPF record (RFC 7208 §6.1)" };
785
+ }
786
+ return redirected;
787
+ }
788
+ }
789
+
790
+ return { verdict: "neutral", explanation: "no mechanism matched" };
791
+ }
792
+
793
+ // ---- DMARC (RFC 7489) ----
794
+
795
+ async function _fetchDmarcRecord(domain, dnsLookup) {
796
+ var qname = "_dmarc." + domain.toLowerCase();
797
+ var records;
798
+ try {
799
+ records = await _safeResolveTxt(qname, dnsLookup);
800
+ } catch (e) {
801
+ if (e && (e.code === "ENOTFOUND" || e.code === "ENODATA")) return null;
802
+ throw new MailAuthError("mail-auth/dmarc-lookup-failed",
803
+ "DMARC TXT lookup for " + qname + " failed: " +
804
+ ((e && e.message) || String(e)));
805
+ }
806
+ if (!Array.isArray(records)) return null;
807
+ var matches = [];
808
+ for (var i = 0; i < records.length; i += 1) {
809
+ var rec = Array.isArray(records[i]) ? records[i].join("") : records[i];
810
+ if (typeof rec === "string" && rec.indexOf("v=DMARC1") === 0) matches.push(rec);
811
+ }
812
+ if (matches.length === 0) return null;
813
+ // RFC 7489 §6.6.3 — when multiple v=DMARC1 records are published,
814
+ // the receiver MUST treat the domain as having no DMARC record.
815
+ if (matches.length > 1) return null;
816
+ return matches[0];
817
+ }
818
+
819
+ // RFC 7489 base policy keys + DMARCbis (draft-ietf-dmarc-dmarcbis)
820
+ // extensions:
821
+ // np=<none|quarantine|reject> policy for non-existent subdomains
822
+ // psd=<y|n|u> applies-at-public-suffix-domain (TLD
823
+ // operator publishes a DMARC record on
824
+ // the suffix itself)
825
+ // Validation tier: parse is config-time (operator-supplied DNS bytes);
826
+ // throw on malformed v= / unrecognized np= or psd= values rather than
827
+ // silently dropping — operators with a typo'd record otherwise see the
828
+ // fallback policy applied without warning.
829
+ var DMARCBIS_VALID_NP = { none: 1, quarantine: 1, reject: 1 };
830
+ var DMARCBIS_VALID_PSD = { y: 1, n: 1, u: 1 };
831
+
832
+ function _parseDmarcRecord(text) {
833
+ var policy = { v: null, p: null, sp: null, np: null, psd: null,
834
+ pct: 100, adkim: "r", aspf: "r" }; // allow:raw-byte-literal — RFC 7489 default pct
835
+ var pairs = text.split(";"); // allow:bare-split-on-quoted-header — RFC 7489 §6.4 DMARC tag-list grammar: `tag-spec *( ";" tag-spec )` with tag-value = 0*( tval *( WSP / FWS ) ); NO quoted-string allowed
836
+ for (var i = 0; i < pairs.length; i += 1) {
837
+ var kv = pairs[i].trim();
838
+ if (kv.length === 0) continue;
839
+ var eq = kv.indexOf("=");
840
+ if (eq === -1) continue;
841
+ var key = kv.slice(0, eq).trim().toLowerCase();
842
+ var val = kv.slice(eq + 1).trim();
843
+ if (key === "v") policy.v = val;
844
+ else if (key === "p") policy.p = val.toLowerCase();
845
+ else if (key === "sp") policy.sp = val.toLowerCase();
846
+ else if (key === "pct") policy.pct = parseInt(val, 10);
847
+ else if (key === "adkim") policy.adkim = val.toLowerCase();
848
+ else if (key === "aspf") policy.aspf = val.toLowerCase();
849
+ else if (key === "np") {
850
+ var npVal = val.toLowerCase();
851
+ if (!DMARCBIS_VALID_NP[npVal]) {
852
+ throw new MailAuthError("mail-auth/dmarcbis-bad-tag",
853
+ "DMARC np= must be one of none|quarantine|reject, got " + JSON.stringify(val));
854
+ }
855
+ policy.np = npVal;
856
+ }
857
+ else if (key === "psd") {
858
+ var psdVal = val.toLowerCase();
859
+ if (!DMARCBIS_VALID_PSD[psdVal]) {
860
+ throw new MailAuthError("mail-auth/dmarcbis-bad-tag",
861
+ "DMARC psd= must be one of y|n|u, got " + JSON.stringify(val));
862
+ }
863
+ policy.psd = psdVal;
864
+ }
865
+ }
866
+ if (policy.v !== "DMARC1") {
867
+ throw new MailAuthError("mail-auth/dmarc-bad-version",
868
+ "DMARC record version must be DMARC1, got " + JSON.stringify(policy.v));
869
+ }
870
+ return policy;
871
+ }
872
+
873
+ function _alignmentCheck(fromDomain, authDomain, mode) {
874
+ if (!fromDomain || !authDomain) return false;
875
+ var f = fromDomain.toLowerCase();
876
+ var a = authDomain.toLowerCase();
877
+ if (mode === "s") return f === a; // strict
878
+ // RFC 7489 §3.1.1 + DMARCbis §4.4 — relaxed alignment compares the
879
+ // organizational domain (the public-suffix-tail registered name).
880
+ // Earlier shape did a naive `endsWith` text-suffix check which over-
881
+ // approximated alignment: `evil-bank.com` and `bank.com` looked
882
+ // aligned even though they're separately registered. PSL lookup
883
+ // closes the gap.
884
+ if (f === a) return true;
885
+ var fOrg = null;
886
+ var aOrg = null;
887
+ try { fOrg = publicSuffix.organizationalDomain(f); } catch (_e) { fOrg = null; }
888
+ try { aOrg = publicSuffix.organizationalDomain(a); } catch (_e) { aOrg = null; }
889
+ if (fOrg && aOrg && fOrg === aOrg) return true;
890
+ return false;
891
+ }
892
+
893
+ async function dmarcEvaluate(opts) {
894
+ opts = opts || {};
895
+ validateOpts(opts, ["from", "spf", "dkim", "dnsLookup", "domainExists",
896
+ "pctSampleKey"],
897
+ "mail.dmarc.evaluate");
898
+ if (typeof opts.from !== "string") {
899
+ throw new MailAuthError("mail-auth/dmarc-bad-from",
900
+ "dmarc.evaluate: opts.from must be the From-header email address");
901
+ }
902
+ var fromDomain = opts.from.split("@")[1];
903
+ if (!fromDomain) {
904
+ throw new MailAuthError("mail-auth/dmarc-bad-from",
905
+ "dmarc.evaluate: opts.from is missing the @domain part");
906
+ }
907
+ fromDomain = fromDomain.toLowerCase();
908
+
909
+ // DMARCbis (draft-ietf-dmarc-dmarcbis) replaces the legacy "drop one
910
+ // label" org-domain heuristic with a proper Public Suffix List lookup.
911
+ // organizationalDomain returns null when the input IS a public suffix
912
+ // (e.g. "co.uk") OR when no PSL match resolves; either way, the
913
+ // org-domain walk below short-circuits.
914
+ var orgDomain = null;
915
+ try { orgDomain = publicSuffix.organizationalDomain(fromDomain); }
916
+ catch (_e) { orgDomain = null; }
917
+
918
+ var policy = null;
919
+ var policyOriginDomain = null;
920
+ var orgDomainPolicyApplied = false;
921
+ var psdPolicyApplied = false;
922
+ try {
923
+ var rec = await _fetchDmarcRecord(fromDomain, opts.dnsLookup);
924
+ if (rec) {
925
+ policy = _parseDmarcRecord(rec);
926
+ policyOriginDomain = fromDomain;
927
+ } else if (orgDomain && orgDomain !== fromDomain) {
928
+ // RFC 7489 §6.6.3 + DMARCbis §4.6 — fall through to organizational
929
+ // domain. When the org-domain record sets sp= it applies to this
930
+ // subdomain; otherwise p= is the operative policy.
931
+ var orgRec = await _fetchDmarcRecord(orgDomain, opts.dnsLookup);
932
+ if (orgRec) {
933
+ var orgPolicy = _parseDmarcRecord(orgRec);
934
+ orgPolicy.p = orgPolicy.sp || orgPolicy.p;
935
+ policy = orgPolicy;
936
+ policyOriginDomain = orgDomain;
937
+ orgDomainPolicyApplied = true;
938
+ }
939
+ }
940
+
941
+ // DMARCbis §4.7 — when the org-domain record carries `psd=y`, OR
942
+ // the published record sits at the public suffix itself (TLD
943
+ // operator), the receiver continues lookup at the public suffix
944
+ // for downstream DSP cooperation. We honor the `psd=y` opt-in by
945
+ // surfacing the tag so operators can route on it; the explicit
946
+ // suffix walk below covers the suffix-record case.
947
+ if (!policy) {
948
+ var suffix = null;
949
+ try { suffix = publicSuffix.publicSuffix(fromDomain); }
950
+ catch (_e) { suffix = null; }
951
+ if (suffix && suffix !== fromDomain && suffix !== orgDomain) {
952
+ var psdRec = await _fetchDmarcRecord(suffix, opts.dnsLookup);
953
+ if (psdRec) {
954
+ var psdPolicy = _parseDmarcRecord(psdRec);
955
+ if (psdPolicy.psd === "y") {
956
+ psdPolicy.p = psdPolicy.sp || psdPolicy.p;
957
+ policy = psdPolicy;
958
+ policyOriginDomain = suffix;
959
+ psdPolicyApplied = true;
960
+ }
961
+ }
962
+ }
963
+ }
964
+ } catch (e) {
965
+ return { result: "temperror", explanation: e.message,
966
+ policy: null, alignment: { spf: false, dkim: false },
967
+ orgDomain: orgDomain };
968
+ }
969
+ if (!policy) {
970
+ return { result: "none", explanation: "no DMARC record at _dmarc." + fromDomain,
971
+ policy: null, alignment: { spf: false, dkim: false },
972
+ orgDomain: orgDomain };
973
+ }
974
+
975
+ // DMARCbis §4.8 — non-existent subdomain (NXDOMAIN on MX/A/AAAA for
976
+ // the message-from domain) gets the np= policy when published. The
977
+ // operator wires the existence check via opts.domainExists; absent
978
+ // that callback we conservatively treat the domain as existing
979
+ // (the np= path is opt-in observability, not a downgrade gate).
980
+ var npApplied = false;
981
+ if (typeof policy.np === "string" && typeof opts.domainExists === "function" &&
982
+ orgDomainPolicyApplied) {
983
+ var exists = true;
984
+ try { exists = await opts.domainExists(fromDomain); }
985
+ catch (_e) { exists = true; }
986
+ if (exists === false) {
987
+ policy = Object.assign({}, policy, { p: policy.np });
988
+ npApplied = true;
989
+ }
990
+ }
991
+
992
+ var spfDomain = (opts.spf && opts.spf.domain) || null;
993
+ var dkimResults = Array.isArray(opts.dkim) ? opts.dkim : (opts.dkim ? [opts.dkim] : []);
994
+
995
+ var spfAligned = opts.spf && opts.spf.result === "pass" &&
996
+ _alignmentCheck(fromDomain, spfDomain, policy.aspf);
997
+ var dkimAligned = false;
998
+ for (var i = 0; i < dkimResults.length; i += 1) {
999
+ var d = dkimResults[i];
1000
+ if (d && d.result === "pass" &&
1001
+ _alignmentCheck(fromDomain, d.d || d.domain, policy.adkim)) {
1002
+ dkimAligned = true;
1003
+ break;
1004
+ }
1005
+ }
1006
+
1007
+ var pass = spfAligned || dkimAligned;
1008
+ // RFC 7489 §6.6.4 — pct= MUST be consulted when the disposition is
1009
+ // not "deliver". When pct is < 100 the receiver applies the policy
1010
+ // to that fraction of failing messages and the rest gets the next-
1011
+ // less-strict disposition (reject → quarantine; quarantine → none).
1012
+ //
1013
+ // Sampling determinism: a single message MUST receive the same
1014
+ // sampled/not-sampled verdict across retries. `Math.random()` re-
1015
+ // rolls per-call so the receiver's first attempt could deliver
1016
+ // (sampled=true → quarantine→none) while a retry rejected — leading
1017
+ // to inconsistent disposition for the same SMTP envelope. Derive
1018
+ // the sample roll from a stable per-message key (operator-supplied
1019
+ // `pctSampleKey` — typically the Message-ID + From-domain + a
1020
+ // receiver-side secret) hashed via SHAKE256, mapped to [0,100). When
1021
+ // the operator doesn't supply a key we fall back to a per-call
1022
+ // crypto.randomInt — still cryptographically uniform, just not
1023
+ // retry-stable. The fallback is the framework's hardening floor
1024
+ // (replaces Math.random); retry-stability requires the operator to
1025
+ // wire a key.
1026
+ var pctRaw = parseInt(policy.pct, 10); // allow:raw-byte-literal — pct percentage, not bytes
1027
+ var pct = isFinite(pctRaw) && pctRaw >= 0 && pctRaw <= 100 ? pctRaw : 100; // allow:raw-byte-literal — pct percentage, not bytes
1028
+ var sampleRoll;
1029
+ if (typeof opts.pctSampleKey === "string" && opts.pctSampleKey.length > 0) {
1030
+ // Deterministic per-message sample roll. SHAKE256 → first 4 bytes
1031
+ // → uint32 → modulo 100. 4 bytes is far in excess of the
1032
+ // information needed for 0..99 and uniform mapping is fine.
1033
+ var hash = nodeCrypto.createHash("shake256", { outputLength: 4 })
1034
+ .update(String(opts.pctSampleKey)).digest();
1035
+ var u32 = (hash[0] << 24 >>> 0) + (hash[1] << 16) + (hash[2] << 8) + hash[3]; // allow:raw-byte-literal — uint32 bit assembly
1036
+ sampleRoll = u32 % 100; // allow:raw-byte-literal — pct sample roll
1037
+ } else {
1038
+ sampleRoll = bCrypto.randomInt(0, 100); // allow:raw-byte-literal — pct sample roll
1039
+ }
1040
+ var sampled = !pass && pct < 100 && sampleRoll >= pct;
1041
+ var recommendedAction = pass ? "deliver" :
1042
+ sampled
1043
+ ? (policy.p === "reject" ? "quarantine" :
1044
+ policy.p === "quarantine" ? "none" : "deliver")
1045
+ : (policy.p === "reject" ? "reject" :
1046
+ policy.p === "quarantine" ? "quarantine" :
1047
+ "deliver");
1048
+
1049
+ return {
1050
+ result: pass ? "pass" : "fail",
1051
+ policy: policy,
1052
+ policyOriginDomain: policyOriginDomain,
1053
+ orgDomain: orgDomain,
1054
+ orgDomainPolicyApplied: orgDomainPolicyApplied,
1055
+ psdPolicyApplied: psdPolicyApplied,
1056
+ npPolicyApplied: npApplied,
1057
+ alignment: { spf: spfAligned, dkim: dkimAligned },
1058
+ recommendedAction: recommendedAction,
1059
+ explanation: pass
1060
+ ? "aligned via " + (spfAligned ? "spf" : "dkim")
1061
+ : "no aligned authentication; policy=" + policy.p,
1062
+ };
1063
+ }
1064
+
1065
+ // ---- ARC (RFC 8617) — full per-hop verification ----
1066
+ //
1067
+ // Each hop carries three headers — ARC-Authentication-Results (AAR),
1068
+ // ARC-Message-Signature (AMS), ARC-Seal (AS). AMS verifies the
1069
+ // message body + selected headers (DKIM-shaped signature). AS signs
1070
+ // the chain-of-custody (all prior AAR/AMS/AS headers + own AAR/AMS
1071
+ // with empty b=). Verification follows §5.1.1 (AMS) + §5.1.2 (AS).
1072
+
1073
+ function _splitHeaders(rfc822) {
1074
+ var sep = rfc822.indexOf("\r\n\r\n");
1075
+ if (sep === -1) sep = rfc822.indexOf("\n\n");
1076
+ if (sep === -1) {
1077
+ throw new MailAuthError("mail-auth/arc-no-body",
1078
+ "ARC: message has no header/body separator");
1079
+ }
1080
+ return rfc822.slice(0, sep);
1081
+ }
1082
+
1083
+ function _parseHeaderLines(headerSection) {
1084
+ // Unfold multi-line headers (lines starting with whitespace).
1085
+ var lines = headerSection.split(/\r?\n/);
1086
+ var unfolded = [];
1087
+ for (var i = 0; i < lines.length; i += 1) {
1088
+ var line = lines[i];
1089
+ if (line.length === 0) continue;
1090
+ if ((line.charAt(0) === " " || line.charAt(0) === "\t") && unfolded.length > 0) {
1091
+ unfolded[unfolded.length - 1] += " " + line.replace(/^\s+/, "");
1092
+ } else {
1093
+ unfolded.push(line);
1094
+ }
1095
+ }
1096
+ return unfolded;
1097
+ }
1098
+
1099
+ // RFC 8617 §5.1.2 caps the chain at 50 sets to bound verifier work and
1100
+ // limit how far an attacker can push junk headers.
1101
+ var ARC_MAX_HOPS = 50; // allow:raw-byte-literal — RFC 8617 §5.1.2 chain ceiling
1102
+
1103
+ async function arcVerify(rfc822, opts) {
1104
+ if (typeof rfc822 !== "string" || rfc822.length === 0) {
1105
+ throw new MailAuthError("mail-auth/arc-bad-input",
1106
+ "arc.verify: rfc822 must be a non-empty string");
1107
+ }
1108
+ opts = opts || {};
1109
+ var headers = _parseHeaderLines(_splitHeaders(rfc822));
1110
+ var hops = [];
1111
+ var seenSlot = {}; // {`<instance>:<name>`: true} — duplicate detection
1112
+
1113
+ // 1. Index ARC headers by instance number. Refuse duplicates: a single
1114
+ // hop has exactly one ARC-Seal / ARC-Message-Signature /
1115
+ // ARC-Authentication-Results. A second copy at the same instance is
1116
+ // a malformed chain (per RFC 8617 §5.1 implicit, and a known
1117
+ // injection vector — a forwarder that re-signs with a duplicate
1118
+ // instance would silently overwrite the original signer).
1119
+ var duplicate = false;
1120
+ var maxInstanceSeen = 0;
1121
+ // RFC 8617 §5.2 — verifier MUST process the chain starting with the
1122
+ // highest-instance set, then walk down. Each hop prepends its three
1123
+ // headers (AS, AMS, AAR) to the message, so the source order from
1124
+ // top to bottom is: i=N (AS, AMS, AAR), i=N-1 (...), ..., i=1.
1125
+ // A chain whose source order doesn't decrease has been re-shuffled
1126
+ // by an intermediary that didn't follow §5.1, or is forged. Track
1127
+ // per-header-set first-appearance order and enforce strictly-
1128
+ // decreasing instances.
1129
+ var orderTrail = []; // [{ inst, name, idx }]
1130
+ for (var i = 0; i < headers.length; i += 1) {
1131
+ var line = headers[i];
1132
+ var colonAt = line.indexOf(":");
1133
+ if (colonAt === -1) continue;
1134
+ var name = line.slice(0, colonAt).trim().toLowerCase();
1135
+ var value = line.slice(colonAt + 1).trim();
1136
+ if (name !== "arc-seal" && name !== "arc-message-signature" &&
1137
+ name !== "arc-authentication-results") continue;
1138
+ // ARC hop instance per RFC 8617 §4.2.1 — bounded to 3 digits; the
1139
+ // spec doesn't define a hard ceiling but operational use never
1140
+ // exceeds 50 hops, and a 999-hop limit prevents pathological
1141
+ // header values from chewing the verifier.
1142
+ var iMatch = value.match(/(?:^|[;,\s])i=(\d{1,3})\b/);
1143
+ var inst = iMatch ? parseInt(iMatch[1], 10) : null;
1144
+ if (inst === null || !isFinite(inst) || inst < 1) continue;
1145
+ if (inst > maxInstanceSeen) maxInstanceSeen = inst;
1146
+ var slotKey = inst + ":" + name;
1147
+ if (seenSlot[slotKey]) { duplicate = true; continue; }
1148
+ seenSlot[slotKey] = true;
1149
+ if (!hops[inst - 1]) hops[inst - 1] = { instance: inst };
1150
+ hops[inst - 1][name] = value;
1151
+ orderTrail.push({ inst: inst, name: name, idx: i });
1152
+ }
1153
+
1154
+ // Source-order enforcement (RFC 8617 §5.1 + §5.2): the first AS for
1155
+ // a given hop must appear before its AMS, which must appear before
1156
+ // its AAR (within a single set). Across sets, hop instances MUST
1157
+ // strictly decrease top-to-bottom. Use the first-appearance index
1158
+ // per hop to validate the cross-set ordering; an out-of-order chain
1159
+ // is treated as a structural failure rather than risking a permissive
1160
+ // verdict.
1161
+ var orderFail = null;
1162
+ if (orderTrail.length > 0) {
1163
+ // Per-hop first-appearance: which i= instance owns each contiguous
1164
+ // run? Walk top to bottom and confirm the instance numbers, when
1165
+ // they change, only EVER decrease.
1166
+ var prevInst = null;
1167
+ for (var oi = 0; oi < orderTrail.length; oi += 1) {
1168
+ var cur = orderTrail[oi].inst;
1169
+ if (prevInst !== null && cur > prevInst) {
1170
+ orderFail = "header-order-ascending-i=" + cur + "-after-i=" + prevInst;
1171
+ break;
1172
+ }
1173
+ prevInst = cur;
1174
+ }
1175
+ }
1176
+
1177
+ if (hops.length === 0) {
1178
+ return { chainStatus: "none", hopCount: 0, hops: [] };
1179
+ }
1180
+
1181
+ if (duplicate) {
1182
+ return {
1183
+ chainStatus: "fail",
1184
+ reason: "duplicate-instance",
1185
+ hopCount: hops.filter(Boolean).length,
1186
+ hops: hops.filter(Boolean).map(function (h) {
1187
+ return { instance: h.instance,
1188
+ hasSeal: !!h["arc-seal"],
1189
+ hasMessageSignature: !!h["arc-message-signature"],
1190
+ hasAuthenticationResults: !!h["arc-authentication-results"],
1191
+ amsResult: "skipped", asResult: "skipped" };
1192
+ }),
1193
+ };
1194
+ }
1195
+
1196
+ if (orderFail) {
1197
+ return {
1198
+ chainStatus: "fail",
1199
+ reason: "header-order-violation: " + orderFail,
1200
+ hopCount: hops.filter(Boolean).length,
1201
+ hops: hops.filter(Boolean).map(function (h) {
1202
+ return { instance: h.instance,
1203
+ hasSeal: !!h["arc-seal"],
1204
+ hasMessageSignature: !!h["arc-message-signature"],
1205
+ hasAuthenticationResults: !!h["arc-authentication-results"],
1206
+ amsResult: "skipped", asResult: "skipped" };
1207
+ }),
1208
+ };
1209
+ }
1210
+
1211
+ if (maxInstanceSeen > ARC_MAX_HOPS) {
1212
+ return {
1213
+ chainStatus: "fail",
1214
+ reason: "too-many-hops",
1215
+ hopCount: maxInstanceSeen,
1216
+ hops: [],
1217
+ };
1218
+ }
1219
+
1220
+ // 2. Structural check — every hop must carry all three headers AND
1221
+ // the chain must start at i=1 with no gaps. RFC 8617 §5.1 requires
1222
+ // instances to form a contiguous 1..N sequence. Indexed loop (not
1223
+ // .some) because sparse arrays skip empty slots in callbacks —
1224
+ // a non-contiguous chain ([hop1, , hop3]) would silently pass.
1225
+ var structuralFail = false;
1226
+ for (var sci = 0; sci < hops.length; sci += 1) {
1227
+ var sch = hops[sci];
1228
+ if (!sch || !sch["arc-seal"] || !sch["arc-message-signature"] ||
1229
+ !sch["arc-authentication-results"]) {
1230
+ structuralFail = true;
1231
+ break;
1232
+ }
1233
+ }
1234
+ if (structuralFail) {
1235
+ return {
1236
+ chainStatus: "fail",
1237
+ reason: "incomplete-or-non-contiguous",
1238
+ hopCount: hops.filter(Boolean).length,
1239
+ hops: hops.filter(Boolean).map(function (h) {
1240
+ return { instance: h.instance,
1241
+ hasSeal: !!h["arc-seal"],
1242
+ hasMessageSignature: !!h["arc-message-signature"],
1243
+ hasAuthenticationResults: !!h["arc-authentication-results"],
1244
+ amsResult: "skipped", asResult: "skipped" };
1245
+ }),
1246
+ };
1247
+ }
1248
+
1249
+ // 3. Per-hop AMS + AS verification.
1250
+ var perHop = [];
1251
+ var anyFail = false;
1252
+ // RFC 8617 §5.2 — operator-tunable clock skew on t= (signing
1253
+ // timestamp) and x= (expiration) tags. Default 5 min.
1254
+ var arcClockSkewMs = typeof opts.clockSkewMs === "number" && opts.clockSkewMs >= 0 // allow:numeric-opt-Infinity — operator-supplied skew, default 5 min
1255
+ ? opts.clockSkewMs : C.TIME.minutes(5);
1256
+ var nowSec = Math.floor(Date.now() / 1000); // allow:raw-byte-literal — Unix epoch seconds divisor
1257
+
1258
+ for (var hopIdx = 0; hopIdx < hops.length; hopIdx += 1) {
1259
+ var hop = hops[hopIdx];
1260
+
1261
+ // RFC 8617 §5.2 — verifier MUST reject AMS or AS with t= timestamp
1262
+ // in the future or x= expiration in the past (with operator skew
1263
+ // tolerance). Pre-v0.8.17 the verifier parsed t= but never
1264
+ // enforced it.
1265
+ var amsTags = _parseArcTagList(hop["arc-message-signature"]);
1266
+ var asTags = _parseArcTagList(hop["arc-seal"]);
1267
+ var amsT = amsTags.t ? parseInt(amsTags.t, 10) : null;
1268
+ var amsX = amsTags.x ? parseInt(amsTags.x, 10) : null;
1269
+ var asT = asTags.t ? parseInt(asTags.t, 10) : null;
1270
+ var asX = asTags.x ? parseInt(asTags.x, 10) : null;
1271
+ var skewSec = Math.floor(arcClockSkewMs / 1000); // allow:raw-byte-literal — sec divisor
1272
+ var timeFault = null;
1273
+ if (amsT && isFinite(amsT) && amsT - skewSec > nowSec) timeFault = "ams-t-future";
1274
+ if (amsX && isFinite(amsX) && amsX + skewSec < nowSec) timeFault = "ams-x-expired";
1275
+ if (asT && isFinite(asT) && asT - skewSec > nowSec) timeFault = "as-t-future";
1276
+ if (asX && isFinite(asX) && asX + skewSec < nowSec) timeFault = "as-x-expired";
1277
+
1278
+ // AMS — RFC 8617 §5.1.1. Same shape as a DKIM-Signature; reuses
1279
+ // the DKIM verifier by injecting a temporary message that has
1280
+ // the AMS as the signing header.
1281
+ var amsResult = timeFault
1282
+ ? { result: "fail", errors: ["ams: " + timeFault + " (RFC 8617 §5.2)"] }
1283
+ : await _verifyArc(rfc822, hop, hops, "ams", opts.dnsLookup, dkim);
1284
+
1285
+ // AS — RFC 8617 §5.1.2. Signs the catenation of all prior
1286
+ // ARC-{AAR,AMS,AS} headers plus current AAR + AMS, then the AS
1287
+ // itself with empty b=.
1288
+ var asResult = timeFault
1289
+ ? { result: "fail", errors: ["as: " + timeFault + " (RFC 8617 §5.2)"] }
1290
+ : await _verifyArc(rfc822, hop, hops, "as", opts.dnsLookup, dkim);
1291
+
1292
+ perHop.push({
1293
+ instance: hop.instance,
1294
+ hasSeal: true,
1295
+ hasMessageSignature: true,
1296
+ hasAuthenticationResults: true,
1297
+ amsResult: amsResult.result,
1298
+ asResult: asResult.result,
1299
+ amsErrors: amsResult.errors,
1300
+ asErrors: asResult.errors,
1301
+ });
1302
+ if (amsResult.result !== "pass" || asResult.result !== "pass") anyFail = true;
1303
+ }
1304
+
1305
+ // 4. Chain Validation per RFC 8617 §5.2.
1306
+ //
1307
+ // Per-hop cv= self-attestation rules:
1308
+ // i=1 — cv=none REQUIRED (no upstream chain to validate)
1309
+ // i>=2 — cv=pass or cv=fail; cv=none is invalid at i>=2
1310
+ //
1311
+ // Once any hop's AS reports cv=fail, the chain is permanently
1312
+ // broken — downstream cv=pass claims after an upstream cv=fail
1313
+ // are malformed (a hop can't claim the chain validates when it
1314
+ // knows an earlier hop saw it fail).
1315
+ var perHopCv = [];
1316
+ var hopRuleViolation = null;
1317
+ var sawFail = false;
1318
+ for (var hi = 0; hi < hops.length; hi += 1) {
1319
+ var as = hops[hi]["arc-seal"];
1320
+ var hopCvMatch = as.match(/(?:^|[;,\s])cv=(none|pass|fail)/);
1321
+ var hopCv = hopCvMatch ? hopCvMatch[1] : null;
1322
+ perHopCv.push(hopCv);
1323
+ if (hopCv === null) {
1324
+ hopRuleViolation = "missing-cv-at-i=" + (hi + 1);
1325
+ break;
1326
+ }
1327
+ if (hi === 0 && hopCv !== "none") {
1328
+ hopRuleViolation = "i=1-cv-must-be-none-got-" + hopCv;
1329
+ break;
1330
+ }
1331
+ if (hi >= 1 && hopCv === "none") {
1332
+ hopRuleViolation = "i=" + (hi + 1) + "-cv=none-invalid-after-hop-1";
1333
+ break;
1334
+ }
1335
+ if (hopCv === "fail") sawFail = true;
1336
+ if (hopCv === "pass" && sawFail) {
1337
+ hopRuleViolation = "i=" + (hi + 1) + "-cv=pass-after-upstream-fail";
1338
+ break;
1339
+ }
1340
+ }
1341
+
1342
+ var lastCv = perHopCv[perHopCv.length - 1];
1343
+ var chainStatus;
1344
+ var reasonOut = null;
1345
+ if (hopRuleViolation) {
1346
+ chainStatus = "fail";
1347
+ reasonOut = hopRuleViolation;
1348
+ } else if (anyFail) {
1349
+ chainStatus = "fail";
1350
+ reasonOut = "signature-verification-failed";
1351
+ } else if (lastCv === "fail") {
1352
+ chainStatus = "fail";
1353
+ reasonOut = "last-as-cv=fail";
1354
+ } else if (hops.length === 1 && lastCv === "none") {
1355
+ chainStatus = "pass";
1356
+ } else if (hops.length > 1 && lastCv === "pass") {
1357
+ chainStatus = "pass";
1358
+ } else {
1359
+ chainStatus = "fail";
1360
+ reasonOut = "unexpected-cv-state";
1361
+ }
1362
+
1363
+ var out = {
1364
+ chainStatus: chainStatus,
1365
+ hopCount: hops.length,
1366
+ cv: lastCv,
1367
+ perHopCv: perHopCv,
1368
+ hops: perHop,
1369
+ };
1370
+ if (reasonOut) out.reason = reasonOut;
1371
+ return out;
1372
+ }
1373
+
1374
+ // Verify a single AMS or AS within the chain by reconstructing the
1375
+ // signed string per RFC 8617 + invoking node:crypto.verify with the
1376
+ // public key fetched from the AMS's d= + s= TXT record.
1377
+ async function _verifyArc(rfc822, hop, allHops, kind, dnsLookup, dkim) {
1378
+ var sigHeaderName = kind === "ams" ? "arc-message-signature" : "arc-seal";
1379
+ var sigValue = hop[sigHeaderName];
1380
+ var tags = _parseArcTagList(sigValue);
1381
+ if (!tags.d || !tags.s || !tags.b || !tags.a) {
1382
+ return { result: "permerror", errors: [kind + ": missing required tag(s) d/s/b/a"] };
1383
+ }
1384
+ if (tags.a !== "rsa-sha256" && tags.a !== "ed25519-sha256") {
1385
+ return { result: "permerror", errors: [kind + ": unsupported alg '" + tags.a + "'"] };
1386
+ }
1387
+
1388
+ // Fetch the signing public key from <s>._domainkey.<d>.
1389
+ var keyTags;
1390
+ try {
1391
+ var qname = tags.s + "._domainkey." + tags.d;
1392
+ var records = await _safeResolveTxt(qname, dnsLookup);
1393
+ keyTags = _parseDkimKeyRecord(records);
1394
+ } catch (e) {
1395
+ var verdict = (e && (e.code === "ENOTFOUND" || e.code === "ENODATA"))
1396
+ ? "permerror" : "temperror";
1397
+ return { result: verdict, errors: [kind + ": key lookup failed: " +
1398
+ ((e && e.message) || String(e))] };
1399
+ }
1400
+ if (!keyTags || !keyTags.p) {
1401
+ return { result: "permerror", errors: [kind + ": key record missing p="] };
1402
+ }
1403
+
1404
+ // Reconstruct the canonical signed string.
1405
+ var canonicalized;
1406
+ if (kind === "ams") {
1407
+ // AMS signs the body + selected headers, identical to DKIM-Sig.
1408
+ // Reuse the DKIM verifier by passing a synthetic message where
1409
+ // the AMS header is renamed to DKIM-Signature.
1410
+ return await _verifyAmsViaDkim(rfc822, hop, sigValue, tags, dkim, dnsLookup);
1411
+ }
1412
+
1413
+ // AS signs the catenation of every prior AAR/AMS/AS plus current
1414
+ // AAR/AMS, then the AS itself with empty b= per RFC 8617 §5.1.2.
1415
+ canonicalized = "";
1416
+ for (var prior = 0; prior < hop.instance; prior += 1) {
1417
+ var p = allHops[prior];
1418
+ if (!p) continue;
1419
+ canonicalized += _canonRelaxedHeader("ARC-Authentication-Results", p["arc-authentication-results"]);
1420
+ canonicalized += _canonRelaxedHeader("ARC-Message-Signature", p["arc-message-signature"]);
1421
+ if (p.instance !== hop.instance) {
1422
+ // Prior AS gets included whole.
1423
+ canonicalized += _canonRelaxedHeader("ARC-Seal", p["arc-seal"]);
1424
+ }
1425
+ }
1426
+ // Current AS with b= emptied. RFC 8617 §5.1.2: canonicalization
1427
+ // includes the AS header with `b=` value stripped + no trailing CRLF.
1428
+ var asUnsigned = sigValue.replace(/(\bb=)[^;]*/i, "$1");
1429
+ canonicalized += _canonRelaxedHeader("ARC-Seal", asUnsigned).replace(/\r\n$/, "");
1430
+
1431
+ // Verify the AS signature.
1432
+ return _runVerify(canonicalized, tags.b, tags.a, keyTags.p, "as");
1433
+ }
1434
+
1435
+ async function _verifyAmsViaDkim(rfc822, hop, sigValue, tags, dkim, dnsLookup) {
1436
+ // Build a synthetic rfc822 where the ARC-Message-Signature is renamed
1437
+ // to DKIM-Signature so the existing DKIM verifier handles AMS
1438
+ // verification (the cryptographic shape is identical).
1439
+ var renamedHeader = "DKIM-Signature: " + sigValue;
1440
+ var sep = rfc822.indexOf("\r\n\r\n");
1441
+ if (sep === -1) sep = rfc822.indexOf("\n\n");
1442
+ var headerEnd = sep === -1 ? rfc822.length : sep;
1443
+ // Strip every other ARC-* header so the DKIM verifier doesn't see
1444
+ // them, AND replace the AMS itself with DKIM-Signature for this hop.
1445
+ var headerLines = _parseHeaderLines(rfc822.slice(0, headerEnd));
1446
+ var rebuilt = [];
1447
+ for (var i = 0; i < headerLines.length; i += 1) {
1448
+ var line = headerLines[i];
1449
+ var colonAt = line.indexOf(":");
1450
+ if (colonAt === -1) { rebuilt.push(line); continue; }
1451
+ var name = line.slice(0, colonAt).trim().toLowerCase();
1452
+ if (name === "arc-message-signature" ||
1453
+ name === "arc-seal" ||
1454
+ name === "dkim-signature") {
1455
+ continue;
1456
+ }
1457
+ if (name === "arc-authentication-results") {
1458
+ // RFC 8617 §5.1.1 — keep only the CURRENT hop's AAR (signer
1459
+ // canonicalizes it via h=). Pre-v0.8.17 stripped every AAR
1460
+ // unconditionally, breaking verification on chains that
1461
+ // included AAR in h= (Microsoft + Google interop).
1462
+ var instMatch = /\bi\s*=\s*(\d+)/.exec(line.slice(colonAt + 1));
1463
+ if (!instMatch || parseInt(instMatch[1], 10) !== hop.instance) continue;
1464
+ }
1465
+ rebuilt.push(line);
1466
+ }
1467
+ rebuilt.unshift(renamedHeader);
1468
+ var synthetic = rebuilt.join("\r\n") + (sep === -1 ? "" :
1469
+ rfc822.slice(headerEnd));
1470
+ var rv = await dkim.verify(synthetic, { dnsLookup: dnsLookup });
1471
+ if (!Array.isArray(rv) || rv.length === 0) {
1472
+ return { result: "permerror", errors: ["ams: dkim verifier returned no results"] };
1473
+ }
1474
+ return { result: rv[0].result, errors: rv[0].errors || [] };
1475
+ }
1476
+
1477
+ function _parseArcTagList(value) {
1478
+ var tags = {};
1479
+ var parts = String(value).split(";"); // allow:bare-split-on-quoted-header — allow:raw-byte-literal — RFC 8617 §4 ARC tag-list grammar (same as the DKIM RFC's): `tag-spec *( ";" tag-spec )`, tag-value contains no DQUOTE
1480
+
1481
+ for (var i = 0; i < parts.length; i += 1) {
1482
+ var p = parts[i].trim();
1483
+ if (p.length === 0) continue;
1484
+ var eq = p.indexOf("=");
1485
+ if (eq === -1) continue;
1486
+ tags[p.slice(0, eq).trim().toLowerCase()] = p.slice(eq + 1).trim().replace(/\s+/g, "");
1487
+ }
1488
+ return tags;
1489
+ }
1490
+
1491
+ function _parseDkimKeyRecord(records) {
1492
+ var joined = "";
1493
+ if (Array.isArray(records)) {
1494
+ for (var i = 0; i < records.length; i += 1) {
1495
+ var rec = records[i];
1496
+ joined = Array.isArray(rec) ? rec.join("") : String(rec);
1497
+ if (joined.indexOf("v=DKIM1") === 0 || joined.indexOf("p=") !== -1) break;
1498
+ }
1499
+ } else {
1500
+ joined = String(records || "");
1501
+ }
1502
+ return _parseArcTagList(joined);
1503
+ }
1504
+
1505
+ function _canonRelaxedHeader(name, value) {
1506
+ // RFC 6376 §3.4.2 — relaxed header canon: lowercase name, unfold,
1507
+ // collapse internal WSP runs, strip trailing WSP.
1508
+ var unfolded = String(value).replace(/\r?\n[ \t]+/g, " ");
1509
+ var trimmed = unfolded.replace(/[ \t]+/g, " ").replace(/^[ \t]+|[ \t]+$/g, "");
1510
+ return name.toLowerCase() + ":" + trimmed + "\r\n";
1511
+ }
1512
+
1513
+ function _pemFromB64KeyMaterial(b64) {
1514
+ var pem = "-----BEGIN PUBLIC KEY-----\n";
1515
+ for (var i = 0; i < b64.length; i += 64) { // allow:raw-byte-literal — PEM wrap width
1516
+ pem += b64.slice(i, i + 64) + "\n"; // allow:raw-byte-literal — PEM wrap width
1517
+ }
1518
+ pem += "-----END PUBLIC KEY-----\n";
1519
+ return pem;
1520
+ }
1521
+
1522
+ function _runVerify(signedString, sigB64, algorithm, keyB64, label) {
1523
+ var nodeCrypto = require("node:crypto");
1524
+ var pem = _pemFromB64KeyMaterial(keyB64);
1525
+ var keyObj;
1526
+ try { keyObj = nodeCrypto.createPublicKey(pem); }
1527
+ catch (e) {
1528
+ return { result: "permerror",
1529
+ errors: [label + ": key parse failed: " + ((e && e.message) || String(e))] };
1530
+ }
1531
+ var nodeAlgo = algorithm === "rsa-sha256" ? "sha256" : null;
1532
+ var sigBuf = Buffer.from(sigB64, "base64");
1533
+ var verified;
1534
+ try {
1535
+ verified = nodeCrypto.verify(nodeAlgo, Buffer.from(signedString, "utf8"), keyObj, sigBuf);
1536
+ } catch (e) {
1537
+ return { result: "permerror",
1538
+ errors: [label + ": verify threw: " + ((e && e.message) || String(e))] };
1539
+ }
1540
+ return verified
1541
+ ? { result: "pass", errors: [] }
1542
+ : { result: "fail", errors: [label + ": signature verification failed"] };
1543
+ }
1544
+
1545
+ void C; // C is imported for future TIME constants in policy fetchers.
1546
+
1547
+ // ---- ARC receiver-side trust evaluation (RFC 8617 §6) ----
1548
+ //
1549
+ // arc.verify confirms the cryptographic chain validates; arc.evaluate
1550
+ // is the operator-side trust decision: given a passing chain, did any
1551
+ // hop in the chain belong to a sealer the operator trusts? The trust
1552
+ // list is operator policy — typically the operator's own domain plus
1553
+ // upstream relays the operator has agreed to honor (mailing list
1554
+ // operators, MX-vendor middleware).
1555
+ //
1556
+ // var rv = await b.mail.arc.evaluate(rfc822, {
1557
+ // trustedSealers: ["example.com", "mailgun.net"],
1558
+ // });
1559
+ // // → { chainStatus: "pass", trusted: true, trustedHop: 2,
1560
+ // // trustedDomain: "mailgun.net" }
1561
+
1562
+ async function arcEvaluate(rfc822, opts) {
1563
+ if (typeof rfc822 !== "string" || rfc822.length === 0) {
1564
+ throw new MailAuthError("mail-auth/arc-bad-input",
1565
+ "arc.evaluate: rfc822 must be a non-empty string");
1566
+ }
1567
+ opts = opts || {};
1568
+ if (!Array.isArray(opts.trustedSealers)) {
1569
+ throw new MailAuthError("mail-auth/arc-bad-trusted-sealers",
1570
+ "arc.evaluate: opts.trustedSealers must be an array of domain strings");
1571
+ }
1572
+ var trusted = {};
1573
+ for (var ti = 0; ti < opts.trustedSealers.length; ti += 1) {
1574
+ var d = opts.trustedSealers[ti];
1575
+ if (typeof d !== "string" || d.length === 0) {
1576
+ throw new MailAuthError("mail-auth/arc-trust-eval-failed",
1577
+ "arc.evaluate: trustedSealers[" + ti + "] must be a non-empty domain string");
1578
+ }
1579
+ trusted[d.toLowerCase()] = true;
1580
+ }
1581
+
1582
+ var verdict = await arcVerify(rfc822, opts);
1583
+ var out = {
1584
+ chainStatus: verdict.chainStatus,
1585
+ hopCount: verdict.hopCount,
1586
+ trusted: false,
1587
+ trustedHop: null,
1588
+ trustedDomain: null,
1589
+ // RFC 8617 §6 trust evaluation extension surface (B6).
1590
+ // trust: "trusted" | "unverified" | "failed"
1591
+ // trustedHops: [{ instance, domain }] of every trusted sealer
1592
+ // in the validated chain
1593
+ // finalAr: verbatim AAR from the most-recent hop (the
1594
+ // receiver's view of upstream auth results)
1595
+ // breakAt: first instance whose AMS or AS failed, or null
1596
+ // when every hop verified
1597
+ trust: verdict.chainStatus === "pass" ? "unverified" : "failed",
1598
+ trustedHops: [],
1599
+ finalAr: null,
1600
+ breakAt: null,
1601
+ };
1602
+ if (verdict.reason) out.reason = verdict.reason;
1603
+
1604
+ // Re-extract per-hop d= (signing domain on AS) AND the AAR text from
1605
+ // the original headers — the verify-result shape doesn't carry
1606
+ // them. One pass over the header section.
1607
+ var headers = _parseHeaderLines(_splitHeaders(rfc822));
1608
+ var hopDomains = {};
1609
+ var hopAr = {};
1610
+ for (var hi = 0; hi < headers.length; hi += 1) {
1611
+ var line = headers[hi];
1612
+ var colonAt = line.indexOf(":");
1613
+ if (colonAt === -1) continue;
1614
+ var name = line.slice(0, colonAt).trim().toLowerCase();
1615
+ var value = line.slice(colonAt + 1).trim();
1616
+ if (name === "arc-seal") {
1617
+ var iMatch = value.match(/(?:^|[;,\s])i=(\d+)/); // allow:regex-no-length-cap — header bounded by RFC 5322 998
1618
+ var dMatch = value.match(/(?:^|[;,\s])d=([^\s;]+)/); // allow:regex-no-length-cap — header bounded by RFC 5322 998
1619
+ if (iMatch && dMatch) hopDomains[parseInt(iMatch[1], 10)] = dMatch[1].toLowerCase();
1620
+ } else if (name === "arc-authentication-results") {
1621
+ var arIMatch = value.match(/\bi\s*=\s*(\d+)/); // allow:regex-no-length-cap — header bounded by RFC 5322 998
1622
+ if (arIMatch) hopAr[parseInt(arIMatch[1], 10)] = value;
1623
+ }
1624
+ }
1625
+
1626
+ // finalAr — the most-recent hop's AAR. Always populated when the
1627
+ // chain has at least one hop (regardless of pass/fail), so the
1628
+ // operator can surface upstream auth context even on a broken chain.
1629
+ if (verdict.hopCount > 0) {
1630
+ out.finalAr = hopAr[verdict.hopCount] || null;
1631
+ }
1632
+
1633
+ // breakAt — first instance whose AMS or AS failed.
1634
+ if (Array.isArray(verdict.hops)) {
1635
+ for (var bi = 0; bi < verdict.hops.length; bi += 1) {
1636
+ var bhop = verdict.hops[bi];
1637
+ if (!bhop) continue;
1638
+ if (bhop.amsResult !== "pass" || bhop.asResult !== "pass") {
1639
+ out.breakAt = bhop.instance;
1640
+ break;
1641
+ }
1642
+ }
1643
+ }
1644
+
1645
+ if (verdict.chainStatus !== "pass" || !Array.isArray(verdict.hops)) return out;
1646
+
1647
+ // Walk hops most-recent-first so we attribute the primary trust
1648
+ // decision to the deepest (closest-to-receiver) trusted sealer, but
1649
+ // also collect EVERY trusted hop so the operator can audit the
1650
+ // full custody chain.
1651
+ for (var ri2 = verdict.hops.length - 1; ri2 >= 0; ri2 -= 1) {
1652
+ var hop = verdict.hops[ri2];
1653
+ if (!hop || hop.amsResult !== "pass" || hop.asResult !== "pass") continue;
1654
+ var domain = hopDomains[hop.instance];
1655
+ if (domain && trusted[domain]) {
1656
+ out.trustedHops.push({ instance: hop.instance, domain: domain });
1657
+ if (!out.trusted) {
1658
+ out.trusted = true;
1659
+ out.trustedHop = hop.instance;
1660
+ out.trustedDomain = domain;
1661
+ }
1662
+ }
1663
+ }
1664
+ out.trust = out.trusted ? "trusted" : "unverified";
1665
+ return out;
1666
+ }
1667
+
1668
+ // ---- Authentication-Results header (RFC 8601) builder ----
1669
+ //
1670
+ // Build the A-R header value the receiving MTA prepends to the message
1671
+ // before delivery. Operators consume per-method results from
1672
+ // b.mail.spf.verify / b.mail.dmarc.evaluate / b.mail.arc.verify (or
1673
+ // .evaluate) and pass them to .emit; the framework formats the RFC
1674
+ // 8601-conformant header string.
1675
+ //
1676
+ // var hdr = b.mail.authResults.emit({
1677
+ // authservId: "mx.example.com",
1678
+ // results: [
1679
+ // { method: "spf", result: "pass", smtpMailfrom: "user@sender.example" },
1680
+ // { method: "dkim", result: "pass", domain: "sender.example" },
1681
+ // { method: "dmarc", result: "pass", from: "user@sender.example" },
1682
+ // { method: "arc", result: "pass" },
1683
+ // ],
1684
+ // });
1685
+ // // → "Authentication-Results: mx.example.com;\r\n spf=pass smtp.mailfrom=user@sender.example;\r\n dkim=pass header.d=sender.example;\r\n dmarc=pass header.from=user@sender.example;\r\n arc=pass"
1686
+
1687
+ // RFC 8601 §2.7 — result vocabulary is METHOD-SPECIFIC, not a flat
1688
+ // allowlist. The flat AR_VALID_RESULTS table previously accepted
1689
+ // `hardfail` for DKIM (only valid for DMARC §2.7.4) and `temperror` /
1690
+ // `permerror` for methods that don't recognize them. Per-method maps
1691
+ // match the spec sections cited.
1692
+ var AR_RESULTS_BY_METHOD = {
1693
+ // §2.7.1 — auth
1694
+ auth: { pass: 1, fail: 1, none: 1, permerror: 1, temperror: 1 },
1695
+ // §2.7.2 — domainkeys (legacy; vocabulary kept narrow)
1696
+ domainkeys: { pass: 1, fail: 1, neutral: 1, none: 1, permerror: 1, temperror: 1, policy: 1 },
1697
+ // §2.7.3 — DKIM
1698
+ dkim: { pass: 1, fail: 1, neutral: 1, none: 1, permerror: 1, temperror: 1, policy: 1 },
1699
+ "dkim-adsp": { pass: 1, fail: 1, discard: 1, nxdomain: 1, none: 1, permerror: 1, temperror: 1 },
1700
+ // §2.7.4 — SPF (uses softfail; not hardfail)
1701
+ spf: { pass: 1, fail: 1, softfail: 1, neutral: 1, none: 1, permerror: 1, temperror: 1, policy: 1 },
1702
+ "sender-id": { pass: 1, fail: 1, softfail: 1, neutral: 1, none: 1, permerror: 1, temperror: 1, policy: 1 },
1703
+ // §2.7.5 — IPRev
1704
+ iprev: { pass: 1, fail: 1, permerror: 1, temperror: 1 },
1705
+ // §2.7.6 — DMARC (this is the ONE place hardfail is valid in some drafts; keep it)
1706
+ dmarc: { pass: 1, fail: 1, none: 1, permerror: 1, temperror: 1, hardfail: 1, bestguesspass: 1 },
1707
+ // RFC 8617 §4.1 — ARC
1708
+ arc: { pass: 1, fail: 1, none: 1 },
1709
+ // RFC 8616 — DANE
1710
+ dane: { pass: 1, fail: 1, none: 1, permerror: 1, temperror: 1 },
1711
+ // VBR + DNSWL + S/MIME — vocabulary kept conservative
1712
+ smime: { pass: 1, fail: 1, neutral: 1, none: 1, permerror: 1, temperror: 1, policy: 1 },
1713
+ vbr: { pass: 1, fail: 1, none: 1, permerror: 1, temperror: 1 },
1714
+ dnswl: { pass: 1, none: 1, temperror: 1 },
1715
+ "x-original-authentication-results": { pass: 1, fail: 1, neutral: 1, none: 1, softfail: 1, hardfail: 1, policy: 1, permerror: 1, temperror: 1, bestguesspass: 1, discard: 1, nxdomain: 1 },
1716
+ };
1717
+ var AR_VALID_METHODS = Object.keys(AR_RESULTS_BY_METHOD).reduce(function (acc, m) {
1718
+ acc[m] = 1; return acc;
1719
+ }, {});
1720
+
1721
+ function authResultsEmit(opts) {
1722
+ validateOpts.requireObject(opts, "authResults.emit", MailAuthError, "mail-auth/ar-bad-input");
1723
+ validateOpts(opts, ["authservId", "results", "version", "fold"], "authResults.emit");
1724
+ validateOpts.requireNonEmptyString(opts.authservId,
1725
+ "authResults.emit: authservId", MailAuthError, "mail-auth/ar-bad-authserv-id");
1726
+ if (/[\r\n\0]/.test(opts.authservId)) {
1727
+ throw new MailAuthError("mail-auth/ar-bad-authserv-id",
1728
+ "authResults.emit: authservId contains forbidden control characters");
1729
+ }
1730
+ if (!Array.isArray(opts.results)) {
1731
+ throw new MailAuthError("mail-auth/ar-bad-results",
1732
+ "authResults.emit: results must be an array");
1733
+ }
1734
+
1735
+ var version = (opts.version === undefined || opts.version === null)
1736
+ ? "1" : String(opts.version);
1737
+ var head = opts.authservId + (version === "1" ? "" : " " + version);
1738
+
1739
+ if (opts.results.length === 0) {
1740
+ // RFC 8601 §2.2 — when no methods evaluated, emit `none`.
1741
+ return "Authentication-Results: " + head + "; none";
1742
+ }
1743
+
1744
+ var clauses = [];
1745
+ for (var i = 0; i < opts.results.length; i += 1) {
1746
+ var r = opts.results[i];
1747
+ if (!r || typeof r !== "object") {
1748
+ throw new MailAuthError("mail-auth/ar-bad-result-entry",
1749
+ "authResults.emit: results[" + i + "] must be an object");
1750
+ }
1751
+ var method = String(r.method || "").toLowerCase();
1752
+ var result = String(r.result || "").toLowerCase();
1753
+ if (!AR_VALID_METHODS[method]) {
1754
+ throw new MailAuthError("mail-auth/ar-bad-method",
1755
+ "authResults.emit: unknown method '" + r.method + "'");
1756
+ }
1757
+ var methodResults = AR_RESULTS_BY_METHOD[method];
1758
+ if (!methodResults || !methodResults[result]) {
1759
+ throw new MailAuthError("mail-auth/ar-bad-result",
1760
+ "authResults.emit: result '" + r.result + "' is not in the RFC 8601 §2.7 vocabulary for method '" + method + "'");
1761
+ }
1762
+ var clause = method + "=" + result;
1763
+ if (r.reason && typeof r.reason === "string" && !/[\r\n\0;]/.test(r.reason)) {
1764
+ // RFC 8601 §2.2 — quoted-string allows backslash-escaped DQUOTE
1765
+ // (`\"`). Pre-v0.8.32 the framework collapsed `"` to `'` which
1766
+ // is lossy. Use the spec-correct escape so the receiver can
1767
+ // round-trip the original reason.
1768
+ clause += ' reason="' + r.reason.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
1769
+ }
1770
+ // Method-specific properties (ptype.property=value triples per
1771
+ // RFC 8601 §2.3). Operators pass them as flat object keys.
1772
+ var props = {
1773
+ smtpMailfrom: "smtp.mailfrom",
1774
+ smtpHelo: "smtp.helo",
1775
+ domain: "header.d",
1776
+ selector: "header.s",
1777
+ from: "header.from",
1778
+ iprev: "policy.iprev",
1779
+ ip: "policy.ip",
1780
+ tls: "policy.tls",
1781
+ };
1782
+ var propKeys = Object.keys(props);
1783
+ for (var pk = 0; pk < propKeys.length; pk += 1) {
1784
+ var k = propKeys[pk];
1785
+ var rv = r[k];
1786
+ if (typeof rv !== "string" || rv.length === 0) continue;
1787
+ // pvalue ABNF per RFC 8601 §2.3:
1788
+ // pvalue = [CFWS] ((value / dot-atom-text) [CFWS]) /
1789
+ // (local-part "@" domain) [CFWS]
1790
+ // For framework emit we require the printable-ASCII subset of
1791
+ // dot-atom-text + local-part-at-domain shapes; CRLF / NUL /
1792
+ // semicolon / SP / HTAB / quoting metacharacters are refused
1793
+ // (operator-supplied value is structured, not free-form).
1794
+ if (!/^[A-Za-z0-9._@\-:[\]]+$/.test(rv)) continue; // allow:regex-no-length-cap — bounded by header line cap
1795
+ clause += " " + props[k] + "=" + rv;
1796
+ }
1797
+ clauses.push(clause);
1798
+ }
1799
+
1800
+ var fold = opts.fold !== false;
1801
+ var sep = fold ? ";\r\n " : "; ";
1802
+ return "Authentication-Results: " + head + ";\r\n " + clauses.join(sep);
1803
+ }
1804
+
1805
+ // ---- DMARC aggregate (RUA) report parser (RFC 7489 §7.2 / draft-ietf-dmarc-aggregate-reporting) ----
1806
+ //
1807
+ // MTAs that publish a DMARC `rua=` policy receive aggregate reports
1808
+ // from peers — XML attached to a multipart/report mail body, often
1809
+ // gzip-compressed. This primitive accepts the report bytes (raw XML,
1810
+ // gzipped XML, or a parsed object) and returns a structured shape
1811
+ // with the metadata, published policy, and per-record evaluation
1812
+ // results.
1813
+ //
1814
+ // var rv = b.mail.dmarc.parseAggregateReport(xmlBytes);
1815
+ // // → {
1816
+ // // reportMetadata: { orgName, email, reportId, dateRange },
1817
+ // // policyPublished: { domain, adkim, aspf, p, sp, pct, ... },
1818
+ // // records: [{ sourceIp, count, dispositions, identifiers, authResults }]
1819
+ // // }
1820
+
1821
+ var DMARC_RUA_MAX_REPORT_BYTES = C.BYTES.mib(8);
1822
+ var DMARC_RUA_MAX_RECORDS_PER_REPORT = 10000; // allow:raw-byte-literal allow:raw-time-literal — record cap, not seconds
1823
+
1824
+ function _arrayOf(value) {
1825
+ if (value === undefined || value === null) return [];
1826
+ return Array.isArray(value) ? value : [value];
1827
+ }
1828
+
1829
+ function dmarcParseAggregateReport(input, opts) {
1830
+ opts = opts || {};
1831
+ var bytes;
1832
+ if (Buffer.isBuffer(input)) bytes = input;
1833
+ else if (typeof input === "string") bytes = Buffer.from(input, "utf8");
1834
+ else if (input && typeof input === "object" && input.feedback) {
1835
+ // operator already pre-parsed via safeXml; skip the parse step.
1836
+ return _shapeAggregateReport(input);
1837
+ }
1838
+ else {
1839
+ throw new MailAuthError("mail-auth/dmarc-rua-bad-input",
1840
+ "dmarc.parseAggregateReport: input must be a Buffer, string, or pre-parsed object");
1841
+ }
1842
+ if (bytes.length > DMARC_RUA_MAX_REPORT_BYTES) {
1843
+ throw new MailAuthError("mail-auth/dmarc-rua-too-large",
1844
+ "dmarc.parseAggregateReport: report exceeds " + DMARC_RUA_MAX_REPORT_BYTES + " bytes");
1845
+ }
1846
+
1847
+ // Auto-detect gzip via magic 0x1f 0x8b (RFC 1952). DMARC RUA reports
1848
+ // are commonly zip- or gzip-compressed; the gzip magic check covers
1849
+ // the bulk of real-world reports. ZIP archives need operator-side
1850
+ // unzip first (the framework doesn't ship a ZIP primitive yet).
1851
+ var contentType = (opts.contentType || "").toLowerCase();
1852
+ var looksGzip = bytes.length >= 2 && bytes[0] === 0x1f && bytes[1] === 0x8b;
1853
+ if (contentType.indexOf("gzip") !== -1 || looksGzip) {
1854
+ try { bytes = zlib.gunzipSync(bytes, { maxOutputLength: DMARC_RUA_MAX_REPORT_BYTES }); }
1855
+ catch (e) {
1856
+ // Distinguish "decompressed bytes exceed cap" (gunzip bomb /
1857
+ // amplification — operator should rate-limit the source) from
1858
+ // "stream is malformed" (operator-level diagnostic) so audit/
1859
+ // alert wiring can react differently. Node surfaces the bomb
1860
+ // case with ERR_BUFFER_TOO_LARGE / "Output length exceeded the
1861
+ // limit" / the explicit `maxOutputLength` code. CVE-class:
1862
+ // CVE-2024-zlib decompression amplification.
1863
+ var msg = (e && e.message) || String(e);
1864
+ var isBomb = (e && (e.code === "ERR_BUFFER_TOO_LARGE" ||
1865
+ e.code === "ERR_OUT_OF_RANGE")) ||
1866
+ /output length|max(?:imum)?\s+output|exceeds?/i.test(msg);
1867
+ if (isBomb) {
1868
+ throw new MailAuthError("mail-auth/dmarc-rua-gunzip-bomb",
1869
+ "dmarc.parseAggregateReport: gunzip output exceeded " +
1870
+ DMARC_RUA_MAX_REPORT_BYTES + " bytes (decompression amplification — refused)");
1871
+ }
1872
+ throw new MailAuthError("mail-auth/dmarc-rua-gunzip-failed",
1873
+ "dmarc.parseAggregateReport: gunzip failed: " + msg);
1874
+ }
1875
+ }
1876
+
1877
+ var parsed;
1878
+ try { parsed = safeXml.parse(bytes.toString("utf8"), { maxBytes: DMARC_RUA_MAX_REPORT_BYTES }); }
1879
+ catch (e) {
1880
+ throw new MailAuthError("mail-auth/dmarc-rua-bad-xml",
1881
+ "dmarc.parseAggregateReport: XML parse failed: " + ((e && e.message) || String(e)));
1882
+ }
1883
+ return _shapeAggregateReport(parsed);
1884
+ }
1885
+
1886
+ function _shapeAggregateReport(parsed) {
1887
+ if (!parsed || typeof parsed !== "object" || !parsed.feedback) {
1888
+ throw new MailAuthError("mail-auth/dmarc-rua-no-feedback",
1889
+ "dmarc.parseAggregateReport: report root must be <feedback>");
1890
+ }
1891
+ var feedback = parsed.feedback;
1892
+ var rmRaw = feedback.report_metadata || {};
1893
+ var ppRaw = feedback.policy_published || {};
1894
+ var records = _arrayOf(feedback.record);
1895
+ if (records.length > DMARC_RUA_MAX_RECORDS_PER_REPORT) {
1896
+ throw new MailAuthError("mail-auth/dmarc-rua-too-many-records",
1897
+ "dmarc.parseAggregateReport: report has " + records.length +
1898
+ " records (cap " + DMARC_RUA_MAX_RECORDS_PER_REPORT + ")");
1899
+ }
1900
+
1901
+ var dateRange = rmRaw.date_range || {};
1902
+ var beginSec = parseInt(dateRange.begin, 10);
1903
+ var endSec = parseInt(dateRange.end, 10);
1904
+
1905
+ var shaped = {
1906
+ reportMetadata: {
1907
+ orgName: rmRaw.org_name || null,
1908
+ email: rmRaw.email || null,
1909
+ reportId: rmRaw.report_id || null,
1910
+ extraContact: rmRaw.extra_contact_info || null,
1911
+ dateRange: {
1912
+ begin: isFinite(beginSec) ? beginSec : null,
1913
+ end: isFinite(endSec) ? endSec : null,
1914
+ },
1915
+ },
1916
+ policyPublished: {
1917
+ domain: ppRaw.domain || null,
1918
+ adkim: ppRaw.adkim || null,
1919
+ aspf: ppRaw.aspf || null,
1920
+ p: ppRaw.p || null,
1921
+ sp: ppRaw.sp || null,
1922
+ pct: ppRaw.pct === undefined ? null : parseInt(ppRaw.pct, 10),
1923
+ fo: ppRaw.fo || null,
1924
+ },
1925
+ records: records.map(function (rec) {
1926
+ var row = rec.row || {};
1927
+ var pe = row.policy_evaluated || {};
1928
+ var ids = rec.identifiers || {};
1929
+ var ar = rec.auth_results || {};
1930
+ var dkimResults = _arrayOf(ar.dkim).map(function (d) {
1931
+ return {
1932
+ domain: d.domain || null,
1933
+ selector: d.selector || null,
1934
+ result: d.result || null,
1935
+ humanResult: d.human_result || null,
1936
+ };
1937
+ });
1938
+ var spfResults = _arrayOf(ar.spf).map(function (s) {
1939
+ return {
1940
+ domain: s.domain || null,
1941
+ result: s.result || null,
1942
+ scope: s.scope || null,
1943
+ };
1944
+ });
1945
+ var reasons = _arrayOf(pe.reason).map(function (r) {
1946
+ return { type: r.type || null, comment: r.comment || null };
1947
+ });
1948
+ var count = parseInt(row.count, 10);
1949
+ return {
1950
+ sourceIp: row.source_ip || null,
1951
+ count: isFinite(count) ? count : null,
1952
+ dispositions: {
1953
+ disposition: pe.disposition || null,
1954
+ dkim: pe.dkim || null,
1955
+ spf: pe.spf || null,
1956
+ reasons: reasons,
1957
+ },
1958
+ identifiers: {
1959
+ headerFrom: ids.header_from || null,
1960
+ envelopeFrom: ids.envelope_from || null,
1961
+ envelopeTo: ids.envelope_to || null,
1962
+ },
1963
+ authResults: {
1964
+ dkim: dkimResults,
1965
+ spf: spfResults,
1966
+ },
1967
+ };
1968
+ }),
1969
+ };
1970
+
1971
+ // Convenience aggregates — most operators want the totals up front.
1972
+ var totalCount = 0;
1973
+ var passCount = 0;
1974
+ var failCount = 0;
1975
+ for (var i = 0; i < shaped.records.length; i += 1) {
1976
+ var r = shaped.records[i];
1977
+ if (typeof r.count === "number") totalCount += r.count;
1978
+ var dispDkim = r.dispositions.dkim;
1979
+ var dispSpf = r.dispositions.spf;
1980
+ if (dispDkim === "pass" || dispSpf === "pass") {
1981
+ if (typeof r.count === "number") passCount += r.count;
1982
+ } else {
1983
+ if (typeof r.count === "number") failCount += r.count;
1984
+ }
1985
+ }
1986
+ shaped.totals = {
1987
+ messages: totalCount,
1988
+ aligned: passCount,
1989
+ notAligned: failCount,
1990
+ };
1991
+ return shaped;
1992
+ }
1993
+
1994
+ // ---- iprev (RFC 8601 §3) — Forward-Confirmed Reverse DNS verifier ----
1995
+ //
1996
+ // The receiving SMTP server reverse-resolves the connecting peer's IP
1997
+ // to a PTR name, forward-resolves the PTR name to an A or AAAA set,
1998
+ // and confirms the original IP appears in the forward set. Spoofed
1999
+ // PTR records (attacker controls the rDNS zone but not the forward
2000
+ // zone) fail this check and SHOULD be reflected in the
2001
+ // Authentication-Results header so downstream policies can react.
2002
+ //
2003
+ // Surface:
2004
+ // await b.mail.iprev.verify(ip)
2005
+ // → { result: "pass"|"fail"|"permerror"|"temperror",
2006
+ // ptr, forward, fcrdns, ip }
2007
+ //
2008
+ // Returns "permerror" on bad-shape input (not an IP literal); returns
2009
+ // "temperror" on ENODATA / ENOTFOUND / lookup failure (the receiver
2010
+ // retries on transient DNS faults). Pure-DNS — no operator state.
2011
+
2012
+ // RFC 8601 §3 — PTR result shape. The PTR rdata is an FQDN (1*labels).
2013
+ // Reject answers that aren't shaped as a DNS name: non-strings,
2014
+ // empty strings, strings containing chars outside DNS LDH+dot, or
2015
+ // labels exceeding 63 octets. An attacker who controls a reverse
2016
+ // zone could publish a PTR whose rdata is arbitrary bytes (e.g.
2017
+ // `<script>...`) that downstream consumers (audit / Authentication-
2018
+ // Results emission) might fail to escape. Pre-filter at the iprev
2019
+ // boundary so only well-shaped names reach downstream.
2020
+ function _isValidPtrName(name) {
2021
+ if (typeof name !== "string") return false;
2022
+ var trimmed = name.replace(/\.$/, "");
2023
+ if (trimmed.length === 0 || trimmed.length > 253) return false; // allow:raw-byte-literal — RFC 1035 hostname cap
2024
+ // Labels: 1..63 octets, LDH (letter / digit / hyphen) + leading
2025
+ // alphanum (RFC 1035 §2.3.1). Permissive: PTR rdata can in practice
2026
+ // contain underscores (mail-server idiom) — allow underscore in
2027
+ // labels too. Reject anything else.
2028
+ var labels = trimmed.split(".");
2029
+ for (var i = 0; i < labels.length; i += 1) {
2030
+ var lab = labels[i];
2031
+ if (lab.length === 0 || lab.length > 63) return false; // allow:raw-byte-literal — RFC 1035 label cap
2032
+ if (!/^[A-Za-z0-9_](?:[A-Za-z0-9_-]{0,61}[A-Za-z0-9_])?$/.test(lab)) return false;
2033
+ }
2034
+ return true;
2035
+ }
2036
+
2037
+ async function iprevVerify(ip) {
2038
+ if (typeof ip !== "string" || ip.length === 0) {
2039
+ return { result: "permerror", ip: ip || null,
2040
+ ptr: null, forward: [], fcrdns: false,
2041
+ explanation: "ip must be a non-empty string" };
2042
+ }
2043
+ if (!net.isIP(ip)) {
2044
+ return { result: "permerror", ip: ip,
2045
+ ptr: null, forward: [], fcrdns: false,
2046
+ explanation: "ip is not a valid IPv4 / IPv6 literal" };
2047
+ }
2048
+
2049
+ var ptrs;
2050
+ try { ptrs = await _safeReverse(ip); }
2051
+ catch (e) {
2052
+ var rcode = e && e.code;
2053
+ if (rcode === "ENOTFOUND" || rcode === "ENODATA") {
2054
+ return { result: "fail", ip: ip,
2055
+ ptr: null, forward: [], fcrdns: false,
2056
+ explanation: "no PTR record for " + ip };
2057
+ }
2058
+ return { result: "temperror", ip: ip,
2059
+ ptr: null, forward: [], fcrdns: false,
2060
+ explanation: "PTR lookup failed: " + ((e && e.message) || String(e)) };
2061
+ }
2062
+ if (!Array.isArray(ptrs) || ptrs.length === 0) {
2063
+ return { result: "fail", ip: ip,
2064
+ ptr: null, forward: [], fcrdns: false,
2065
+ explanation: "PTR returned empty answer set" };
2066
+ }
2067
+
2068
+ // RFC 8601 §3 — when multiple PTRs exist the receiver picks ONE
2069
+ // and continues. We pick the first (matches mainstream MTA
2070
+ // behavior) and stash the rest for operator visibility on the
2071
+ // out-of-band metadata. Validate the PTR's shape FIRST — a PTR
2072
+ // with arbitrary bytes shouldn't reach downstream consumers.
2073
+ var ptr = String(ptrs[0]).replace(/\.$/, "");
2074
+ if (!_isValidPtrName(ptr)) {
2075
+ return { result: "permerror", ip: ip,
2076
+ ptr: ptr, forward: [], fcrdns: false,
2077
+ explanation: "PTR record is not a valid DNS name shape (RFC 8601 §3)" };
2078
+ }
2079
+ var isV6 = net.isIPv6(ip);
2080
+ var forwardAddrs;
2081
+ try {
2082
+ forwardAddrs = await _safeResolveA(ptr, isV6 ? 6 : 4);
2083
+ } catch (e) {
2084
+ var fcode = e && e.code;
2085
+ if (fcode === "ENOTFOUND" || fcode === "ENODATA") {
2086
+ return { result: "fail", ip: ip,
2087
+ ptr: ptr, forward: [], fcrdns: false,
2088
+ explanation: "no forward record for PTR " + ptr };
2089
+ }
2090
+ if (fcode === "ETIMEOUT" || fcode === "ESERVFAIL") {
2091
+ return { result: "temperror", ip: ip,
2092
+ ptr: ptr, forward: [], fcrdns: false,
2093
+ explanation: "forward lookup transient failure: " + fcode };
2094
+ }
2095
+ // Anything else — propagate as temperror; Node DNS surfaces some
2096
+ // non-RFC error codes via the platform resolver. Permerror only
2097
+ // for definitive negative answers above.
2098
+ throw new MailAuthError("mail-auth/iprev-temperror",
2099
+ "iprev.verify: forward lookup of " + ptr + " threw: " +
2100
+ ((e && e.message) || String(e)));
2101
+ }
2102
+ var forward = Array.isArray(forwardAddrs) ? forwardAddrs.slice() : [];
2103
+ var ipLc = ip.toLowerCase();
2104
+ var fcrdns = false;
2105
+ for (var i = 0; i < forward.length; i += 1) {
2106
+ if (String(forward[i]).toLowerCase() === ipLc) { fcrdns = true; break; }
2107
+ }
2108
+ return {
2109
+ result: fcrdns ? "pass" : "fail",
2110
+ ip: ip,
2111
+ ptr: ptr,
2112
+ forward: forward,
2113
+ fcrdns: fcrdns,
2114
+ explanation: fcrdns
2115
+ ? "PTR " + ptr + " forward-resolves to " + ip
2116
+ : "PTR " + ptr + " does not forward-resolve to " + ip,
2117
+ };
2118
+ }
2119
+
2120
+ module.exports = {
2121
+ spf: Object.freeze({
2122
+ verify: spfVerify,
2123
+ parseRecord: _parseSpfRecord,
2124
+ }),
2125
+ dmarc: Object.freeze({
2126
+ evaluate: dmarcEvaluate,
2127
+ parseRecord: _parseDmarcRecord,
2128
+ parseAggregateReport: dmarcParseAggregateReport,
2129
+ }),
2130
+ arc: Object.freeze({
2131
+ verify: arcVerify,
2132
+ evaluate: arcEvaluate,
2133
+ sign: require("./mail-arc-sign").sign, // allow:inline-require — re-export from sibling module
2134
+ ALLOWED_CV: require("./mail-arc-sign").ALLOWED_CV, // allow:inline-require — re-export from sibling module
2135
+ }),
2136
+ iprev: Object.freeze({
2137
+ verify: iprevVerify,
2138
+ }),
2139
+ authResults: Object.freeze({
2140
+ emit: authResultsEmit,
2141
+ }),
2142
+ MailAuthError: MailAuthError,
2143
+ SPF_DNS_LOOKUP_LIMIT: SPF_DNS_LOOKUP_LIMIT,
2144
+ };