@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.
- package/CHANGELOG.md +87 -0
- package/LICENSE +17 -0
- package/README.md +117 -0
- package/SECURITY.md +139 -0
- package/lib/admin.js +952 -0
- package/lib/analytics.js +267 -0
- package/lib/cart.js +279 -0
- package/lib/catalog-import.js +344 -0
- package/lib/catalog.js +769 -0
- package/lib/checkout.js +320 -0
- package/lib/config.js +151 -0
- package/lib/customers.js +322 -0
- package/lib/email.js +242 -0
- package/lib/externaldb-d1.js +283 -0
- package/lib/index.js +57 -0
- package/lib/inventory-alerts.js +198 -0
- package/lib/newsletter.js +142 -0
- package/lib/order.js +380 -0
- package/lib/payment.js +318 -0
- package/lib/pricing.js +185 -0
- package/lib/r2-bridge.js +169 -0
- package/lib/shipping.js +185 -0
- package/lib/storefront.js +2160 -0
- package/lib/subscriptions.js +410 -0
- package/lib/tax.js +161 -0
- package/lib/theme.js +194 -0
- package/lib/vendor/MANIFEST.json +19 -0
- package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +23 -0
- package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +34 -0
- package/lib/vendor/blamejs/.clusterfuzzlite/project.yaml +16 -0
- package/lib/vendor/blamejs/.dockerignore +45 -0
- package/lib/vendor/blamejs/.gitattributes +42 -0
- package/lib/vendor/blamejs/.github/CODEOWNERS +4 -0
- package/lib/vendor/blamejs/.github/FUNDING.yml +2 -0
- package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/bug_report.md +58 -0
- package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/feature_request.md +99 -0
- package/lib/vendor/blamejs/.github/PULL_REQUEST_TEMPLATE.md +77 -0
- package/lib/vendor/blamejs/.github/dependabot.yml +37 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +148 -0
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +107 -0
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +122 -0
- package/lib/vendor/blamejs/.github/workflows/ci.yml +511 -0
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +50 -0
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +655 -0
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +406 -0
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +101 -0
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +134 -0
- package/lib/vendor/blamejs/.gitignore +102 -0
- package/lib/vendor/blamejs/.gitleaks.toml +166 -0
- package/lib/vendor/blamejs/.hadolint.yaml +18 -0
- package/lib/vendor/blamejs/.npmrc +5 -0
- package/lib/vendor/blamejs/.pinact.yaml +17 -0
- package/lib/vendor/blamejs/ARCHITECTURE.md +158 -0
- package/lib/vendor/blamejs/CHANGELOG.md +1351 -0
- package/lib/vendor/blamejs/CODE_OF_CONDUCT.md +86 -0
- package/lib/vendor/blamejs/CONTRIBUTING.md +156 -0
- package/lib/vendor/blamejs/GOVERNANCE.md +201 -0
- package/lib/vendor/blamejs/LICENSE +201 -0
- package/lib/vendor/blamejs/LTS-CALENDAR.md +29 -0
- package/lib/vendor/blamejs/MIGRATING.md +29 -0
- package/lib/vendor/blamejs/NOTICE +81 -0
- package/lib/vendor/blamejs/README.md +304 -0
- package/lib/vendor/blamejs/SECURITY.md +432 -0
- package/lib/vendor/blamejs/api-snapshot.json +48709 -0
- package/lib/vendor/blamejs/assets/BlameJS_Logo.png +0 -0
- package/lib/vendor/blamejs/assets/BlameJS_Logo.svg +129 -0
- package/lib/vendor/blamejs/bench/README.md +77 -0
- package/lib/vendor/blamejs/bench/_helpers.js +70 -0
- package/lib/vendor/blamejs/bench/baseline.json +183 -0
- package/lib/vendor/blamejs/bench/crypto-hash.bench.js +19 -0
- package/lib/vendor/blamejs/bench/crypto-symmetric.bench.js +28 -0
- package/lib/vendor/blamejs/bench/run.js +140 -0
- package/lib/vendor/blamejs/bench/safe-json.bench.js +31 -0
- package/lib/vendor/blamejs/bin/blamejs.js +13 -0
- package/lib/vendor/blamejs/docker/caddy/Caddyfile +46 -0
- package/lib/vendor/blamejs/docker/coredns/Corefile +37 -0
- package/lib/vendor/blamejs/docker/haproxy/haproxy.cfg +52 -0
- package/lib/vendor/blamejs/docker/init/generate-certs.sh +118 -0
- package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +87 -0
- package/lib/vendor/blamejs/docker/mitmproxy/config.yaml +16 -0
- package/lib/vendor/blamejs/docker/mongo/init-tls.sh +17 -0
- package/lib/vendor/blamejs/docker/mysql/my.cnf +12 -0
- package/lib/vendor/blamejs/docker/nats/nats.conf +33 -0
- package/lib/vendor/blamejs/docker/postgres/init-tls.sh +17 -0
- package/lib/vendor/blamejs/docker/postgres/postgresql.conf +18 -0
- package/lib/vendor/blamejs/docker/rabbitmq/rabbitmq.conf +18 -0
- package/lib/vendor/blamejs/docker/redis/redis.conf +15 -0
- package/lib/vendor/blamejs/docker/squid/squid.conf +24 -0
- package/lib/vendor/blamejs/docker/syslog/syslog-ng.conf +34 -0
- package/lib/vendor/blamejs/docker-compose.test.yml +545 -0
- package/lib/vendor/blamejs/docs/cis-postgres-crosswalk.md +102 -0
- package/lib/vendor/blamejs/docs/cis-sqlite-equivalent.md +92 -0
- package/lib/vendor/blamejs/eslint.config.mjs +204 -0
- package/lib/vendor/blamejs/examples/wiki/Caddyfile +40 -0
- package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +218 -0
- package/lib/vendor/blamejs/examples/wiki/Dockerfile +120 -0
- package/lib/vendor/blamejs/examples/wiki/README.md +157 -0
- package/lib/vendor/blamejs/examples/wiki/cli-snapshot.json +250 -0
- package/lib/vendor/blamejs/examples/wiki/docker-compose.prod.yml +231 -0
- package/lib/vendor/blamejs/examples/wiki/docker-compose.yml +166 -0
- package/lib/vendor/blamejs/examples/wiki/env-snapshot.json +217 -0
- package/lib/vendor/blamejs/examples/wiki/lib/auto-site-entries.js +139 -0
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +555 -0
- package/lib/vendor/blamejs/examples/wiki/lib/harvest-cli.js +507 -0
- package/lib/vendor/blamejs/examples/wiki/lib/harvest-env-vars.js +435 -0
- package/lib/vendor/blamejs/examples/wiki/lib/harvest-errors.js +282 -0
- package/lib/vendor/blamejs/examples/wiki/lib/harvest-vendored-deps.js +321 -0
- package/lib/vendor/blamejs/examples/wiki/lib/nav.js +15 -0
- package/lib/vendor/blamejs/examples/wiki/lib/opts-resolver.js +75 -0
- package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +508 -0
- package/lib/vendor/blamejs/examples/wiki/lib/section.js +276 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +587 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +318 -0
- package/lib/vendor/blamejs/examples/wiki/lib/symbol-index.js +122 -0
- package/lib/vendor/blamejs/examples/wiki/migrations/0001-pages-schema.js +74 -0
- package/lib/vendor/blamejs/examples/wiki/package.json +18 -0
- package/lib/vendor/blamejs/examples/wiki/public/img/blamejs-logo.png +0 -0
- package/lib/vendor/blamejs/examples/wiki/public/img/blamejs-logo.svg +129 -0
- package/lib/vendor/blamejs/examples/wiki/public/robots.txt +5 -0
- package/lib/vendor/blamejs/examples/wiki/public/vendor/MANIFEST.json +30 -0
- package/lib/vendor/blamejs/examples/wiki/public/vendor/prism.css +1 -0
- package/lib/vendor/blamejs/examples/wiki/public/vendor/prism.js +15 -0
- package/lib/vendor/blamejs/examples/wiki/public/wiki.css +1250 -0
- package/lib/vendor/blamejs/examples/wiki/routes/admin.js +366 -0
- package/lib/vendor/blamejs/examples/wiki/routes/integration.js +230 -0
- package/lib/vendor/blamejs/examples/wiki/routes/pages.js +266 -0
- package/lib/vendor/blamejs/examples/wiki/scripts/backfill-module-metadata.js +214 -0
- package/lib/vendor/blamejs/examples/wiki/seeders/prod/0001-default-pages.js +35 -0
- package/lib/vendor/blamejs/examples/wiki/seeders/prod/pages/_index.js +34 -0
- package/lib/vendor/blamejs/examples/wiki/seeders/prod/pages/api.js +76 -0
- package/lib/vendor/blamejs/examples/wiki/server.js +129 -0
- package/lib/vendor/blamejs/examples/wiki/site.config.js +197 -0
- package/lib/vendor/blamejs/examples/wiki/snippets/README.md +38 -0
- package/lib/vendor/blamejs/examples/wiki/snippets/auth/password-hash.example.js +15 -0
- package/lib/vendor/blamejs/examples/wiki/src/editor.js +103 -0
- package/lib/vendor/blamejs/examples/wiki/src/wiki.js +349 -0
- package/lib/vendor/blamejs/examples/wiki/test/AUDIT.md +155 -0
- package/lib/vendor/blamejs/examples/wiki/test/codebase-patterns.test.js +594 -0
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +741 -0
- package/lib/vendor/blamejs/examples/wiki/test/find-missing-pages.js +254 -0
- package/lib/vendor/blamejs/examples/wiki/test/integration.js +391 -0
- package/lib/vendor/blamejs/examples/wiki/test/validate-cli-snapshot.js +379 -0
- package/lib/vendor/blamejs/examples/wiki/test/validate-env-snapshot.js +346 -0
- package/lib/vendor/blamejs/examples/wiki/test/validate-nav-coverage.js +212 -0
- package/lib/vendor/blamejs/examples/wiki/test/validate-site-coverage.js +252 -0
- package/lib/vendor/blamejs/examples/wiki/test/validate-source-comment-blocks.js +107 -0
- package/lib/vendor/blamejs/examples/wiki/views/_layout.html +115 -0
- package/lib/vendor/blamejs/examples/wiki/views/admin/api-keys.html +51 -0
- package/lib/vendor/blamejs/examples/wiki/views/admin/dashboard.html +22 -0
- package/lib/vendor/blamejs/examples/wiki/views/admin/edit.html +17 -0
- package/lib/vendor/blamejs/examples/wiki/views/home.html +85 -0
- package/lib/vendor/blamejs/examples/wiki/views/login.html +18 -0
- package/lib/vendor/blamejs/examples/wiki/views/page.html +5 -0
- package/lib/vendor/blamejs/examples/wiki/views/partials/nav.html +13 -0
- package/lib/vendor/blamejs/examples/wiki/views/search.html +19 -0
- package/lib/vendor/blamejs/examples/wiki/wiki.config.js +15 -0
- package/lib/vendor/blamejs/fuzz/README.md +137 -0
- package/lib/vendor/blamejs/fuzz/_expected.js +35 -0
- package/lib/vendor/blamejs/fuzz/guard-agent-registry.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-csv.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/01-basic.csv +3 -0
- package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/02-formula.csv +1 -0
- package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/03-hyperlink.csv +1 -0
- package/lib/vendor/blamejs/fuzz/guard-dsn.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-email.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-email_seed_corpus/01-basic.eml +5 -0
- package/lib/vendor/blamejs/fuzz/guard-envelope.fuzz.js +24 -0
- package/lib/vendor/blamejs/fuzz/guard-event-bus-payload.fuzz.js +24 -0
- package/lib/vendor/blamejs/fuzz/guard-event-bus-topic.fuzz.js +20 -0
- package/lib/vendor/blamejs/fuzz/guard-html.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/01-basic.html +1 -0
- package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/02-script.html +1 -0
- package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/03-event.html +1 -0
- package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/04-jsurl.html +1 -0
- package/lib/vendor/blamejs/fuzz/guard-idempotency-key.fuzz.js +20 -0
- package/lib/vendor/blamejs/fuzz/guard-imap-command.fuzz.js +35 -0
- package/lib/vendor/blamejs/fuzz/guard-jmap.fuzz.js +41 -0
- package/lib/vendor/blamejs/fuzz/guard-json.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/01-basic.json +1 -0
- package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/02-proto.json +1 -0
- package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/03-dupkey.json +1 -0
- package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/04-nan.json +1 -0
- package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/05-bom.json +1 -0
- package/lib/vendor/blamejs/fuzz/guard-list-id.fuzz.js +21 -0
- package/lib/vendor/blamejs/fuzz/guard-list-unsubscribe.fuzz.js +25 -0
- package/lib/vendor/blamejs/fuzz/guard-mail-compose.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-mail-move.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-mail-query.fuzz.js +27 -0
- package/lib/vendor/blamejs/fuzz/guard-mail-reply.fuzz.js +23 -0
- package/lib/vendor/blamejs/fuzz/guard-mail-sieve.fuzz.js +36 -0
- package/lib/vendor/blamejs/fuzz/guard-managesieve-command.fuzz.js +26 -0
- package/lib/vendor/blamejs/fuzz/guard-markdown.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/01-basic.md +2 -0
- package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/02-jsurl.md +1 -0
- package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/03-jsimg.md +1 -0
- package/lib/vendor/blamejs/fuzz/guard-message-id.fuzz.js +26 -0
- package/lib/vendor/blamejs/fuzz/guard-pop3-command.fuzz.js +23 -0
- package/lib/vendor/blamejs/fuzz/guard-posture-chain.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-saga-config.fuzz.js +32 -0
- package/lib/vendor/blamejs/fuzz/guard-smtp-command.fuzz.js +27 -0
- package/lib/vendor/blamejs/fuzz/guard-snapshot-envelope.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-stream-args.fuzz.js +22 -0
- package/lib/vendor/blamejs/fuzz/guard-svg.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-svg_seed_corpus/01-basic.svg +1 -0
- package/lib/vendor/blamejs/fuzz/guard-svg_seed_corpus/02-script.svg +1 -0
- package/lib/vendor/blamejs/fuzz/guard-tenant-id.fuzz.js +20 -0
- package/lib/vendor/blamejs/fuzz/guard-trace-context.fuzz.js +30 -0
- package/lib/vendor/blamejs/fuzz/guard-xml.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-xml_seed_corpus/01-basic.xml +1 -0
- package/lib/vendor/blamejs/fuzz/guard-xml_seed_corpus/02-xxe.xml +1 -0
- package/lib/vendor/blamejs/fuzz/guard-yaml.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/01-basic.yaml +2 -0
- package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/02-anchor.yaml +2 -0
- package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/03-norway.yaml +1 -0
- package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/04-multidoc.yaml +4 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-ini.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-ini_seed_corpus/01-basic.ini +2 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-toml.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-toml_seed_corpus/01-basic.toml +4 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-xml.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-xml_seed_corpus/01-basic.xml +1 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-yaml.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/parsers__safe-yaml_seed_corpus/01-basic.yaml +4 -0
- package/lib/vendor/blamejs/fuzz/safe-decompress.fuzz.js +49 -0
- package/lib/vendor/blamejs/fuzz/safe-dns.fuzz.js +29 -0
- package/lib/vendor/blamejs/fuzz/safe-ical.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/safe-icap.fuzz.js +42 -0
- package/lib/vendor/blamejs/fuzz/safe-json.fuzz.js +25 -0
- package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/01-object.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/02-array.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/03-string.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/04-proto.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/05-deep.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-jsonpath.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/01-basic.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/02-filter.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/03-deepscan.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/04-slice.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-mime.fuzz.js +27 -0
- package/lib/vendor/blamejs/fuzz/safe-mount-info.fuzz.js +33 -0
- package/lib/vendor/blamejs/fuzz/safe-sieve.fuzz.js +28 -0
- package/lib/vendor/blamejs/fuzz/safe-smtp.fuzz.js +64 -0
- package/lib/vendor/blamejs/fuzz/safe-url.fuzz.js +16 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/01-basic.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/02-userinfo.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/03-dangerous.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/04-data.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/05-ipv6.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/06-idn.txt +1 -0
- package/lib/vendor/blamejs/fuzz/safe-vcard.fuzz.js +16 -0
- package/lib/vendor/blamejs/index.js +678 -0
- package/lib/vendor/blamejs/keys/release-pqc-pub.json +7 -0
- package/lib/vendor/blamejs/lib/_test/crypto-fixtures.js +67 -0
- package/lib/vendor/blamejs/lib/a2a-tasks.js +598 -0
- package/lib/vendor/blamejs/lib/a2a.js +407 -0
- package/lib/vendor/blamejs/lib/acme.js +1448 -0
- package/lib/vendor/blamejs/lib/agent-audit.js +45 -0
- package/lib/vendor/blamejs/lib/agent-event-bus.js +382 -0
- package/lib/vendor/blamejs/lib/agent-idempotency.js +497 -0
- package/lib/vendor/blamejs/lib/agent-orchestrator.js +717 -0
- package/lib/vendor/blamejs/lib/agent-posture-chain.js +366 -0
- package/lib/vendor/blamejs/lib/agent-saga.js +321 -0
- package/lib/vendor/blamejs/lib/agent-snapshot.js +676 -0
- package/lib/vendor/blamejs/lib/agent-stream.js +269 -0
- package/lib/vendor/blamejs/lib/agent-tenant.js +632 -0
- package/lib/vendor/blamejs/lib/agent-trace.js +281 -0
- package/lib/vendor/blamejs/lib/ai-adverse-decision.js +184 -0
- package/lib/vendor/blamejs/lib/ai-content-detect.js +268 -0
- package/lib/vendor/blamejs/lib/ai-input.js +201 -0
- package/lib/vendor/blamejs/lib/ai-model-manifest.js +363 -0
- package/lib/vendor/blamejs/lib/ai-pref.js +340 -0
- package/lib/vendor/blamejs/lib/api-key.js +721 -0
- package/lib/vendor/blamejs/lib/api-snapshot.js +458 -0
- package/lib/vendor/blamejs/lib/app-shutdown.js +557 -0
- package/lib/vendor/blamejs/lib/app.js +365 -0
- package/lib/vendor/blamejs/lib/archive.js +547 -0
- package/lib/vendor/blamejs/lib/arg-parser.js +697 -0
- package/lib/vendor/blamejs/lib/argon2-builtin.js +173 -0
- package/lib/vendor/blamejs/lib/asn1-der.js +424 -0
- package/lib/vendor/blamejs/lib/asyncapi-bindings.js +160 -0
- package/lib/vendor/blamejs/lib/asyncapi-traits.js +143 -0
- package/lib/vendor/blamejs/lib/asyncapi.js +575 -0
- package/lib/vendor/blamejs/lib/atomic-file.js +1023 -0
- package/lib/vendor/blamejs/lib/audit-chain.js +266 -0
- package/lib/vendor/blamejs/lib/audit-daily-review.js +389 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +751 -0
- package/lib/vendor/blamejs/lib/audit-tools.js +1113 -0
- package/lib/vendor/blamejs/lib/audit.js +1671 -0
- package/lib/vendor/blamejs/lib/auth/aal.js +169 -0
- package/lib/vendor/blamejs/lib/auth/access-lock.js +220 -0
- package/lib/vendor/blamejs/lib/auth/acr-vocabulary.js +265 -0
- package/lib/vendor/blamejs/lib/auth/ato-kill-switch.js +112 -0
- package/lib/vendor/blamejs/lib/auth/auth-time-tracker.js +111 -0
- package/lib/vendor/blamejs/lib/auth/bot-challenge.js +573 -0
- package/lib/vendor/blamejs/lib/auth/ciba.js +637 -0
- package/lib/vendor/blamejs/lib/auth/dpop.js +516 -0
- package/lib/vendor/blamejs/lib/auth/elevation-grant.js +306 -0
- package/lib/vendor/blamejs/lib/auth/fal.js +229 -0
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +681 -0
- package/lib/vendor/blamejs/lib/auth/jwt-external.js +519 -0
- package/lib/vendor/blamejs/lib/auth/jwt.js +430 -0
- package/lib/vendor/blamejs/lib/auth/lockout.js +449 -0
- package/lib/vendor/blamejs/lib/auth/oauth.js +2141 -0
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +657 -0
- package/lib/vendor/blamejs/lib/auth/oid4vp.js +531 -0
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +600 -0
- package/lib/vendor/blamejs/lib/auth/passkey.js +676 -0
- package/lib/vendor/blamejs/lib/auth/password.js +693 -0
- package/lib/vendor/blamejs/lib/auth/saml.js +2109 -0
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-disclosure.js +95 -0
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +225 -0
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-issuer.js +197 -0
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +728 -0
- package/lib/vendor/blamejs/lib/auth/status-list.js +272 -0
- package/lib/vendor/blamejs/lib/auth/step-up-policy.js +335 -0
- package/lib/vendor/blamejs/lib/auth/step-up.js +454 -0
- package/lib/vendor/blamejs/lib/auth-bot-challenge.js +505 -0
- package/lib/vendor/blamejs/lib/auth-header.js +148 -0
- package/lib/vendor/blamejs/lib/backup/bundle.js +265 -0
- package/lib/vendor/blamejs/lib/backup/crypto.js +176 -0
- package/lib/vendor/blamejs/lib/backup/index.js +1001 -0
- package/lib/vendor/blamejs/lib/backup/manifest.js +443 -0
- package/lib/vendor/blamejs/lib/boot-gates.js +174 -0
- package/lib/vendor/blamejs/lib/breach-deadline.js +272 -0
- package/lib/vendor/blamejs/lib/break-glass.js +1753 -0
- package/lib/vendor/blamejs/lib/budr.js +205 -0
- package/lib/vendor/blamejs/lib/bundler.js +461 -0
- package/lib/vendor/blamejs/lib/cache-redis.js +256 -0
- package/lib/vendor/blamejs/lib/cache-status.js +288 -0
- package/lib/vendor/blamejs/lib/cache.js +1331 -0
- package/lib/vendor/blamejs/lib/calendar.js +1240 -0
- package/lib/vendor/blamejs/lib/canonical-json.js +143 -0
- package/lib/vendor/blamejs/lib/cdn-cache-control.js +473 -0
- package/lib/vendor/blamejs/lib/cert.js +763 -0
- package/lib/vendor/blamejs/lib/chain-writer.js +259 -0
- package/lib/vendor/blamejs/lib/circuit-breaker.js +101 -0
- package/lib/vendor/blamejs/lib/cli-helpers.js +237 -0
- package/lib/vendor/blamejs/lib/cli.js +2328 -0
- package/lib/vendor/blamejs/lib/client-hints.js +318 -0
- package/lib/vendor/blamejs/lib/cloud-events.js +277 -0
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +317 -0
- package/lib/vendor/blamejs/lib/cluster-storage.js +351 -0
- package/lib/vendor/blamejs/lib/cluster.js +1017 -0
- package/lib/vendor/blamejs/lib/cms-codec.js +826 -0
- package/lib/vendor/blamejs/lib/codepoint-class.js +262 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-logging.js +190 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +205 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-risk.js +189 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +200 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act.js +821 -0
- package/lib/vendor/blamejs/lib/compliance-eaa.js +204 -0
- package/lib/vendor/blamejs/lib/compliance-sanctions-aliases.js +167 -0
- package/lib/vendor/blamejs/lib/compliance-sanctions-fetcher.js +206 -0
- package/lib/vendor/blamejs/lib/compliance-sanctions-fuzzy.js +297 -0
- package/lib/vendor/blamejs/lib/compliance-sanctions.js +569 -0
- package/lib/vendor/blamejs/lib/compliance.js +1558 -0
- package/lib/vendor/blamejs/lib/config-drift.js +426 -0
- package/lib/vendor/blamejs/lib/config.js +446 -0
- package/lib/vendor/blamejs/lib/consent.js +369 -0
- package/lib/vendor/blamejs/lib/constants.js +209 -0
- package/lib/vendor/blamejs/lib/content-credentials.js +704 -0
- package/lib/vendor/blamejs/lib/cookies.js +560 -0
- package/lib/vendor/blamejs/lib/cra-report.js +299 -0
- package/lib/vendor/blamejs/lib/credential-hash.js +394 -0
- package/lib/vendor/blamejs/lib/crypto-field.js +1017 -0
- package/lib/vendor/blamejs/lib/crypto-hpke-pq.js +187 -0
- package/lib/vendor/blamejs/lib/crypto-hpke.js +256 -0
- package/lib/vendor/blamejs/lib/crypto.js +1908 -0
- package/lib/vendor/blamejs/lib/csp.js +271 -0
- package/lib/vendor/blamejs/lib/csv.js +418 -0
- package/lib/vendor/blamejs/lib/daemon.js +481 -0
- package/lib/vendor/blamejs/lib/dark-patterns.js +488 -0
- package/lib/vendor/blamejs/lib/data-act.js +328 -0
- package/lib/vendor/blamejs/lib/db-collection.js +587 -0
- package/lib/vendor/blamejs/lib/db-declare-row-policy.js +267 -0
- package/lib/vendor/blamejs/lib/db-declare-view.js +420 -0
- package/lib/vendor/blamejs/lib/db-file-lifecycle.js +333 -0
- package/lib/vendor/blamejs/lib/db-query.js +802 -0
- package/lib/vendor/blamejs/lib/db-role-context.js +50 -0
- package/lib/vendor/blamejs/lib/db-schema.js +322 -0
- package/lib/vendor/blamejs/lib/db.js +3111 -0
- package/lib/vendor/blamejs/lib/dbsc.js +299 -0
- package/lib/vendor/blamejs/lib/ddl-change-control.js +523 -0
- package/lib/vendor/blamejs/lib/deprecate.js +377 -0
- package/lib/vendor/blamejs/lib/dev.js +405 -0
- package/lib/vendor/blamejs/lib/dora.js +402 -0
- package/lib/vendor/blamejs/lib/dr-runbook.js +368 -0
- package/lib/vendor/blamejs/lib/dsr.js +1188 -0
- package/lib/vendor/blamejs/lib/dual-control.js +526 -0
- package/lib/vendor/blamejs/lib/early-hints.js +212 -0
- package/lib/vendor/blamejs/lib/error-page.js +420 -0
- package/lib/vendor/blamejs/lib/events.js +214 -0
- package/lib/vendor/blamejs/lib/external-db-migrate.js +659 -0
- package/lib/vendor/blamejs/lib/external-db.js +1877 -0
- package/lib/vendor/blamejs/lib/fapi2.js +394 -0
- package/lib/vendor/blamejs/lib/fda-21cfr11.js +395 -0
- package/lib/vendor/blamejs/lib/fdx.js +370 -0
- package/lib/vendor/blamejs/lib/fedcm.js +264 -0
- package/lib/vendor/blamejs/lib/file-type.js +360 -0
- package/lib/vendor/blamejs/lib/file-upload.js +1256 -0
- package/lib/vendor/blamejs/lib/flag-cache.js +136 -0
- package/lib/vendor/blamejs/lib/flag-evaluation-context.js +135 -0
- package/lib/vendor/blamejs/lib/flag-providers.js +279 -0
- package/lib/vendor/blamejs/lib/flag-targeting.js +210 -0
- package/lib/vendor/blamejs/lib/flag.js +346 -0
- package/lib/vendor/blamejs/lib/forms.js +525 -0
- package/lib/vendor/blamejs/lib/framework-error.js +724 -0
- package/lib/vendor/blamejs/lib/framework-schema.js +845 -0
- package/lib/vendor/blamejs/lib/framework-sha1-hibp.js +34 -0
- package/lib/vendor/blamejs/lib/fsm.js +469 -0
- package/lib/vendor/blamejs/lib/gate-contract.js +1661 -0
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +261 -0
- package/lib/vendor/blamejs/lib/graphql-federation.js +234 -0
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +179 -0
- package/lib/vendor/blamejs/lib/guard-all.js +555 -0
- package/lib/vendor/blamejs/lib/guard-archive.js +901 -0
- package/lib/vendor/blamejs/lib/guard-auth.js +451 -0
- package/lib/vendor/blamejs/lib/guard-cidr.js +676 -0
- package/lib/vendor/blamejs/lib/guard-csv.js +1176 -0
- package/lib/vendor/blamejs/lib/guard-domain.js +814 -0
- package/lib/vendor/blamejs/lib/guard-dsn.js +382 -0
- package/lib/vendor/blamejs/lib/guard-email.js +951 -0
- package/lib/vendor/blamejs/lib/guard-envelope.js +294 -0
- package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +217 -0
- package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +150 -0
- package/lib/vendor/blamejs/lib/guard-filename.js +956 -0
- package/lib/vendor/blamejs/lib/guard-graphql.js +731 -0
- package/lib/vendor/blamejs/lib/guard-html-wcag-aria.js +164 -0
- package/lib/vendor/blamejs/lib/guard-html-wcag-forms.js +144 -0
- package/lib/vendor/blamejs/lib/guard-html-wcag-tables.js +154 -0
- package/lib/vendor/blamejs/lib/guard-html-wcag-tagwalk.js +44 -0
- package/lib/vendor/blamejs/lib/guard-html-wcag.js +470 -0
- package/lib/vendor/blamejs/lib/guard-html.js +1209 -0
- package/lib/vendor/blamejs/lib/guard-idempotency-key.js +151 -0
- package/lib/vendor/blamejs/lib/guard-image.js +584 -0
- package/lib/vendor/blamejs/lib/guard-imap-command.js +337 -0
- package/lib/vendor/blamejs/lib/guard-jmap.js +321 -0
- package/lib/vendor/blamejs/lib/guard-json.js +935 -0
- package/lib/vendor/blamejs/lib/guard-jsonpath.js +512 -0
- package/lib/vendor/blamejs/lib/guard-jwt.js +772 -0
- package/lib/vendor/blamejs/lib/guard-list-id.js +318 -0
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +412 -0
- package/lib/vendor/blamejs/lib/guard-mail-compose.js +282 -0
- package/lib/vendor/blamejs/lib/guard-mail-move.js +202 -0
- package/lib/vendor/blamejs/lib/guard-mail-query.js +310 -0
- package/lib/vendor/blamejs/lib/guard-mail-reply.js +172 -0
- package/lib/vendor/blamejs/lib/guard-mail-sieve.js +207 -0
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +566 -0
- package/lib/vendor/blamejs/lib/guard-markdown.js +768 -0
- package/lib/vendor/blamejs/lib/guard-message-id.js +267 -0
- package/lib/vendor/blamejs/lib/guard-mime.js +609 -0
- package/lib/vendor/blamejs/lib/guard-oauth.js +650 -0
- package/lib/vendor/blamejs/lib/guard-pdf.js +569 -0
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +317 -0
- package/lib/vendor/blamejs/lib/guard-posture-chain.js +201 -0
- package/lib/vendor/blamejs/lib/guard-regex.js +632 -0
- package/lib/vendor/blamejs/lib/guard-saga-config.js +157 -0
- package/lib/vendor/blamejs/lib/guard-shell.js +522 -0
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +594 -0
- package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +168 -0
- package/lib/vendor/blamejs/lib/guard-stream-args.js +166 -0
- package/lib/vendor/blamejs/lib/guard-svg.js +1163 -0
- package/lib/vendor/blamejs/lib/guard-template.js +490 -0
- package/lib/vendor/blamejs/lib/guard-tenant-id.js +138 -0
- package/lib/vendor/blamejs/lib/guard-time.js +586 -0
- package/lib/vendor/blamejs/lib/guard-trace-context.js +172 -0
- package/lib/vendor/blamejs/lib/guard-uuid.js +548 -0
- package/lib/vendor/blamejs/lib/guard-xml.js +666 -0
- package/lib/vendor/blamejs/lib/guard-yaml.js +726 -0
- package/lib/vendor/blamejs/lib/hal.js +125 -0
- package/lib/vendor/blamejs/lib/handlers.js +350 -0
- package/lib/vendor/blamejs/lib/honeytoken.js +168 -0
- package/lib/vendor/blamejs/lib/html-balance.js +347 -0
- package/lib/vendor/blamejs/lib/http-client-cache.js +923 -0
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +519 -0
- package/lib/vendor/blamejs/lib/http-client.js +2152 -0
- package/lib/vendor/blamejs/lib/http-message-signature.js +589 -0
- package/lib/vendor/blamejs/lib/http2-teardown.js +34 -0
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +398 -0
- package/lib/vendor/blamejs/lib/i18n.js +931 -0
- package/lib/vendor/blamejs/lib/iab-mspa.js +257 -0
- package/lib/vendor/blamejs/lib/iab-tcf.js +461 -0
- package/lib/vendor/blamejs/lib/importmap-integrity.js +90 -0
- package/lib/vendor/blamejs/lib/inbox.js +435 -0
- package/lib/vendor/blamejs/lib/incident-report.js +314 -0
- package/lib/vendor/blamejs/lib/ip-utils.js +102 -0
- package/lib/vendor/blamejs/lib/jobs.js +185 -0
- package/lib/vendor/blamejs/lib/jose-jwe-experimental.js +228 -0
- package/lib/vendor/blamejs/lib/jsonapi.js +230 -0
- package/lib/vendor/blamejs/lib/keychain.js +865 -0
- package/lib/vendor/blamejs/lib/lazy-require.js +48 -0
- package/lib/vendor/blamejs/lib/legal-hold.js +374 -0
- package/lib/vendor/blamejs/lib/local-db-thin.js +321 -0
- package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +369 -0
- package/lib/vendor/blamejs/lib/log-stream-local.js +146 -0
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +410 -0
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +286 -0
- package/lib/vendor/blamejs/lib/log-stream-syslog.js +310 -0
- package/lib/vendor/blamejs/lib/log-stream-webhook.js +199 -0
- package/lib/vendor/blamejs/lib/log-stream.js +584 -0
- package/lib/vendor/blamejs/lib/log.js +625 -0
- package/lib/vendor/blamejs/lib/lro.js +200 -0
- package/lib/vendor/blamejs/lib/mail-agent.js +786 -0
- package/lib/vendor/blamejs/lib/mail-arc-sign.js +417 -0
- package/lib/vendor/blamejs/lib/mail-arf.js +343 -0
- package/lib/vendor/blamejs/lib/mail-auth.js +2144 -0
- package/lib/vendor/blamejs/lib/mail-bimi.js +1047 -0
- package/lib/vendor/blamejs/lib/mail-bounce.js +955 -0
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +1286 -0
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +789 -0
- package/lib/vendor/blamejs/lib/mail-crypto.js +108 -0
- package/lib/vendor/blamejs/lib/mail-dav.js +1224 -0
- package/lib/vendor/blamejs/lib/mail-deploy.js +1119 -0
- package/lib/vendor/blamejs/lib/mail-dkim.js +1250 -0
- package/lib/vendor/blamejs/lib/mail-greylist.js +448 -0
- package/lib/vendor/blamejs/lib/mail-helo.js +473 -0
- package/lib/vendor/blamejs/lib/mail-journal.js +435 -0
- package/lib/vendor/blamejs/lib/mail-mdn.js +424 -0
- package/lib/vendor/blamejs/lib/mail-rbl.js +392 -0
- package/lib/vendor/blamejs/lib/mail-require-tls.js +198 -0
- package/lib/vendor/blamejs/lib/mail-scan.js +502 -0
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +629 -0
- package/lib/vendor/blamejs/lib/mail-server-imap.js +1858 -0
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +1565 -0
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +908 -0
- package/lib/vendor/blamejs/lib/mail-server-mx.js +969 -0
- package/lib/vendor/blamejs/lib/mail-server-pop3.js +915 -0
- package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +315 -0
- package/lib/vendor/blamejs/lib/mail-server-registry.js +378 -0
- package/lib/vendor/blamejs/lib/mail-server-submission.js +1396 -0
- package/lib/vendor/blamejs/lib/mail-server-tls.js +445 -0
- package/lib/vendor/blamejs/lib/mail-sieve.js +557 -0
- package/lib/vendor/blamejs/lib/mail-spam-score.js +284 -0
- package/lib/vendor/blamejs/lib/mail-srs.js +248 -0
- package/lib/vendor/blamejs/lib/mail-store-fts.js +394 -0
- package/lib/vendor/blamejs/lib/mail-store.js +929 -0
- package/lib/vendor/blamejs/lib/mail-unsubscribe.js +400 -0
- package/lib/vendor/blamejs/lib/mail.js +1971 -0
- package/lib/vendor/blamejs/lib/mcp-tool-registry.js +473 -0
- package/lib/vendor/blamejs/lib/mcp.js +950 -0
- package/lib/vendor/blamejs/lib/metrics.js +1503 -0
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +177 -0
- package/lib/vendor/blamejs/lib/middleware/ai-act-disclosure.js +203 -0
- package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +981 -0
- package/lib/vendor/blamejs/lib/middleware/assetlinks.js +137 -0
- package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +171 -0
- package/lib/vendor/blamejs/lib/middleware/attach-user.js +220 -0
- package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +293 -0
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +1519 -0
- package/lib/vendor/blamejs/lib/middleware/bot-disclose.js +183 -0
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +217 -0
- package/lib/vendor/blamejs/lib/middleware/clear-site-data.js +122 -0
- package/lib/vendor/blamejs/lib/middleware/compose-pipeline.js +355 -0
- package/lib/vendor/blamejs/lib/middleware/compression.js +489 -0
- package/lib/vendor/blamejs/lib/middleware/cookies.js +130 -0
- package/lib/vendor/blamejs/lib/middleware/cors.js +386 -0
- package/lib/vendor/blamejs/lib/middleware/csp-nonce.js +388 -0
- package/lib/vendor/blamejs/lib/middleware/csp-report.js +167 -0
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +499 -0
- package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +243 -0
- package/lib/vendor/blamejs/lib/middleware/db-role-for.js +304 -0
- package/lib/vendor/blamejs/lib/middleware/dpop.js +402 -0
- package/lib/vendor/blamejs/lib/middleware/error-handler.js +69 -0
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +168 -0
- package/lib/vendor/blamejs/lib/middleware/flag-context.js +110 -0
- package/lib/vendor/blamejs/lib/middleware/gpc.js +153 -0
- package/lib/vendor/blamejs/lib/middleware/headers.js +242 -0
- package/lib/vendor/blamejs/lib/middleware/health.js +438 -0
- package/lib/vendor/blamejs/lib/middleware/host-allowlist.js +189 -0
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +964 -0
- package/lib/vendor/blamejs/lib/middleware/index.js +183 -0
- package/lib/vendor/blamejs/lib/middleware/nel.js +214 -0
- package/lib/vendor/blamejs/lib/middleware/network-allowlist.js +237 -0
- package/lib/vendor/blamejs/lib/middleware/no-cache.js +106 -0
- package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +177 -0
- package/lib/vendor/blamejs/lib/middleware/protected-resource-metadata.js +277 -0
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +556 -0
- package/lib/vendor/blamejs/lib/middleware/request-id.js +79 -0
- package/lib/vendor/blamejs/lib/middleware/request-log.js +205 -0
- package/lib/vendor/blamejs/lib/middleware/require-aal.js +138 -0
- package/lib/vendor/blamejs/lib/middleware/require-auth.js +144 -0
- package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +290 -0
- package/lib/vendor/blamejs/lib/middleware/require-content-type.js +113 -0
- package/lib/vendor/blamejs/lib/middleware/require-methods.js +97 -0
- package/lib/vendor/blamejs/lib/middleware/require-mtls.js +212 -0
- package/lib/vendor/blamejs/lib/middleware/require-step-up.js +226 -0
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +375 -0
- package/lib/vendor/blamejs/lib/middleware/security-headers.js +285 -0
- package/lib/vendor/blamejs/lib/middleware/security-txt.js +170 -0
- package/lib/vendor/blamejs/lib/middleware/span-http-server.js +280 -0
- package/lib/vendor/blamejs/lib/middleware/speculation-rules.js +323 -0
- package/lib/vendor/blamejs/lib/middleware/sse.js +200 -0
- package/lib/vendor/blamejs/lib/middleware/trace-log-correlation.js +167 -0
- package/lib/vendor/blamejs/lib/middleware/trace-propagate.js +148 -0
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +749 -0
- package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +164 -0
- package/lib/vendor/blamejs/lib/migration-files.js +37 -0
- package/lib/vendor/blamejs/lib/migrations.js +385 -0
- package/lib/vendor/blamejs/lib/mime-parse.js +198 -0
- package/lib/vendor/blamejs/lib/money.js +699 -0
- package/lib/vendor/blamejs/lib/mtls-ca.js +572 -0
- package/lib/vendor/blamejs/lib/mtls-engine-default.js +501 -0
- package/lib/vendor/blamejs/lib/network-byte-quota.js +308 -0
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +533 -0
- package/lib/vendor/blamejs/lib/network-dns.js +1930 -0
- package/lib/vendor/blamejs/lib/network-heartbeat.js +425 -0
- package/lib/vendor/blamejs/lib/network-nts.js +574 -0
- package/lib/vendor/blamejs/lib/network-proxy.js +265 -0
- package/lib/vendor/blamejs/lib/network-smtp-policy.js +836 -0
- package/lib/vendor/blamejs/lib/network-tls.js +3126 -0
- package/lib/vendor/blamejs/lib/network.js +346 -0
- package/lib/vendor/blamejs/lib/nis2-report.js +181 -0
- package/lib/vendor/blamejs/lib/nist-crosswalk.js +293 -0
- package/lib/vendor/blamejs/lib/nonce-store.js +177 -0
- package/lib/vendor/blamejs/lib/notify.js +683 -0
- package/lib/vendor/blamejs/lib/ntp-check.js +458 -0
- package/lib/vendor/blamejs/lib/numeric-bounds.js +111 -0
- package/lib/vendor/blamejs/lib/numeric-checks.js +40 -0
- package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +349 -0
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +488 -0
- package/lib/vendor/blamejs/lib/object-store/gcs-bucket-ops.js +351 -0
- package/lib/vendor/blamejs/lib/object-store/gcs.js +515 -0
- package/lib/vendor/blamejs/lib/object-store/http-put.js +153 -0
- package/lib/vendor/blamejs/lib/object-store/http-request.js +38 -0
- package/lib/vendor/blamejs/lib/object-store/index.js +197 -0
- package/lib/vendor/blamejs/lib/object-store/local.js +163 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +1133 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +957 -0
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +420 -0
- package/lib/vendor/blamejs/lib/observability-tracer.js +395 -0
- package/lib/vendor/blamejs/lib/observability.js +720 -0
- package/lib/vendor/blamejs/lib/openapi-paths-builder.js +248 -0
- package/lib/vendor/blamejs/lib/openapi-schema-walk.js +192 -0
- package/lib/vendor/blamejs/lib/openapi-security.js +169 -0
- package/lib/vendor/blamejs/lib/openapi-yaml.js +154 -0
- package/lib/vendor/blamejs/lib/openapi.js +489 -0
- package/lib/vendor/blamejs/lib/otel-export.js +278 -0
- package/lib/vendor/blamejs/lib/outbox.js +547 -0
- package/lib/vendor/blamejs/lib/pagination.js +542 -0
- package/lib/vendor/blamejs/lib/parsers/index.js +91 -0
- package/lib/vendor/blamejs/lib/parsers/safe-env.js +642 -0
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +293 -0
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +784 -0
- package/lib/vendor/blamejs/lib/parsers/safe-xml.js +390 -0
- package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +1015 -0
- package/lib/vendor/blamejs/lib/permissions.js +793 -0
- package/lib/vendor/blamejs/lib/pick.js +105 -0
- package/lib/vendor/blamejs/lib/pqc-agent.js +351 -0
- package/lib/vendor/blamejs/lib/pqc-gate.js +279 -0
- package/lib/vendor/blamejs/lib/pqc-software.js +271 -0
- package/lib/vendor/blamejs/lib/problem-details.js +482 -0
- package/lib/vendor/blamejs/lib/process-spawn.js +196 -0
- package/lib/vendor/blamejs/lib/promise-pool.js +162 -0
- package/lib/vendor/blamejs/lib/protobuf-encoder.js +190 -0
- package/lib/vendor/blamejs/lib/protocol-dispatcher.js +161 -0
- package/lib/vendor/blamejs/lib/public-suffix.js +403 -0
- package/lib/vendor/blamejs/lib/pubsub-cluster.js +154 -0
- package/lib/vendor/blamejs/lib/pubsub-redis.js +167 -0
- package/lib/vendor/blamejs/lib/pubsub.js +463 -0
- package/lib/vendor/blamejs/lib/queue-local.js +476 -0
- package/lib/vendor/blamejs/lib/queue-redis.js +745 -0
- package/lib/vendor/blamejs/lib/queue-sqs.js +319 -0
- package/lib/vendor/blamejs/lib/queue.js +1016 -0
- package/lib/vendor/blamejs/lib/redact.js +1007 -0
- package/lib/vendor/blamejs/lib/redis-client.js +520 -0
- package/lib/vendor/blamejs/lib/render.js +285 -0
- package/lib/vendor/blamejs/lib/request-helpers.js +767 -0
- package/lib/vendor/blamejs/lib/resource-access-lock.js +116 -0
- package/lib/vendor/blamejs/lib/restore-bundle.js +340 -0
- package/lib/vendor/blamejs/lib/restore-rollback.js +365 -0
- package/lib/vendor/blamejs/lib/restore.js +409 -0
- package/lib/vendor/blamejs/lib/retention.js +640 -0
- package/lib/vendor/blamejs/lib/retry.js +523 -0
- package/lib/vendor/blamejs/lib/router.js +1289 -0
- package/lib/vendor/blamejs/lib/safe-async.js +1184 -0
- package/lib/vendor/blamejs/lib/safe-buffer.js +562 -0
- package/lib/vendor/blamejs/lib/safe-decompress.js +297 -0
- package/lib/vendor/blamejs/lib/safe-dns.js +665 -0
- package/lib/vendor/blamejs/lib/safe-ical.js +634 -0
- package/lib/vendor/blamejs/lib/safe-icap.js +502 -0
- package/lib/vendor/blamejs/lib/safe-json.js +946 -0
- package/lib/vendor/blamejs/lib/safe-jsonpath.js +285 -0
- package/lib/vendor/blamejs/lib/safe-mime.js +831 -0
- package/lib/vendor/blamejs/lib/safe-mount-info.js +306 -0
- package/lib/vendor/blamejs/lib/safe-path.js +254 -0
- package/lib/vendor/blamejs/lib/safe-redirect.js +106 -0
- package/lib/vendor/blamejs/lib/safe-schema.js +1810 -0
- package/lib/vendor/blamejs/lib/safe-sieve.js +684 -0
- package/lib/vendor/blamejs/lib/safe-smtp.js +185 -0
- package/lib/vendor/blamejs/lib/safe-sql.js +363 -0
- package/lib/vendor/blamejs/lib/safe-url.js +428 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +473 -0
- package/lib/vendor/blamejs/lib/sandbox-worker.js +135 -0
- package/lib/vendor/blamejs/lib/sandbox.js +358 -0
- package/lib/vendor/blamejs/lib/scheduler.js +827 -0
- package/lib/vendor/blamejs/lib/sd-notify.js +269 -0
- package/lib/vendor/blamejs/lib/sec-cyber.js +214 -0
- package/lib/vendor/blamejs/lib/security-assert.js +395 -0
- package/lib/vendor/blamejs/lib/seeders.js +620 -0
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +309 -0
- package/lib/vendor/blamejs/lib/self-update.js +804 -0
- package/lib/vendor/blamejs/lib/server-timing.js +174 -0
- package/lib/vendor/blamejs/lib/session-device-binding.js +431 -0
- package/lib/vendor/blamejs/lib/session-stores.js +138 -0
- package/lib/vendor/blamejs/lib/session.js +1162 -0
- package/lib/vendor/blamejs/lib/slug.js +381 -0
- package/lib/vendor/blamejs/lib/sse.js +349 -0
- package/lib/vendor/blamejs/lib/ssrf-guard.js +792 -0
- package/lib/vendor/blamejs/lib/standard-webhooks.js +183 -0
- package/lib/vendor/blamejs/lib/static.js +1249 -0
- package/lib/vendor/blamejs/lib/storage.js +1272 -0
- package/lib/vendor/blamejs/lib/stream-throttle.js +235 -0
- package/lib/vendor/blamejs/lib/structured-fields.js +244 -0
- package/lib/vendor/blamejs/lib/subject.js +667 -0
- package/lib/vendor/blamejs/lib/tcpa-10dlc.js +175 -0
- package/lib/vendor/blamejs/lib/template.js +931 -0
- package/lib/vendor/blamejs/lib/tenant-quota.js +545 -0
- package/lib/vendor/blamejs/lib/test-harness.js +275 -0
- package/lib/vendor/blamejs/lib/testing.js +1185 -0
- package/lib/vendor/blamejs/lib/time.js +578 -0
- package/lib/vendor/blamejs/lib/tls-exporter.js +239 -0
- package/lib/vendor/blamejs/lib/totp.js +318 -0
- package/lib/vendor/blamejs/lib/tracing.js +546 -0
- package/lib/vendor/blamejs/lib/uuid.js +207 -0
- package/lib/vendor/blamejs/lib/validate-opts.js +381 -0
- package/lib/vendor/blamejs/lib/vault/index.js +638 -0
- package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +311 -0
- package/lib/vendor/blamejs/lib/vault/passphrase-source.js +198 -0
- package/lib/vendor/blamejs/lib/vault/rotate.js +803 -0
- package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +471 -0
- package/lib/vendor/blamejs/lib/vault/wrap.js +296 -0
- package/lib/vendor/blamejs/lib/vault-aad.js +259 -0
- package/lib/vendor/blamejs/lib/vendor/.vendor-data-pubkey +4 -0
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +161 -0
- package/lib/vendor/blamejs/lib/vendor/bimi-trust-anchors.data.js +68 -0
- package/lib/vendor/blamejs/lib/vendor/bimi-trust-anchors.pem +33 -0
- package/lib/vendor/blamejs/lib/vendor/common-passwords-top-10000.data.js +1325 -0
- package/lib/vendor/blamejs/lib/vendor/common-passwords-top-10000.txt +10002 -0
- package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +9 -0
- package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +18 -0
- package/lib/vendor/blamejs/lib/vendor/pki.cjs +181 -0
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +16382 -0
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5881 -0
- package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +328 -0
- package/lib/vendor/blamejs/lib/vendor/vendor-data-pubkey.js +16 -0
- package/lib/vendor/blamejs/lib/vendor-data.js +520 -0
- package/lib/vendor/blamejs/lib/vex.js +630 -0
- package/lib/vendor/blamejs/lib/watcher.js +608 -0
- package/lib/vendor/blamejs/lib/web-push-vapid.js +322 -0
- package/lib/vendor/blamejs/lib/webhook.js +977 -0
- package/lib/vendor/blamejs/lib/websocket-channels.js +327 -0
- package/lib/vendor/blamejs/lib/websocket.js +1561 -0
- package/lib/vendor/blamejs/lib/wiki-concepts.js +338 -0
- package/lib/vendor/blamejs/lib/worker-pool.js +464 -0
- package/lib/vendor/blamejs/lib/ws-client.js +978 -0
- package/lib/vendor/blamejs/lib/xml-c14n.js +506 -0
- package/lib/vendor/blamejs/memory/specs/node-26-map-getorinsert-migration.md +164 -0
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +19 -0
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/README.md +88 -0
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/build.sh +26 -0
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/project.yaml +28 -0
- package/lib/vendor/blamejs/package.json +81 -0
- package/lib/vendor/blamejs/release-notes/v0.0.x.json +310 -0
- package/lib/vendor/blamejs/release-notes/v0.1.x.json +1798 -0
- package/lib/vendor/blamejs/release-notes/v0.10.x.json +1288 -0
- package/lib/vendor/blamejs/release-notes/v0.11.x.json +2551 -0
- package/lib/vendor/blamejs/release-notes/v0.12.0.json +64 -0
- package/lib/vendor/blamejs/release-notes/v0.12.1.json +32 -0
- package/lib/vendor/blamejs/release-notes/v0.12.2.json +45 -0
- package/lib/vendor/blamejs/release-notes/v0.2.x.json +706 -0
- package/lib/vendor/blamejs/release-notes/v0.3.x.json +786 -0
- package/lib/vendor/blamejs/release-notes/v0.4.x.json +588 -0
- package/lib/vendor/blamejs/release-notes/v0.5.x.json +390 -0
- package/lib/vendor/blamejs/release-notes/v0.6.x.json +1947 -0
- package/lib/vendor/blamejs/release-notes/v0.7.x.json +3811 -0
- package/lib/vendor/blamejs/release-notes/v0.8.x.json +3318 -0
- package/lib/vendor/blamejs/release-notes/v0.9.x.json +2257 -0
- package/lib/vendor/blamejs/scripts/build-vendored-sbom.js +325 -0
- package/lib/vendor/blamejs/scripts/check-api-snapshot.js +62 -0
- package/lib/vendor/blamejs/scripts/check-changelog-extract.js +108 -0
- package/lib/vendor/blamejs/scripts/check-pack-against-gitignore.js +83 -0
- package/lib/vendor/blamejs/scripts/check-services.js +483 -0
- package/lib/vendor/blamejs/scripts/check-vendor-currency.js +349 -0
- package/lib/vendor/blamejs/scripts/consolidate-release-notes.js +216 -0
- package/lib/vendor/blamejs/scripts/gen-migrating.js +275 -0
- package/lib/vendor/blamejs/scripts/generate-changelog-entry.js +577 -0
- package/lib/vendor/blamejs/scripts/generate-release-signing-key.js +79 -0
- package/lib/vendor/blamejs/scripts/publish-dep-confusion-placeholder.sh +101 -0
- package/lib/vendor/blamejs/scripts/refresh-api-snapshot.js +31 -0
- package/lib/vendor/blamejs/scripts/refresh-vendor-manifest.js +132 -0
- package/lib/vendor/blamejs/scripts/release.js +652 -0
- package/lib/vendor/blamejs/scripts/sha3-digest.js +62 -0
- package/lib/vendor/blamejs/scripts/sign-release-artifact.js +92 -0
- package/lib/vendor/blamejs/scripts/test-integration.js +181 -0
- package/lib/vendor/blamejs/scripts/test-wiki-integration.js +126 -0
- package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +77 -0
- package/lib/vendor/blamejs/scripts/vendor-data-gen.js +186 -0
- package/lib/vendor/blamejs/scripts/vendor-data-keygen.js +101 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +278 -0
- package/lib/vendor/blamejs/test/00-primitives.js +19075 -0
- package/lib/vendor/blamejs/test/10-state.js +622 -0
- package/lib/vendor/blamejs/test/20-db.js +561 -0
- package/lib/vendor/blamejs/test/30-chain.js +2110 -0
- package/lib/vendor/blamejs/test/40-consumers.js +2453 -0
- package/lib/vendor/blamejs/test/50-integration.js +486 -0
- package/lib/vendor/blamejs/test/_helpers.js +10 -0
- package/lib/vendor/blamejs/test/_smoke-worker.js +69 -0
- package/lib/vendor/blamejs/test/fixtures/exploit-corpus/corpus.json +368 -0
- package/lib/vendor/blamejs/test/fixtures/http-client-stream-payload.txt +2 -0
- package/lib/vendor/blamejs/test/fixtures/worker-pool/echo.js +52 -0
- package/lib/vendor/blamejs/test/helpers/_codebase-shingle-worker.js +24 -0
- package/lib/vendor/blamejs/test/helpers/_codebase-shingle.js +203 -0
- package/lib/vendor/blamejs/test/helpers/_shape-match.js +513 -0
- package/lib/vendor/blamejs/test/helpers/check.js +36 -0
- package/lib/vendor/blamejs/test/helpers/cluster.js +70 -0
- package/lib/vendor/blamejs/test/helpers/db.js +143 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +207 -0
- package/lib/vendor/blamejs/test/helpers/fs-watch.js +101 -0
- package/lib/vendor/blamejs/test/helpers/http.js +14 -0
- package/lib/vendor/blamejs/test/helpers/index.js +93 -0
- package/lib/vendor/blamejs/test/helpers/json-round-trip.js +120 -0
- package/lib/vendor/blamejs/test/helpers/mocks.js +20 -0
- package/lib/vendor/blamejs/test/helpers/otel.js +13 -0
- package/lib/vendor/blamejs/test/helpers/services.js +380 -0
- package/lib/vendor/blamejs/test/helpers/wait.js +206 -0
- package/lib/vendor/blamejs/test/integration/cache.test.js +235 -0
- package/lib/vendor/blamejs/test/integration/cluster-provider-mysql.test.js +174 -0
- package/lib/vendor/blamejs/test/integration/federation-auth.test.js +611 -0
- package/lib/vendor/blamejs/test/integration/http-client.test.js +129 -0
- package/lib/vendor/blamejs/test/integration/log-stream.test.js +219 -0
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +181 -0
- package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +152 -0
- package/lib/vendor/blamejs/test/integration/mail-smtp.test.js +161 -0
- package/lib/vendor/blamejs/test/integration/mtls-ca.test.js +289 -0
- package/lib/vendor/blamejs/test/integration/network-dns.test.js +123 -0
- package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +101 -0
- package/lib/vendor/blamejs/test/integration/ntp-check.test.js +89 -0
- package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +403 -0
- package/lib/vendor/blamejs/test/integration/pqc-pkcs8-forward-compat.test.js +271 -0
- package/lib/vendor/blamejs/test/integration/pubsub.test.js +137 -0
- package/lib/vendor/blamejs/test/integration/queue-redis.test.js +352 -0
- package/lib/vendor/blamejs/test/integration/redis-client-tls.test.js +96 -0
- package/lib/vendor/blamejs/test/integration/ssrf-guard.test.js +98 -0
- package/lib/vendor/blamejs/test/integration/websocket-permessage-deflate.test.js +261 -0
- package/lib/vendor/blamejs/test/integration/ws-client-roundtrip.test.js +230 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +211 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a.test.js +59 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/access-lock.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +219 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +266 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +262 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-orchestrator.test.js +390 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-posture-chain.test.js +174 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +279 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +322 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-stream.test.js +227 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-tenant.test.js +302 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-trace.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-adverse-decision.test.js +44 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-content-detect.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-input.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-model-manifest.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ai-pref.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +1080 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +311 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-zip-stream.test.js +291 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +267 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +108 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asyncapi.test.js +929 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-conflict-path.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-cve-defensive.test.js +176 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-daily-review.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-export-cadf.test.js +97 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-framework-namespaces.test.js +141 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-segregation.test.js +115 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-ml-dsa-65.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-use-store.test.js +246 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge-verifier.test.js +485 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge.test.js +331 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +352 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-lockout.test.js +572 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-password-audit.test.js +61 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-bucket-ops.test.js +258 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-worker.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/bearer-auth.test.js +107 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-chunked-malformed.test.js +131 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-smuggling.test.js +118 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/boot-gates.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/breach-deadline.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +861 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/budr.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/bundler-engine.test.js +209 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache-status.test.js +129 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +871 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +891 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/canonical-json-jcs.test.js +43 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cdn-cache-control.test.js +243 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +550 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +107 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-api-key.test.js +147 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-audit-verify-chain.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-backup.test.js +135 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-config-drift.test.js +67 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +75 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-file-type.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-helpers.test.js +145 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-mtls.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-password.test.js +97 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-restore.test.js +160 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-retention.test.js +84 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-security.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-vault.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/client-hints.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +237 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +9600 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-cascade.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-eaa.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +712 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +278 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/config-drift.test.js +97 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/config.test.js +424 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cors.test.js +357 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cra-report.test.js +31 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-base64url.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-envelope.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +193 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hpke-pq.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hpke.test.js +155 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-mlkem768-x25519.test.js +129 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-namespace-hash.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-random-int.test.js +72 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-builder.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-nonce.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +180 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +210 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daily-byte-quota.test.js +153 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/data-act.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-collection-extensions.test.js +226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-collection.test.js +136 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-init-extensions.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query-extensions.test.js +191 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-role-for.test.js +228 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-vacuum.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +184 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/declare-row-policy.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/declare-view.test.js +303 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dns-dnssec-algorithm.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dns-null-mx.test.js +39 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dora.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dr-runbook.test.js +59 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr-state-rules.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +786 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/early-hints.test.js +147 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/events.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/exploit-replay.test.js +243 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-routing.test.js +531 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +118 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fapi2.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fda-21cfr11.test.js +156 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fdx.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +434 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +432 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/file-type.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +887 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/forensic-snapshot.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fsm.test.js +375 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gcs-bucket-ops.test.js +321 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +41 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-agent-registry.test.js +87 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-all.test.js +328 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +339 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +694 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-dsn.test.js +296 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +234 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-envelope.test.js +192 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-event-bus-payload.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-event-bus-topic.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +386 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html-wcag.test.js +859 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +357 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +174 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +317 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-list-id.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-list-unsubscribe.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-compose.test.js +111 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-move.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-query.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-reply.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-sieve.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +301 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +265 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-message-id.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +161 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-posture-chain.test.js +100 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-saga-config.test.js +79 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-smtp-command.test.js +269 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-snapshot-envelope.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-stream-args.test.js +78 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +288 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-tenant-id.test.js +69 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-trace-context.test.js +102 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-xml.test.js +202 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-yaml.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/hal.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/honeytoken.test.js +50 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +37 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +692 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +280 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +225 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +203 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +991 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/iab-mspa.test.js +63 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/iab-tcf.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +612 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/importmap-integrity.test.js +56 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +166 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/incident-report.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jose-jwe-experimental.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-api.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-round-trip-helper.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +159 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +118 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/local-db-thin.test.js +150 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-cloudwatch.test.js +489 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-otlp-grpc.test.js +207 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-otlp.test.js +283 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/lro.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +417 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-arf.test.js +208 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +910 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +502 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +680 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-canspam.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +297 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +514 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy-tlsrpt.test.js +369 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +627 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-feedback-id.test.js +56 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-greylist.test.js +217 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +283 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +217 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-mdn.test.js +334 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-rbl.test.js +271 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-require-tls.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +215 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +336 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +732 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +840 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +130 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +285 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +229 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +394 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-tls.test.js +147 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-spam-score.test.js +204 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-srs.test.js +152 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +279 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-unsubscribe.test.js +165 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +439 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp-tool-registry.test.js +202 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +155 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-shadow-registry.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +224 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-compose-pipeline.test.js +278 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +376 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-paths.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nel.test.js +200 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-allowlist.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-byte-quota.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +372 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +635 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-heartbeat-passive.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls-build-options.test.js +130 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls-ct-inclusion.test.js +179 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +447 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +369 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nis2-report.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nist-crosswalk.test.js +42 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/no-cache.test.js +98 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/notify.test.js +707 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/numeric-bounds.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +72 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +597 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/openapi.test.js +877 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +257 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pagination.test.js +522 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +216 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +324 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +546 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +153 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-software.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/problem-details.test.js +195 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/process-spawn.test.js +62 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/promise-pool.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/protected-resource-metadata.test.js +68 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/protobuf-encoder.test.js +138 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/protocol-dispatcher.test.js +174 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/public-suffix.test.js +197 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +232 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-dlq-extend-lease.test.js +178 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +322 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-priority-rate-progress.test.js +266 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-sqs.test.js +300 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-cluster.test.js +338 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-registry.test.js +75 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +246 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +130 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +335 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-log.test.js +170 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-auth-cache-control.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-mtls.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/resource-access-lock.test.js +52 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention-floor.test.js +67 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retry.test.js +535 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-cross-origin-redirect.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +128 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-loops.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-parallel.test.js +170 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +248 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +451 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +289 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-icap.test.js +206 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-jsonpath.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +339 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mount-info.test.js +180 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +78 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-sieve.test.js +123 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-smtp.test.js +95 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-idn-homograph.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +257 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/saml-slo.test.js +249 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +228 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-exactly-once.test.js +238 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +700 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-notify.test.js +67 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sec-cyber.test.js +85 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +107 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +175 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/seeders.test.js +816 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update-standalone-verifier.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +302 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/server-timing.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +247 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +295 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/shape-match.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +952 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-multipart-sse.test.js +441 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/slug.test.js +330 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/smtp-policy.test.js +233 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/source-comment-blocks.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/speculation-rules.test.js +319 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sse.test.js +148 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +283 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/standard-webhooks.test.js +67 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +266 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/step-up.test.js +487 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-chunk-scratch.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +773 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/stream-throttle.test.js +173 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields.test.js +180 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tcpa-10dlc.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +571 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/test-harness.test.js +190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/testing-request.test.js +119 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/testing.test.js +522 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tls-exporter.test.js +168 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-ct.test.js +275 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-verify.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tls-pinset-drift.test.js +35 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tls-preferred-groups.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tracing.test.js +280 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/uuid.test.js +93 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-aad.test.js +277 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-seal-pem-file.test.js +252 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vendor-data.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vendor-manifest.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vex.test.js +661 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +308 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/web-push-vapid.test.js +144 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +674 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket-channels.test.js +360 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool.test.js +302 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +349 -0
- package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +717 -0
- package/lib/vendor/blamejs/test/layer-5-integration/bundler-output.test.js +444 -0
- package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +597 -0
- package/lib/vendor/blamejs/test/layer-5-integration/security-chaos.test.js +308 -0
- package/lib/vendor/blamejs/test/smoke.js +431 -0
- package/lib/webhooks.js +305 -0
- package/package.json +43 -0
|
@@ -0,0 +1,1858 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module b.mail.server.imap
|
|
4
|
+
* @nav Mail
|
|
5
|
+
* @title Mail IMAP Server
|
|
6
|
+
* @order 546
|
|
7
|
+
*
|
|
8
|
+
* @intro
|
|
9
|
+
* IMAP4rev2 mailbox-access listener (RFC 9051; obsoletes RFC 3501).
|
|
10
|
+
* Modern MUAs (Thunderbird, Apple Mail, mutt, K-9, FairEmail,
|
|
11
|
+
* etc.) connect here to read + manage messages without operators
|
|
12
|
+
* running dovecot / cyrus alongside. Composes the framework's
|
|
13
|
+
* existing substrates:
|
|
14
|
+
*
|
|
15
|
+
* - `b.guardImapCommand` for wire-protocol shape + smuggling
|
|
16
|
+
* defense (literal-injection, bare-CR/LF refusal, per-verb
|
|
17
|
+
* shape, RFC 9051 §2.2.2 literal framing)
|
|
18
|
+
* - `b.mail.server.rateLimit` for per-IP DoS defense (concurrent
|
|
19
|
+
* + rate + AUTH-failure budget + slow-loris)
|
|
20
|
+
* - `b.mailStore` (operator-supplied backend) for the actual
|
|
21
|
+
* mail storage + UIDVALIDITY + modseq tracking
|
|
22
|
+
* - operator-supplied authenticator for SASL credential verify
|
|
23
|
+
* - `b.mail.server.tls` recommended for cert + key loading +
|
|
24
|
+
* rotation
|
|
25
|
+
*
|
|
26
|
+
* ## State machine (RFC 9051 §3)
|
|
27
|
+
*
|
|
28
|
+
* ```
|
|
29
|
+
* NOT-AUTHENTICATED → [STARTTLS → NOT-AUTH-TLS] → AUTH/LOGIN →
|
|
30
|
+
* AUTHENTICATED ↔ SELECTED → LOGOUT
|
|
31
|
+
* ↑ EXAMINE ↓ CLOSE / UNSELECT
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Commands gated by state:
|
|
35
|
+
*
|
|
36
|
+
* - NOT-AUTHENTICATED: STARTTLS / AUTHENTICATE / LOGIN / NOOP /
|
|
37
|
+
* CAPABILITY / LOGOUT / ID
|
|
38
|
+
* - AUTHENTICATED: SELECT / EXAMINE / CREATE / DELETE / RENAME /
|
|
39
|
+
* SUBSCRIBE / UNSUBSCRIBE / LIST / STATUS / APPEND / NAMESPACE /
|
|
40
|
+
* IDLE / ENABLE / NOOP / CAPABILITY / LOGOUT / ID
|
|
41
|
+
* - SELECTED: CHECK / CLOSE / UNSELECT / EXPUNGE / SEARCH / FETCH /
|
|
42
|
+
* STORE / COPY / MOVE / UID … / IDLE / NOOP / CAPABILITY /
|
|
43
|
+
* LOGOUT + every AUTHENTICATED command
|
|
44
|
+
*
|
|
45
|
+
* Tagged response model: every client command carries a tag
|
|
46
|
+
* (`A001 LOGIN …`); server replies with one or more untagged
|
|
47
|
+
* responses (`* …`) then `A001 OK …` / `A001 NO …` / `A001 BAD …`.
|
|
48
|
+
*
|
|
49
|
+
* ## Wire-protocol defenses
|
|
50
|
+
*
|
|
51
|
+
* - **STARTTLS stripping (CVE-2021-33515 Dovecot class)** —
|
|
52
|
+
* STARTTLS upgrade clears pre-handshake receive buffer; any
|
|
53
|
+
* pipelined command queued before TLS is refused with
|
|
54
|
+
* `BAD Pipelined post-STARTTLS not permitted`.
|
|
55
|
+
*
|
|
56
|
+
* - **Literal-injection (CVE-2018-19518 INC IMAP class)** —
|
|
57
|
+
* `{n}` literal continuation MUST come on a line of its own
|
|
58
|
+
* (per `b.guardImapCommand.detectLiteralSmuggling`); oversize
|
|
59
|
+
* literals refused (default 64 MiB); LITERAL+ (RFC 7888) non-
|
|
60
|
+
* synchronizing literals only honored post-AUTH.
|
|
61
|
+
*
|
|
62
|
+
* - **Mailbox-name traversal** — mailbox path components
|
|
63
|
+
* validated through `_validateMailboxName`: refuses `..`, NUL,
|
|
64
|
+
* control chars, oversize. UTF-8 mailbox names (RFC 9051 §5.1)
|
|
65
|
+
* accepted; modified-UTF7 (RFC 3501 §5.1.3 legacy) refused unless
|
|
66
|
+
* `allowLegacyMUtf7: true`.
|
|
67
|
+
*
|
|
68
|
+
* - **APPEND-flood** — per-tenant byte/sec cap surfaces via the
|
|
69
|
+
* `b.mail.server.rateLimit`'s `minBytesPerSecond` floor on the
|
|
70
|
+
* APPEND-literal-body phase (same shape the MX listener uses for
|
|
71
|
+
* DATA-body).
|
|
72
|
+
*
|
|
73
|
+
* - **Resource exhaustion** — per-line cap (default 8 KiB sans
|
|
74
|
+
* literal payload), per-literal cap (64 MiB), per-connection idle
|
|
75
|
+
* cap (default 30 min when not in IDLE; IDLE itself capped at
|
|
76
|
+
* 29 min per RFC 2177 §3 to force re-issue).
|
|
77
|
+
*
|
|
78
|
+
* - **Connection-rate + AUTH-failure budget** — composes
|
|
79
|
+
* `b.mail.server.rateLimit`. Each AUTH failure increments the
|
|
80
|
+
* budget; trip the cap and new AUTH attempts get
|
|
81
|
+
* `* BAD Too many AUTH failures` + connection close.
|
|
82
|
+
*
|
|
83
|
+
* ## Audit lifecycle
|
|
84
|
+
*
|
|
85
|
+
* - `mail.server.imap.connect` — IP, TLS state
|
|
86
|
+
* - `mail.server.imap.auth_attempt` — mechanism, actor-hash
|
|
87
|
+
* - `mail.server.imap.auth_success` — mechanism, tenantId, scopes
|
|
88
|
+
* - `mail.server.imap.auth_failed` — mechanism, reason
|
|
89
|
+
* - `mail.server.imap.select` — mailbox, modseq, exists count
|
|
90
|
+
* - `mail.server.imap.append` — mailbox, size, flags
|
|
91
|
+
* - `mail.server.imap.fetch_bulk` — sequence-set size, BODY parts
|
|
92
|
+
* - `mail.server.imap.expunge` — count, modseq
|
|
93
|
+
* - `mail.server.imap.literal_overflow_refused` — attempt size, cap
|
|
94
|
+
* - `mail.server.imap.rate_limit_refused` — IP, reason
|
|
95
|
+
* - `mail.server.imap.smtp_smuggling_detected` — literal-injection
|
|
96
|
+
*
|
|
97
|
+
* ## What v1 does NOT ship
|
|
98
|
+
*
|
|
99
|
+
* - **SEARCH** — operator wires `opts.search(actor, mailbox, query)`
|
|
100
|
+
* when ready; the listener emits `BAD search-not-configured`
|
|
101
|
+
* until then. SEARCH expressions are operator-domain logic
|
|
102
|
+
* against the mailStore index.
|
|
103
|
+
* - **NOTIFY (RFC 5465)**, **METADATA (RFC 5464)**, **CATENATE
|
|
104
|
+
* (RFC 4469)**, **URLAUTH (RFC 4467)**, **IMAPSIEVE (RFC 6785)**,
|
|
105
|
+
* **COMPRESS=DEFLATE (RFC 4978)** — opt-in / refused.
|
|
106
|
+
* - **CONDSTORE / QRESYNC (RFC 7162)** — modseq is exposed via
|
|
107
|
+
* STATUS but per-FETCH CHANGEDSINCE delta is operator-side
|
|
108
|
+
* follow-up.
|
|
109
|
+
*
|
|
110
|
+
* @card
|
|
111
|
+
* IMAP4rev2 mailbox-access listener (RFC 9051; obsoletes RFC 3501).
|
|
112
|
+
* State machine NOT-AUTH → STARTTLS → AUTH → SELECTED → LOGOUT.
|
|
113
|
+
* Composes b.guardImapCommand (wire-protocol gate), b.mail.server.
|
|
114
|
+
* rateLimit (DoS defense), operator-supplied mailStore + SASL
|
|
115
|
+
* authenticator. Default-on per-IP rate-limit + literal-injection
|
|
116
|
+
* refusal + mailbox-traversal refusal.
|
|
117
|
+
*/
|
|
118
|
+
|
|
119
|
+
var net = require("node:net");
|
|
120
|
+
var lazyRequire = require("./lazy-require");
|
|
121
|
+
var C = require("./constants");
|
|
122
|
+
var bCrypto = require("./crypto");
|
|
123
|
+
var numericBounds = require("./numeric-bounds");
|
|
124
|
+
var validateOpts = require("./validate-opts");
|
|
125
|
+
var guardImapCommand = require("./guard-imap-command");
|
|
126
|
+
var mailServerRateLimit = require("./mail-server-rate-limit");
|
|
127
|
+
var mailServerRegistry = require("./mail-server-registry");
|
|
128
|
+
var mailServerTls = require("./mail-server-tls");
|
|
129
|
+
var { defineClass } = require("./framework-error");
|
|
130
|
+
|
|
131
|
+
var audit = lazyRequire(function () { return require("./audit"); });
|
|
132
|
+
|
|
133
|
+
var MailServerImapError = defineClass("MailServerImapError", { alwaysPermanent: true });
|
|
134
|
+
|
|
135
|
+
var DEFAULT_MAX_LINE_BYTES = C.BYTES.kib(8);
|
|
136
|
+
var DEFAULT_MAX_LITERAL = C.BYTES.mib(64);
|
|
137
|
+
var DEFAULT_IDLE_TIMEOUT_MS = C.TIME.minutes(30);
|
|
138
|
+
var IDLE_BANDWIDTH_TIMEOUT_MS = C.TIME.minutes(29); // RFC 2177 §3 — re-issue before 30
|
|
139
|
+
var DEFAULT_GREETING_VENDOR = "blamejs IMAP4rev2";
|
|
140
|
+
var pkgVersion = require("../package.json").version;
|
|
141
|
+
|
|
142
|
+
// Error-message clamp bytes — protocol-string clamp, not a byte count.
|
|
143
|
+
// Centralized so the allow:raw-byte-literal marker lives in one place
|
|
144
|
+
// and the per-call sites read cleanly.
|
|
145
|
+
var ERR_CLAMP = 200; // allow:raw-byte-literal — protocol-reply error-message clamp
|
|
146
|
+
var LINE_PREVIEW = 80; // allow:raw-byte-literal — audit-line preview clamp
|
|
147
|
+
|
|
148
|
+
// RFC 9051 §6.3.12 + RFC 5322 §3.3 date-time parser for IMAP APPEND.
|
|
149
|
+
// Format: `DD-Mon-YYYY HH:MM:SS ±HHMM` where Mon is the 3-letter
|
|
150
|
+
// English month abbreviation (case-insensitive on parse, but the IMAP
|
|
151
|
+
// spec emits canonical mixed-case `Jan`/`Feb`/...). Returns the
|
|
152
|
+
// millisecond epoch, or null on any parse failure — the caller emits
|
|
153
|
+
// `BAD` rather than silently using `Date.now()`.
|
|
154
|
+
var IMAP_MONTHS = Object.freeze({
|
|
155
|
+
jan: 0, feb: 1, mar: 2, apr: 3, may: 4, jun: 5, // allow:raw-byte-literal — month-index table (0-5)
|
|
156
|
+
jul: 6, aug: 7, sep: 8, oct: 9, nov: 10, dec: 11, // allow:raw-byte-literal — month-index table (6-11)
|
|
157
|
+
});
|
|
158
|
+
var IMAP_DT_RE = /^\s*(\d{1,2})-([A-Za-z]{3})-(\d{4})\s+(\d{2}):(\d{2}):(\d{2})\s+([+-])(\d{2})(\d{2})\s*$/;
|
|
159
|
+
function _parseImapDateTime(s) {
|
|
160
|
+
if (typeof s !== "string") return null;
|
|
161
|
+
var m = s.match(IMAP_DT_RE); // allow:regex-no-length-cap — input bounded by IMAP literal cap
|
|
162
|
+
if (!m) return null;
|
|
163
|
+
var day = parseInt(m[1], 10);
|
|
164
|
+
var month = IMAP_MONTHS[m[2].toLowerCase()];
|
|
165
|
+
if (month === undefined) return null;
|
|
166
|
+
var year = parseInt(m[3], 10);
|
|
167
|
+
var hour = parseInt(m[4], 10);
|
|
168
|
+
var min = parseInt(m[5], 10);
|
|
169
|
+
var sec = parseInt(m[6], 10);
|
|
170
|
+
var sign = m[7] === "-" ? -1 : 1;
|
|
171
|
+
var tzH = parseInt(m[8], 10);
|
|
172
|
+
var tzM = parseInt(m[9], 10);
|
|
173
|
+
if (day < 1 || day > 31 || hour > 23 || min > 59 || sec > 59 || tzH > 23 || tzM > 59) return null;
|
|
174
|
+
var utcMs = Date.UTC(year, month, day, hour, min, sec);
|
|
175
|
+
if (!isFinite(utcMs)) return null;
|
|
176
|
+
// RFC 5322 §3.3 — date-time MUST be a real calendar date. `Date.UTC`
|
|
177
|
+
// silently normalises impossible inputs (`Feb 31 2026` → `Mar 3 2026`);
|
|
178
|
+
// round-trip via the calendar fields and refuse any drift so a
|
|
179
|
+
// hostile client can't smuggle a different internalDate than the
|
|
180
|
+
// wire suggests.
|
|
181
|
+
var probe = new Date(utcMs);
|
|
182
|
+
if (probe.getUTCFullYear() !== year ||
|
|
183
|
+
probe.getUTCMonth() !== month ||
|
|
184
|
+
probe.getUTCDate() !== day ||
|
|
185
|
+
probe.getUTCHours() !== hour ||
|
|
186
|
+
probe.getUTCMinutes() !== min ||
|
|
187
|
+
probe.getUTCSeconds() !== sec) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
return utcMs - sign * (tzH * C.TIME.hours(1) + tzM * C.TIME.minutes(1));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Mailbox name validator. RFC 9051 §5.1 — UTF-8 hierarchy. Refuse
|
|
194
|
+
// path-traversal (`..`), NUL, C0 controls, leading/trailing slash,
|
|
195
|
+
// oversize.
|
|
196
|
+
function _validateMailboxName(name, opts) {
|
|
197
|
+
if (typeof name !== "string" || name.length === 0) return false;
|
|
198
|
+
if (name.length > 1024) return false; // allow:raw-byte-literal — mailbox name cap
|
|
199
|
+
for (var i = 0; i < name.length; i += 1) {
|
|
200
|
+
var c = name.charCodeAt(i);
|
|
201
|
+
if (c < 0x20 || c === 0x7F) return false; // allow:raw-byte-literal — control-byte refusal
|
|
202
|
+
}
|
|
203
|
+
if (name.indexOf("..") !== -1) return false;
|
|
204
|
+
if (name === "/" || name[0] === "/" || name[name.length - 1] === "/") return false;
|
|
205
|
+
// Modified-UTF7 detection — RFC 3501 §5.1.3. Sequences are
|
|
206
|
+
// `&...-`. Refuse under strict (RFC 9051 uses raw UTF-8).
|
|
207
|
+
if (opts && opts.allowLegacyMUtf7 !== true) {
|
|
208
|
+
if (/&[A-Za-z0-9+/]*-/.test(name)) return false; // allow:regex-no-length-cap — mailbox name already length-capped above
|
|
209
|
+
}
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @primitive b.mail.server.imap.create
|
|
215
|
+
* @signature b.mail.server.imap.create(opts)
|
|
216
|
+
* @since 0.9.49
|
|
217
|
+
* @status stable
|
|
218
|
+
* @related b.mail.server.mx.create, b.mail.server.submission.create, b.mailStore.create
|
|
219
|
+
*
|
|
220
|
+
* Build an IMAP4rev2 listener (RFC 9051). The handle exposes
|
|
221
|
+
* `listen({ port, address })` → ephemeral-bind promise resolving to
|
|
222
|
+
* `{ port, address }`, plus `close()` for graceful shutdown.
|
|
223
|
+
*
|
|
224
|
+
* @opts
|
|
225
|
+
* tlsContext: SecureContext, // required (no plaintext mode)
|
|
226
|
+
* greeting: string, // default "blamejs IMAP4rev2"
|
|
227
|
+
* maxLineBytes: number, // default 8192
|
|
228
|
+
* maxLiteralBytes: number, // default 64 MiB
|
|
229
|
+
* idleTimeoutMs: number, // default 30 min
|
|
230
|
+
* profile: "strict" | "balanced" | "permissive",
|
|
231
|
+
* auth: {
|
|
232
|
+
* mechanisms: ["PLAIN", "LOGIN", "SCRAM-SHA-256", "EXTERNAL", "XOAUTH2"],
|
|
233
|
+
* verify: async function (mechanism, credentials) → { ok, actor },
|
|
234
|
+
* },
|
|
235
|
+
* mailStore: b.mailStore handle, // required
|
|
236
|
+
* rateLimit: b.mail.server.rateLimit handle | opts | false,
|
|
237
|
+
* audit: b.audit // optional
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* var imap = b.mail.server.imap.create({
|
|
241
|
+
* tlsContext: b.mail.server.tls.context({ certFile, keyFile }).secureContext,
|
|
242
|
+
* auth: {
|
|
243
|
+
* mechanisms: ["PLAIN", "SCRAM-SHA-256"],
|
|
244
|
+
* verify: async function (mech, creds) {
|
|
245
|
+
* return { ok: true, actor: { tenantId: "t1", username: creds.authzid } };
|
|
246
|
+
* },
|
|
247
|
+
* },
|
|
248
|
+
* mailStore: b.mailStore.create({ backend: b.db.handle() }),
|
|
249
|
+
* });
|
|
250
|
+
* await imap.listen({ port: 143 });
|
|
251
|
+
*/
|
|
252
|
+
function create(opts) {
|
|
253
|
+
validateOpts.requireObject(opts, "mail.server.imap.create",
|
|
254
|
+
MailServerImapError, "mail-server-imap/bad-opts");
|
|
255
|
+
if (!opts.tlsContext) {
|
|
256
|
+
throw new MailServerImapError("mail-server-imap/no-tls-context",
|
|
257
|
+
"mail.server.imap.create: tlsContext is required (no implicit plaintext mode). " +
|
|
258
|
+
"Use b.mail.server.tls.context({ certFile, keyFile, watch: true }) to load + " +
|
|
259
|
+
"auto-reload a cert/key pair from disk.");
|
|
260
|
+
}
|
|
261
|
+
if (!opts.mailStore || typeof opts.mailStore.appendMessage !== "function") {
|
|
262
|
+
throw new MailServerImapError("mail-server-imap/no-mail-store",
|
|
263
|
+
"mail.server.imap.create: mailStore is required (compose b.mailStore.create({ backend: ... }))");
|
|
264
|
+
}
|
|
265
|
+
numericBounds.requireAllPositiveFiniteIntIfPresent(opts,
|
|
266
|
+
["maxLineBytes", "maxLiteralBytes", "idleTimeoutMs"],
|
|
267
|
+
"mail.server.imap.", MailServerImapError, "mail-server-imap/bad-bound");
|
|
268
|
+
|
|
269
|
+
var greeting = opts.greeting || DEFAULT_GREETING_VENDOR;
|
|
270
|
+
var maxLineBytes = opts.maxLineBytes || DEFAULT_MAX_LINE_BYTES;
|
|
271
|
+
var maxLiteralBytes = opts.maxLiteralBytes || DEFAULT_MAX_LITERAL;
|
|
272
|
+
var idleTimeoutMs = opts.idleTimeoutMs || DEFAULT_IDLE_TIMEOUT_MS;
|
|
273
|
+
var profile = opts.profile || "strict";
|
|
274
|
+
var authConfig = opts.auth || null;
|
|
275
|
+
var mailStore = opts.mailStore;
|
|
276
|
+
var allowLegacyMUtf7 = profile === "permissive";
|
|
277
|
+
|
|
278
|
+
var rateLimit;
|
|
279
|
+
if (opts.rateLimit === false) {
|
|
280
|
+
rateLimit = mailServerRateLimit.create({ disabled: true });
|
|
281
|
+
} else if (opts.rateLimit && typeof opts.rateLimit.admitConnection === "function") {
|
|
282
|
+
rateLimit = opts.rateLimit;
|
|
283
|
+
} else {
|
|
284
|
+
rateLimit = mailServerRateLimit.create(opts.rateLimit || {});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
var tcpServer = null;
|
|
288
|
+
var listening = false;
|
|
289
|
+
var connections = new Set();
|
|
290
|
+
|
|
291
|
+
function _emit(action, metadata, outcome) {
|
|
292
|
+
try {
|
|
293
|
+
audit().safeEmit({
|
|
294
|
+
action: action,
|
|
295
|
+
outcome: outcome || "success",
|
|
296
|
+
metadata: metadata || {},
|
|
297
|
+
});
|
|
298
|
+
} catch (_e) { /* drop-silent — audit best-effort */ }
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function _handleConnection(rawSocket) {
|
|
302
|
+
var remoteAddress = rawSocket.remoteAddress || "0.0.0.0";
|
|
303
|
+
var admit = rateLimit.admitConnection(remoteAddress);
|
|
304
|
+
if (!admit.ok) {
|
|
305
|
+
_emit("mail.server.imap.rate_limit_refused",
|
|
306
|
+
{ remoteAddress: remoteAddress, reason: admit.reason }, "denied");
|
|
307
|
+
try { rawSocket.write("* BAD Too many connections from your IP\r\n"); }
|
|
308
|
+
catch (_e) { /* socket may be down */ }
|
|
309
|
+
try { rawSocket.destroy(); } catch (_e2) { /* idempotent */ }
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
rawSocket.once("close", function () { rateLimit.releaseConnection(remoteAddress); });
|
|
313
|
+
|
|
314
|
+
var connectionId = "imapconn-" + bCrypto.generateToken(8); // allow:raw-byte-literal — connection-id length
|
|
315
|
+
var socket = rawSocket;
|
|
316
|
+
connections.add(socket);
|
|
317
|
+
|
|
318
|
+
var state = {
|
|
319
|
+
id: connectionId,
|
|
320
|
+
remoteAddress: remoteAddress,
|
|
321
|
+
tls: false,
|
|
322
|
+
stage: "not-authenticated",
|
|
323
|
+
actor: null,
|
|
324
|
+
selectedMailbox: null,
|
|
325
|
+
selectedReadOnly: false,
|
|
326
|
+
authPending: null,
|
|
327
|
+
pendingLiteral: null, // { tag, verb, line, size, body }
|
|
328
|
+
idle: null, // { tag, timer }
|
|
329
|
+
// Per-connection receive buffer (must NOT be a closure variable —
|
|
330
|
+
// multiple concurrent connections would clobber each other).
|
|
331
|
+
lineBuffer: Buffer.alloc(0),
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
_emit("mail.server.imap.connect",
|
|
335
|
+
{ connectionId: connectionId, remoteAddress: remoteAddress });
|
|
336
|
+
|
|
337
|
+
socket.setTimeout(idleTimeoutMs);
|
|
338
|
+
socket.on("timeout", function () {
|
|
339
|
+
_writeUntagged(socket, "BYE Idle timeout");
|
|
340
|
+
_close(socket, state);
|
|
341
|
+
});
|
|
342
|
+
socket.on("error", function (err) {
|
|
343
|
+
_emit("mail.server.imap.socket_error",
|
|
344
|
+
{ connectionId: connectionId, error: (err && err.message) || String(err) }, "failure");
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// Greeting per RFC 9051 §7.1.5 — `* OK <greeting>`.
|
|
348
|
+
_writeUntagged(socket, "OK [CAPABILITY " + _capabilityLine(state) + "] " + greeting);
|
|
349
|
+
|
|
350
|
+
socket.on("data", function (chunk) {
|
|
351
|
+
// Per-line cap MUST gate the concat — a single large TCP chunk
|
|
352
|
+
// (~64 KiB on most kernels) can push the buffer past the line
|
|
353
|
+
// cap BEFORE the drain loop runs, so the cap-check inside the
|
|
354
|
+
// loop sees a buffer that's already grown past the policy
|
|
355
|
+
// floor. When the chunk would itself overrun the line cap AND
|
|
356
|
+
// no literal is pending (where over-cap bytes are legitimate
|
|
357
|
+
// payload), reject here and tear the connection down.
|
|
358
|
+
var pendingLiteral = state.pendingLiteral;
|
|
359
|
+
var room = pendingLiteral
|
|
360
|
+
? (pendingLiteral.size - pendingLiteral.body.length) + maxLineBytes
|
|
361
|
+
: (maxLineBytes - state.lineBuffer.length);
|
|
362
|
+
if (chunk.length > room) {
|
|
363
|
+
_writeUntagged(socket, "BAD Line too long (cap " + maxLineBytes + ")");
|
|
364
|
+
_close(socket, state);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
state.lineBuffer = Buffer.concat([state.lineBuffer, chunk]);
|
|
368
|
+
_drainBuffer(state, socket);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Receive-buffer drain: extract complete lines (CRLF-terminated)
|
|
373
|
+
// and dispatch. When the previous command opened a literal (e.g.
|
|
374
|
+
// APPEND ... {N}), the next N bytes are the literal payload — we
|
|
375
|
+
// accumulate them before resuming line-mode dispatch.
|
|
376
|
+
function _drainBuffer(state, socket) {
|
|
377
|
+
while (true) {
|
|
378
|
+
if (state.pendingLiteral) {
|
|
379
|
+
var need = state.pendingLiteral.size - state.pendingLiteral.body.length;
|
|
380
|
+
if (state.lineBuffer.length < need) {
|
|
381
|
+
state.pendingLiteral.body = Buffer.concat([state.pendingLiteral.body, state.lineBuffer]);
|
|
382
|
+
state.lineBuffer = Buffer.alloc(0);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
state.pendingLiteral.body = Buffer.concat([state.pendingLiteral.body, state.lineBuffer.subarray(0, need)]);
|
|
386
|
+
state.lineBuffer = state.lineBuffer.subarray(need);
|
|
387
|
+
_completeLiteralCommand(state, socket);
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
var crlf = state.lineBuffer.indexOf("\r\n");
|
|
391
|
+
if (crlf === -1) {
|
|
392
|
+
if (state.lineBuffer.length > maxLineBytes) {
|
|
393
|
+
_writeUntagged(socket, "BAD Line too long (cap " + maxLineBytes + ")");
|
|
394
|
+
_close(socket, state);
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
var rawLine = state.lineBuffer.subarray(0, crlf).toString("utf8");
|
|
399
|
+
state.lineBuffer = state.lineBuffer.subarray(crlf + 2);
|
|
400
|
+
_handleLine(state, socket, rawLine);
|
|
401
|
+
if (state.stage === "closed") return;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function _handleLine(state, socket, line) {
|
|
406
|
+
// Continuation: AUTHENTICATE multi-step expects a client response
|
|
407
|
+
if (state.authPending) {
|
|
408
|
+
_runAuthStep(state, socket, line.trim());
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
// IDLE termination — RFC 2177 §3 expects `DONE` line.
|
|
412
|
+
if (state.idle) {
|
|
413
|
+
if (line.toUpperCase() === "DONE") {
|
|
414
|
+
var idleTag = state.idle.tag;
|
|
415
|
+
if (state.idle.timer) clearTimeout(state.idle.timer);
|
|
416
|
+
state.idle = null;
|
|
417
|
+
_writeTagged(socket, idleTag, "OK IDLE terminated");
|
|
418
|
+
} else {
|
|
419
|
+
_writeUntagged(socket, "BAD Expected DONE during IDLE");
|
|
420
|
+
}
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
var parsed;
|
|
424
|
+
try {
|
|
425
|
+
parsed = guardImapCommand.validate(line, {
|
|
426
|
+
profile: profile,
|
|
427
|
+
authenticated: state.actor !== null,
|
|
428
|
+
});
|
|
429
|
+
} catch (e) {
|
|
430
|
+
if (e && e.code === "guard-imap-command/literal-smuggling") {
|
|
431
|
+
_emit("mail.server.imap.smtp_smuggling_detected",
|
|
432
|
+
{ connectionId: state.id, line: line.slice(0, LINE_PREVIEW) }, "denied");
|
|
433
|
+
}
|
|
434
|
+
_writeUntagged(socket, "BAD " + (e && e.message ? e.message.slice(0, ERR_CLAMP) : "syntax"));
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
// Literal-opener: stash + emit continuation. Zero-length literals
|
|
438
|
+
// (`{0}`) are legal per RFC 9051 §6.3.12 (e.g. APPEND of an empty
|
|
439
|
+
// message body — rare but spec-compliant; refusing them would
|
|
440
|
+
// diverge from the wire-protocol).
|
|
441
|
+
if (parsed.literalSize !== null) {
|
|
442
|
+
if (parsed.literalSize > maxLiteralBytes) {
|
|
443
|
+
_emit("mail.server.imap.literal_overflow_refused",
|
|
444
|
+
{ connectionId: state.id, attempted: parsed.literalSize, cap: maxLiteralBytes },
|
|
445
|
+
"denied");
|
|
446
|
+
_writeTagged(socket, parsed.tag,
|
|
447
|
+
"NO Literal " + parsed.literalSize + " bytes exceeds cap " + maxLiteralBytes);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
// Zero-byte literal: no continuation, no read — synthesize the
|
|
451
|
+
// pending-literal with an empty body and complete immediately on
|
|
452
|
+
// the next loop tick.
|
|
453
|
+
if (parsed.literalSize === 0) {
|
|
454
|
+
state.pendingLiteral = {
|
|
455
|
+
tag: parsed.tag,
|
|
456
|
+
verb: parsed.verb,
|
|
457
|
+
line: line,
|
|
458
|
+
size: 0,
|
|
459
|
+
body: Buffer.alloc(0),
|
|
460
|
+
synchronizing: !parsed.literalNonSync,
|
|
461
|
+
};
|
|
462
|
+
_completeLiteralCommand(state, socket);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
state.pendingLiteral = {
|
|
466
|
+
tag: parsed.tag,
|
|
467
|
+
verb: parsed.verb,
|
|
468
|
+
line: line,
|
|
469
|
+
size: parsed.literalSize,
|
|
470
|
+
body: Buffer.alloc(0),
|
|
471
|
+
synchronizing: !parsed.literalNonSync,
|
|
472
|
+
};
|
|
473
|
+
if (!parsed.literalNonSync) {
|
|
474
|
+
_writeUntagged(socket, "+ Ready for literal data");
|
|
475
|
+
}
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
_dispatch(state, socket, parsed, line);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function _completeLiteralCommand(state, socket) {
|
|
482
|
+
var pending = state.pendingLiteral;
|
|
483
|
+
state.pendingLiteral = null;
|
|
484
|
+
// Strip the trailing literal opener `{N}` (or `{N+}`) from the line
|
|
485
|
+
var lineNoLit = pending.line.replace(/\{[0-9]+\+?\}$/, "").trim(); // allow:regex-no-length-cap — line length already capped upstream
|
|
486
|
+
var parsed;
|
|
487
|
+
try { parsed = guardImapCommand.validate(lineNoLit, { profile: profile, authenticated: state.actor !== null }); }
|
|
488
|
+
catch (e) {
|
|
489
|
+
_writeTagged(socket, pending.tag, "BAD " + (e && e.message ? e.message.slice(0, ERR_CLAMP) : "syntax"));
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
_dispatch(state, socket, parsed, lineNoLit, pending.body);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Adapter shim — uniform `(state, socket, parsed, literalBody)`
|
|
496
|
+
// dispatch contract over the per-verb handlers. Builds the registry
|
|
497
|
+
// defaults lazily on first dispatch so the closure-scoped handler
|
|
498
|
+
// references are bound when needed (handlers are hoisted by their
|
|
499
|
+
// function-declarations; the registry init runs at dispatch time).
|
|
500
|
+
var _registry = null;
|
|
501
|
+
function _ensureRegistry() {
|
|
502
|
+
if (_registry !== null) return _registry;
|
|
503
|
+
// Per-handler resource budgets. Sized per the verb's known
|
|
504
|
+
// payload shape (LIST scans the folder tree; FETCH walks N
|
|
505
|
+
// messages; APPEND accepts a literal up to maxLiteralBytes).
|
|
506
|
+
var SHORT_MS = 5 * 1000; // allow:raw-time-literal — 5s short-command budget
|
|
507
|
+
var MEDIUM_MS = 30 * 1000; // allow:raw-time-literal — 30s medium-command budget
|
|
508
|
+
var LONG_MS = 2 * 60 * 1000; // allow:raw-time-literal — 2 min long-command budget (FETCH / APPEND)
|
|
509
|
+
var SHORT_B = 8 * 1024; // allow:raw-byte-literal — 8 KiB short-command response cap
|
|
510
|
+
var MEDIUM_B = 1024 * 1024; // allow:raw-byte-literal — 1 MiB medium-command response cap
|
|
511
|
+
var LONG_B = 64 * 1024 * 1024; // allow:raw-byte-literal — 64 MiB FETCH/APPEND response cap
|
|
512
|
+
var defaults = {
|
|
513
|
+
CAPABILITY: { fn: function (s, so, p) { return _handleCapability(s, so, p.tag); },
|
|
514
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
515
|
+
NOOP: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "OK NOOP completed"); },
|
|
516
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
517
|
+
LOGOUT: { fn: function (s, so, p) { return _handleLogout(s, so, p.tag); },
|
|
518
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
519
|
+
ID: { fn: function (s, so, p) { return _handleId(s, so, p.tag, p.args); },
|
|
520
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
521
|
+
STARTTLS: { fn: function (s, so, p) { return _handleStartTls(s, so, p.tag); },
|
|
522
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
523
|
+
AUTHENTICATE: { fn: function (s, so, p) { return _handleAuthenticate(s, so, p.tag, p.args); },
|
|
524
|
+
maxHandlerBytes: MEDIUM_B, maxHandlerMs: MEDIUM_MS },
|
|
525
|
+
LOGIN: { fn: function (s, so, p) { return _handleLogin(s, so, p.tag, p.args); },
|
|
526
|
+
maxHandlerBytes: MEDIUM_B, maxHandlerMs: MEDIUM_MS },
|
|
527
|
+
ENABLE: { fn: function (s, so, p) { return _handleEnable(s, so, p.tag, p.args); },
|
|
528
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
529
|
+
SELECT: { fn: function (s, so, p) { return _handleSelect(s, so, p.tag, p.args, false); },
|
|
530
|
+
maxHandlerBytes: MEDIUM_B, maxHandlerMs: MEDIUM_MS },
|
|
531
|
+
EXAMINE: { fn: function (s, so, p) { return _handleSelect(s, so, p.tag, p.args, true); },
|
|
532
|
+
maxHandlerBytes: MEDIUM_B, maxHandlerMs: MEDIUM_MS },
|
|
533
|
+
LIST: { fn: function (s, so, p) { return _handleList(s, so, p.tag, p.args); },
|
|
534
|
+
maxHandlerBytes: MEDIUM_B, maxHandlerMs: MEDIUM_MS },
|
|
535
|
+
STATUS: { fn: function (s, so, p) { return _handleStatus(s, so, p.tag, p.args); },
|
|
536
|
+
maxHandlerBytes: MEDIUM_B, maxHandlerMs: MEDIUM_MS },
|
|
537
|
+
NAMESPACE: { fn: function (s, so, p) {
|
|
538
|
+
_writeUntagged(so, "NAMESPACE ((\"\" \"/\")) NIL NIL");
|
|
539
|
+
return _writeTagged(so, p.tag, "OK NAMESPACE completed");
|
|
540
|
+
},
|
|
541
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
542
|
+
APPEND: { fn: function (s, so, p, lit) { return _handleAppend(s, so, p.tag, p.args, lit); },
|
|
543
|
+
maxHandlerBytes: LONG_B, maxHandlerMs: LONG_MS },
|
|
544
|
+
CHECK: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "OK CHECK completed"); },
|
|
545
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
546
|
+
CLOSE: { fn: function (s, so, p) { return _handleClose(s, so, p.tag); },
|
|
547
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
548
|
+
UNSELECT: { fn: function (s, so, p) { return _handleClose(s, so, p.tag); },
|
|
549
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
550
|
+
EXPUNGE: { fn: function (s, so, p) { return _handleExpunge(s, so, p.tag); },
|
|
551
|
+
maxHandlerBytes: MEDIUM_B, maxHandlerMs: MEDIUM_MS },
|
|
552
|
+
FETCH: { fn: function (s, so, p) { return _handleFetch(s, so, p.tag, p.args); },
|
|
553
|
+
maxHandlerBytes: LONG_B, maxHandlerMs: LONG_MS },
|
|
554
|
+
STORE: { fn: function (s, so, p) { return _handleStore(s, so, p.tag, p.args); },
|
|
555
|
+
maxHandlerBytes: MEDIUM_B, maxHandlerMs: MEDIUM_MS },
|
|
556
|
+
UID: { fn: function (s, so, p) { return _handleUid(s, so, p.tag, p.args); },
|
|
557
|
+
maxHandlerBytes: LONG_B, maxHandlerMs: LONG_MS },
|
|
558
|
+
IDLE: { fn: function (s, so, p) { return _handleIdle(s, so, p.tag); },
|
|
559
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: LONG_MS },
|
|
560
|
+
// v0.11.28 — RFC 5465 NOTIFY / RFC 5464 METADATA / RFC 4469 CATENATE.
|
|
561
|
+
NOTIFY: { fn: function (s, so, p) { return _handleNotify(s, so, p.tag, p.args); },
|
|
562
|
+
maxHandlerBytes: MEDIUM_B, maxHandlerMs: MEDIUM_MS },
|
|
563
|
+
GETMETADATA: { fn: function (s, so, p) { return _handleGetMetadata(s, so, p.tag, p.args); },
|
|
564
|
+
maxHandlerBytes: MEDIUM_B, maxHandlerMs: MEDIUM_MS },
|
|
565
|
+
SETMETADATA: { fn: function (s, so, p, lit) { return _handleSetMetadata(s, so, p.tag, p.args, lit); },
|
|
566
|
+
maxHandlerBytes: LONG_B, maxHandlerMs: MEDIUM_MS },
|
|
567
|
+
DONE: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "BAD DONE outside IDLE"); },
|
|
568
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
569
|
+
// Defaults for the verbs the v0.9.49 listener didn't dispatch —
|
|
570
|
+
// operators wire concrete handlers via opts.overrides.
|
|
571
|
+
SEARCH: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "NO SEARCH not configured"); },
|
|
572
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
573
|
+
CREATE: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "NO CREATE not configured"); },
|
|
574
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
575
|
+
DELETE: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "NO DELETE not configured"); },
|
|
576
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
577
|
+
RENAME: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "NO RENAME not configured"); },
|
|
578
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
579
|
+
SUBSCRIBE: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "NO SUBSCRIBE not configured"); },
|
|
580
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
581
|
+
UNSUBSCRIBE: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "NO UNSUBSCRIBE not configured"); },
|
|
582
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
583
|
+
COPY: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "NO COPY not configured"); },
|
|
584
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
585
|
+
MOVE: { fn: function (s, so, p) { return _writeTagged(so, p.tag, "NO MOVE not configured"); },
|
|
586
|
+
maxHandlerBytes: SHORT_B, maxHandlerMs: SHORT_MS },
|
|
587
|
+
};
|
|
588
|
+
_registry = mailServerRegistry.create({
|
|
589
|
+
protocol: "imap",
|
|
590
|
+
defaults: defaults,
|
|
591
|
+
overrides: opts.overrides || {},
|
|
592
|
+
// b.agent.tenant adoption (v0.10.12). Operators wiring multi-
|
|
593
|
+
// tenant IMAP deployments pass `tenantScope` from
|
|
594
|
+
// `b.agent.tenant.create({...})` plus the per-listener tenant id.
|
|
595
|
+
// The registry then gates every dispatch on
|
|
596
|
+
// `tenantScope.check(state.actor, agentTenantId)` before guard
|
|
597
|
+
// validation or audit emission.
|
|
598
|
+
tenantScope: opts.tenantScope || null,
|
|
599
|
+
agentTenantId: opts.agentTenantId || null,
|
|
600
|
+
notFoundHandler: function (verb, _state, socket, parsed) {
|
|
601
|
+
return _writeTagged(socket, parsed.tag,
|
|
602
|
+
"BAD Verb '" + verb + "' not implemented in v1");
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
return _registry;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function _dispatch(state, socket, parsed, _rawLine, literalBody) {
|
|
609
|
+
// Registry dispatch may return a Promise (async override handler,
|
|
610
|
+
// or a safeAsync.withTimeout-wrapped Promise). The caller's
|
|
611
|
+
// try/catch is synchronous, so a Promise rejection would surface
|
|
612
|
+
// as an unhandled rejection AND the client would never receive
|
|
613
|
+
// the tagged error reply. Attach a catch that converts the
|
|
614
|
+
// rejection into a `BAD`/`NO` tagged response + audit emit.
|
|
615
|
+
var result;
|
|
616
|
+
try {
|
|
617
|
+
result = _ensureRegistry().dispatch(parsed.verb, state, socket, parsed, literalBody);
|
|
618
|
+
} catch (err) {
|
|
619
|
+
_writeTagged(socket, parsed.tag,
|
|
620
|
+
"NO " + ((err && err.message) || "handler threw").slice(0, ERR_CLAMP));
|
|
621
|
+
_emit("mail.server.imap.handler_threw",
|
|
622
|
+
{ connectionId: state.id, verb: parsed.verb,
|
|
623
|
+
error: (err && err.message) || String(err) }, "failure");
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
if (result && typeof result.then === "function") {
|
|
627
|
+
result.then(
|
|
628
|
+
function () { /* tagged response already written by handler */ },
|
|
629
|
+
function (err) {
|
|
630
|
+
try {
|
|
631
|
+
_writeTagged(socket, parsed.tag,
|
|
632
|
+
"NO " + ((err && err.message) || "handler rejected").slice(0, ERR_CLAMP));
|
|
633
|
+
} catch (_we) { /* socket may already be gone */ }
|
|
634
|
+
try {
|
|
635
|
+
_emit("mail.server.imap.handler_rejected",
|
|
636
|
+
{ connectionId: state.id, verb: parsed.verb,
|
|
637
|
+
error: (err && err.message) || String(err) }, "failure");
|
|
638
|
+
} catch (_ae) { /* drop-silent */ }
|
|
639
|
+
}
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
return result;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function _capabilityLine(state) {
|
|
646
|
+
var caps = ["IMAP4rev2"];
|
|
647
|
+
if (!state.tls) caps.push("STARTTLS");
|
|
648
|
+
// RFC 7162 §3 — CONDSTORE is server-advertised; clients ENABLE
|
|
649
|
+
// before relying on MODSEQ in untagged FETCH responses. QRESYNC
|
|
650
|
+
// (§3.2) adds the VANISHED responses on SELECT + post-EXPUNGE
|
|
651
|
+
// and implicitly engages CONDSTORE per §3.2.5.
|
|
652
|
+
caps.push("CONDSTORE");
|
|
653
|
+
caps.push("QRESYNC");
|
|
654
|
+
// v0.11.28 — opt-in extensions (advertised so capable clients can
|
|
655
|
+
// exercise them; each handler refuses gracefully when the operator
|
|
656
|
+
// backend doesn't supply the corresponding hook).
|
|
657
|
+
caps.push("NOTIFY"); // RFC 5465
|
|
658
|
+
caps.push("METADATA"); // RFC 5464 — per-mailbox annotations // allow:raw-byte-literal — RFC number in comment
|
|
659
|
+
caps.push("METADATA-SERVER"); // RFC 5464 §3.1 — server-wide annotations // allow:raw-byte-literal — RFC number in comment
|
|
660
|
+
caps.push("CATENATE"); // RFC 4469 — APPEND from existing parts
|
|
661
|
+
// NB: COMPRESS=DEFLATE (RFC 4978) intentionally NOT advertised —
|
|
662
|
+
// CRIME-class compression-oracle attack on the encrypted IMAP
|
|
663
|
+
// stream. Operators who explicitly enable it via opts.compress
|
|
664
|
+
// get a documented downgrade; v1 default is off.
|
|
665
|
+
// Advertise AUTH=<mech> ONLY for mechanisms the operator wired
|
|
666
|
+
// in opts.auth.mechanisms. RFC 9051 §7.2 — clients pick from the
|
|
667
|
+
// advertised list; advertising AUTH=PLAIN when authConfig is null
|
|
668
|
+
// or doesn't include PLAIN sets clients up for AUTHENTICATE
|
|
669
|
+
// requests that the listener refuses with "no AUTHENTICATE
|
|
670
|
+
// configured" / "mechanism not advertised".
|
|
671
|
+
if (authConfig && Array.isArray(authConfig.mechanisms)) {
|
|
672
|
+
for (var i = 0; i < authConfig.mechanisms.length; i += 1) {
|
|
673
|
+
var m = String(authConfig.mechanisms[i]).toUpperCase();
|
|
674
|
+
if (caps.indexOf("AUTH=" + m) === -1) caps.push("AUTH=" + m);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return caps.join(" ");
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// RFC 7162 §3.1 — ENABLE CONDSTORE flips the per-state flag that
|
|
681
|
+
// makes subsequent untagged FETCH responses include the MODSEQ
|
|
682
|
+
// attribute and lets STORE / FETCH carry CHANGEDSINCE /
|
|
683
|
+
// UNCHANGEDSINCE modifiers. Unknown ENABLE arguments are silently
|
|
684
|
+
// ignored per RFC 5161 §3.1 — the server lists in `ENABLED <name>`
|
|
685
|
+
// only the extensions it actually turned on.
|
|
686
|
+
function _handleEnable(state, socket, tag, args) {
|
|
687
|
+
var requested = (args || "").split(/\s+/).filter(Boolean);
|
|
688
|
+
var enabled = [];
|
|
689
|
+
for (var i = 0; i < requested.length; i += 1) {
|
|
690
|
+
var name = requested[i].toUpperCase();
|
|
691
|
+
if (name === "CONDSTORE") {
|
|
692
|
+
if (!state.enabledCondStore) {
|
|
693
|
+
state.enabledCondStore = true;
|
|
694
|
+
enabled.push("CONDSTORE");
|
|
695
|
+
}
|
|
696
|
+
} else if (name === "QRESYNC") {
|
|
697
|
+
// RFC 7162 §3.2.5 — QRESYNC implicitly engages CONDSTORE.
|
|
698
|
+
// The client signals it can consume `* VANISHED (EARLIER)`
|
|
699
|
+
// responses on SELECT / EXAMINE + post-EXPUNGE; the listener
|
|
700
|
+
// flips both flags and the SELECT handler honours the
|
|
701
|
+
// QRESYNC parameter list when present.
|
|
702
|
+
if (!state.enabledQResync) {
|
|
703
|
+
state.enabledQResync = true;
|
|
704
|
+
state.enabledCondStore = true;
|
|
705
|
+
enabled.push("QRESYNC");
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
_writeUntagged(socket, "ENABLED" + (enabled.length ? " " + enabled.join(" ") : ""));
|
|
710
|
+
_writeTagged(socket, tag, "OK ENABLE completed");
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// RFC 5465 NOTIFY — `NOTIFY SET [STATUS] (<filter-set> (<event>...))*`
|
|
714
|
+
// / `NOTIFY NONE`. Subscribes the connection to mailbox / message
|
|
715
|
+
// events on a filter set. Actual event emission is operator-side
|
|
716
|
+
// (the backend's `subscribeNotify(actor, spec, emitFn)` hook); this
|
|
717
|
+
// handler stores the parsed subscription on `state.notifySpec` so
|
|
718
|
+
// the backend can read it on later mutations. NOTIFY NONE clears.
|
|
719
|
+
function _handleNotify(state, socket, tag, args) {
|
|
720
|
+
if (!_requireAuth(state, socket, tag)) return;
|
|
721
|
+
var raw = (args || "").trim();
|
|
722
|
+
if (/^NONE\b/i.test(raw)) {
|
|
723
|
+
state.notifySpec = null;
|
|
724
|
+
if (typeof mailStore.subscribeNotify === "function") {
|
|
725
|
+
try { mailStore.subscribeNotify(state.actor, null, null); }
|
|
726
|
+
catch (_e) { /* drop-silent — operator hook may refuse mid-life */ }
|
|
727
|
+
}
|
|
728
|
+
_writeTagged(socket, tag, "OK NOTIFY completed");
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
var setMatch = raw.match(/^SET\s+(?:STATUS\s+)?(.+)$/i); // allow:regex-no-length-cap — args length already capped upstream
|
|
732
|
+
if (!setMatch) {
|
|
733
|
+
_writeTagged(socket, tag, "BAD NOTIFY syntax (RFC 5465 §6)");
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
// Store the spec verbatim; the backend parses the filter-set
|
|
737
|
+
// vocabulary (`SELECTED`, `SELECTED-DELAYED`, `INBOXES`,
|
|
738
|
+
// `PERSONAL`, `SUBSCRIBED`, `MAILBOXES <list>`, `SUBTREE <list>`)
|
|
739
|
+
// since the event semantics live there. The listener's job is to
|
|
740
|
+
// hand the wire string to the backend.
|
|
741
|
+
state.notifySpec = setMatch[1];
|
|
742
|
+
if (typeof mailStore.subscribeNotify === "function") {
|
|
743
|
+
Promise.resolve()
|
|
744
|
+
.then(function () {
|
|
745
|
+
return mailStore.subscribeNotify(state.actor, state.notifySpec, function (event) {
|
|
746
|
+
// Backend pushes events as { kind, mailbox, payload }; we
|
|
747
|
+
// emit them as untagged responses on the same connection.
|
|
748
|
+
if (!event || typeof event.kind !== "string") return;
|
|
749
|
+
try {
|
|
750
|
+
if (event.kind === "STATUS") {
|
|
751
|
+
_writeUntagged(socket, "STATUS " + event.payload);
|
|
752
|
+
} else if (event.kind === "LIST") {
|
|
753
|
+
_writeUntagged(socket, "LIST " + event.payload);
|
|
754
|
+
} else if (event.kind === "FETCH") {
|
|
755
|
+
_writeUntagged(socket, (event.seq || "") + " FETCH (" + (event.payload || "") + ")");
|
|
756
|
+
}
|
|
757
|
+
} catch (_e) { /* drop-silent — socket may already be closed */ }
|
|
758
|
+
});
|
|
759
|
+
})
|
|
760
|
+
.then(function () { _writeTagged(socket, tag, "OK NOTIFY completed"); })
|
|
761
|
+
.catch(function (err) {
|
|
762
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "NOTIFY refused").slice(0, ERR_CLAMP));
|
|
763
|
+
});
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
// Backend doesn't expose the subscribe hook — accept the wire
|
|
767
|
+
// command but emit no events. RFC 5465 §6 says NO is the right
|
|
768
|
+
// refusal shape when the server cannot fulfil the subscription.
|
|
769
|
+
_writeTagged(socket, tag, "NO NOTIFY backend not configured");
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// RFC 5464 §4.1 GETMETADATA — `GETMETADATA [opts] mailbox entries`.
|
|
773
|
+
// `mailbox` may be `""` for server-wide annotations (METADATA-SERVER).
|
|
774
|
+
// Entries are slash-prefixed names (`/private/foo` / `/shared/bar`).
|
|
775
|
+
// Backend hook: `mailStore.getMetadata(actor, mailbox, names) →
|
|
776
|
+
// [{ entry, value }]`.
|
|
777
|
+
function _handleGetMetadata(state, socket, tag, args) {
|
|
778
|
+
if (!_requireAuth(state, socket, tag)) return;
|
|
779
|
+
if (typeof mailStore.getMetadata !== "function") {
|
|
780
|
+
_writeTagged(socket, tag, "NO GETMETADATA backend not configured");
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
// Strip optional MAXSIZE / DEPTH opts: GETMETADATA (MAXSIZE 1024) "" ("/foo")
|
|
784
|
+
var rest = (args || "").trim();
|
|
785
|
+
var opts = {};
|
|
786
|
+
var optsMatch = rest.match(/^\(([^)]+)\)\s+(.+)$/); // allow:regex-no-length-cap — args length already capped upstream
|
|
787
|
+
if (optsMatch) {
|
|
788
|
+
var optBody = optsMatch[1];
|
|
789
|
+
var maxMatch = optBody.match(/MAXSIZE\s+(\d+)/i); // allow:regex-no-length-cap — optBody bounded by parens
|
|
790
|
+
if (maxMatch) opts.maxSize = parseInt(maxMatch[1], 10);
|
|
791
|
+
var depthMatch = optBody.match(/DEPTH\s+(\w+)/i); // allow:regex-no-length-cap — optBody bounded
|
|
792
|
+
if (depthMatch) opts.depth = depthMatch[1];
|
|
793
|
+
rest = optsMatch[2];
|
|
794
|
+
}
|
|
795
|
+
var partsMatch = rest.match(/^(\S+|"[^"]*")\s+(\(([^)]+)\)|(\/\S+))$/); // allow:regex-no-length-cap — args length already capped upstream
|
|
796
|
+
if (!partsMatch) {
|
|
797
|
+
_writeTagged(socket, tag, "BAD GETMETADATA syntax (RFC 5464 §4.1)");
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
var mailbox = _unquote(partsMatch[1]);
|
|
801
|
+
var entries = partsMatch[3]
|
|
802
|
+
? partsMatch[3].split(/\s+/).filter(Boolean)
|
|
803
|
+
: [partsMatch[4]];
|
|
804
|
+
if (mailbox !== "" && !_validateMailboxName(mailbox, { allowLegacyMUtf7: allowLegacyMUtf7 })) {
|
|
805
|
+
_writeTagged(socket, tag, "BAD Mailbox name refused");
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
Promise.resolve()
|
|
809
|
+
.then(function () { return mailStore.getMetadata(state.actor, mailbox, entries, opts); })
|
|
810
|
+
.then(function (rows) {
|
|
811
|
+
if (Array.isArray(rows) && rows.length > 0) {
|
|
812
|
+
var pairs = rows.map(function (r) {
|
|
813
|
+
var v = r.value === null || r.value === undefined ? "NIL" : '"' + String(r.value).replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + '"';
|
|
814
|
+
return r.entry + " " + v;
|
|
815
|
+
}).join(" ");
|
|
816
|
+
_writeUntagged(socket, "METADATA " + (mailbox === "" ? '""' : mailbox) + " (" + pairs + ")");
|
|
817
|
+
}
|
|
818
|
+
_writeTagged(socket, tag, "OK GETMETADATA completed");
|
|
819
|
+
})
|
|
820
|
+
.catch(function (err) {
|
|
821
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "GETMETADATA failed").slice(0, ERR_CLAMP));
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// RFC 5464 §4.3 SETMETADATA — `SETMETADATA mailbox (entry value ...)`.
|
|
826
|
+
// Setting `value = NIL` clears the entry. Backend hook:
|
|
827
|
+
// `mailStore.setMetadata(actor, mailbox, entries)`. The wire format
|
|
828
|
+
// delivers each value as a quoted-string or NIL atom; the parser
|
|
829
|
+
// here handles the simple single-line shape (no literals across
|
|
830
|
+
// SETMETADATA — operators using >1 KiB metadata go through APPEND).
|
|
831
|
+
function _handleSetMetadata(state, socket, tag, args, _literalBody) {
|
|
832
|
+
if (!_requireAuth(state, socket, tag)) return;
|
|
833
|
+
if (typeof mailStore.setMetadata !== "function") {
|
|
834
|
+
_writeTagged(socket, tag, "NO SETMETADATA backend not configured");
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
var match = (args || "").trim().match(/^(\S+|"[^"]*")\s+\((.+)\)$/); // allow:regex-no-length-cap — args length already capped upstream
|
|
838
|
+
if (!match) {
|
|
839
|
+
_writeTagged(socket, tag, "BAD SETMETADATA syntax (RFC 5464 §4.3)");
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
var mailbox = _unquote(match[1]);
|
|
843
|
+
var body = match[2];
|
|
844
|
+
if (mailbox !== "" && !_validateMailboxName(mailbox, { allowLegacyMUtf7: allowLegacyMUtf7 })) {
|
|
845
|
+
_writeTagged(socket, tag, "BAD Mailbox name refused");
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
// Tokenise `<entry> <value> <entry> <value> ...`. Values are
|
|
849
|
+
// `"..."` quoted-string OR `NIL`. Entries are `/private/...` /
|
|
850
|
+
// `/shared/...` slash-prefixed names.
|
|
851
|
+
var entries = [];
|
|
852
|
+
var i = 0;
|
|
853
|
+
while (i < body.length) {
|
|
854
|
+
while (i < body.length && /\s/.test(body[i])) i++;
|
|
855
|
+
if (i >= body.length) break;
|
|
856
|
+
var entryStart = i;
|
|
857
|
+
while (i < body.length && !/\s/.test(body[i])) i++;
|
|
858
|
+
var entryName = body.slice(entryStart, i);
|
|
859
|
+
while (i < body.length && /\s/.test(body[i])) i++;
|
|
860
|
+
if (i >= body.length) {
|
|
861
|
+
_writeTagged(socket, tag, "BAD SETMETADATA entry '" + entryName + "' missing value");
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
var valStart = i;
|
|
865
|
+
var value;
|
|
866
|
+
if (body[i] === '"') {
|
|
867
|
+
i++;
|
|
868
|
+
var v = "";
|
|
869
|
+
while (i < body.length && body[i] !== '"') {
|
|
870
|
+
if (body[i] === "\\" && i + 1 < body.length) { v += body[i + 1]; i += 2; }
|
|
871
|
+
else { v += body[i]; i++; }
|
|
872
|
+
}
|
|
873
|
+
if (body[i] !== '"') {
|
|
874
|
+
_writeTagged(socket, tag, "BAD SETMETADATA unterminated quoted value");
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
i++;
|
|
878
|
+
value = v;
|
|
879
|
+
} else {
|
|
880
|
+
while (i < body.length && !/\s/.test(body[i])) i++;
|
|
881
|
+
var tok = body.slice(valStart, i);
|
|
882
|
+
value = tok.toUpperCase() === "NIL" ? null : tok;
|
|
883
|
+
}
|
|
884
|
+
entries.push({ entry: entryName, value: value });
|
|
885
|
+
}
|
|
886
|
+
if (entries.length === 0) {
|
|
887
|
+
_writeTagged(socket, tag, "BAD SETMETADATA empty entry list");
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
Promise.resolve()
|
|
891
|
+
.then(function () { return mailStore.setMetadata(state.actor, mailbox, entries); })
|
|
892
|
+
.then(function () { _writeTagged(socket, tag, "OK SETMETADATA completed"); })
|
|
893
|
+
.catch(function (err) {
|
|
894
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "SETMETADATA failed").slice(0, ERR_CLAMP));
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function _handleCapability(state, socket, tag) {
|
|
899
|
+
_writeUntagged(socket, "CAPABILITY " + _capabilityLine(state));
|
|
900
|
+
_writeTagged(socket, tag, "OK CAPABILITY completed");
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
function _handleId(state, socket, tag, args) {
|
|
904
|
+
// RFC 2971 — clients send a key/value list, server replies with
|
|
905
|
+
// its own. We accept anything (validator caps line size) and reply
|
|
906
|
+
// with a minimal identifier.
|
|
907
|
+
void args;
|
|
908
|
+
_writeUntagged(socket, "ID (\"name\" \"blamejs\" \"version\" \"" + pkgVersion + "\")");
|
|
909
|
+
_writeTagged(socket, tag, "OK ID completed");
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function _handleLogout(state, socket, tag) {
|
|
913
|
+
_writeUntagged(socket, "BYE Logging out");
|
|
914
|
+
_writeTagged(socket, tag, "OK LOGOUT completed");
|
|
915
|
+
_close(socket, state);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
function _handleStartTls(state, socket, tag) {
|
|
919
|
+
if (state.tls) {
|
|
920
|
+
_writeTagged(socket, tag, "BAD TLS already negotiated");
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
_writeTagged(socket, tag, "OK Begin TLS negotiation now");
|
|
924
|
+
// Drain EVERY pre-handshake state field that could carry attacker-
|
|
925
|
+
// controlled bytes past the upgrade boundary (RFC 9051 §11.1 /
|
|
926
|
+
// CVE-2021-33515 class STARTTLS-injection defense):
|
|
927
|
+
// - lineBuffer: unparsed bytes pipelined before the handshake.
|
|
928
|
+
// - pendingLiteral: half-collected APPEND/AUTHENTICATE literal
|
|
929
|
+
// bytes; if not cleared, the literal completes after upgrade
|
|
930
|
+
// using bytes the peer sent in plaintext.
|
|
931
|
+
// - authPending: the AUTHENTICATE step token; a dangling token
|
|
932
|
+
// would let the post-TLS state machine resume an exchange that
|
|
933
|
+
// started in plaintext, conflating cleartext + TLS-protected
|
|
934
|
+
// phases of the same SASL run.
|
|
935
|
+
// Listener-removal + idle-timeout re-arm live in the shared
|
|
936
|
+
// upgradeSocket helper (b.mail.server.tls.upgradeSocket).
|
|
937
|
+
state.lineBuffer = Buffer.alloc(0);
|
|
938
|
+
state.pendingLiteral = null;
|
|
939
|
+
state.authPending = null;
|
|
940
|
+
mailServerTls.upgradeSocket({
|
|
941
|
+
plainSocket: socket,
|
|
942
|
+
secureContext: opts.tlsContext,
|
|
943
|
+
idleTimeoutMs: idleTimeoutMs,
|
|
944
|
+
onSecure: function (_tlsSocket) { state.tls = true; },
|
|
945
|
+
onData: function (tlsSocket, chunk) {
|
|
946
|
+
state.lineBuffer = Buffer.concat([state.lineBuffer, chunk]);
|
|
947
|
+
_drainBuffer(state, tlsSocket);
|
|
948
|
+
},
|
|
949
|
+
onError: function (err) {
|
|
950
|
+
_emit("mail.server.imap.tls_handshake_failed",
|
|
951
|
+
{ connectionId: state.id, error: (err && err.message) || String(err) }, "failure");
|
|
952
|
+
_close(socket, state);
|
|
953
|
+
},
|
|
954
|
+
onTimeout: function (tlsSocket) {
|
|
955
|
+
_writeUntagged(tlsSocket, "BYE Idle timeout");
|
|
956
|
+
_close(tlsSocket, state);
|
|
957
|
+
},
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
function _handleAuthenticate(state, socket, tag, args) {
|
|
962
|
+
if (state.actor) {
|
|
963
|
+
_writeTagged(socket, tag, "BAD Already authenticated");
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (!state.tls && profile !== "permissive") {
|
|
967
|
+
_writeTagged(socket, tag, "BAD AUTHENTICATE requires TLS (use STARTTLS first)");
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
if (!authConfig || typeof authConfig.verify !== "function") {
|
|
971
|
+
_writeTagged(socket, tag, "NO AUTHENTICATE not configured on this listener");
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
var authAdmit = rateLimit.checkAuthAdmit(state.remoteAddress);
|
|
975
|
+
if (!authAdmit.ok) {
|
|
976
|
+
_emit("mail.server.imap.auth_rate_limit_refused",
|
|
977
|
+
{ connectionId: state.id, remoteAddress: state.remoteAddress, reason: authAdmit.reason },
|
|
978
|
+
"denied");
|
|
979
|
+
_writeTagged(socket, tag, "NO [ALERT] Too many AUTH failures from your IP");
|
|
980
|
+
_close(socket, state);
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
var mechName = args.split(" ")[0].toUpperCase();
|
|
984
|
+
var initialResp = args.indexOf(" ") === -1 ? null : args.slice(args.indexOf(" ") + 1).trim();
|
|
985
|
+
var mechanisms = (authConfig.mechanisms || ["PLAIN", "LOGIN"]).map(function (m) {
|
|
986
|
+
return String(m).toUpperCase();
|
|
987
|
+
});
|
|
988
|
+
if (mechanisms.indexOf(mechName) === -1) {
|
|
989
|
+
_writeTagged(socket, tag, "NO Mechanism '" + mechName + "' not advertised");
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
_emit("mail.server.imap.auth_attempt",
|
|
993
|
+
{ connectionId: state.id, mechanism: mechName, remoteAddress: state.remoteAddress });
|
|
994
|
+
state.authPending = { mechanism: mechName, tag: tag, step: 0 };
|
|
995
|
+
_runAuthStep(state, socket, initialResp);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function _runAuthStep(state, socket, clientResp) {
|
|
999
|
+
var pending = state.authPending;
|
|
1000
|
+
Promise.resolve()
|
|
1001
|
+
.then(function () {
|
|
1002
|
+
return authConfig.verify(pending.mechanism, {
|
|
1003
|
+
step: pending.step,
|
|
1004
|
+
clientResponse: clientResp,
|
|
1005
|
+
tls: state.tls,
|
|
1006
|
+
remoteAddress: state.remoteAddress,
|
|
1007
|
+
});
|
|
1008
|
+
})
|
|
1009
|
+
.then(function (result) {
|
|
1010
|
+
pending.step += 1;
|
|
1011
|
+
if (result && result.pending && typeof result.challenge === "string") {
|
|
1012
|
+
// Server-side challenge — `+ <base64>` per RFC 9051 §6.2.2.
|
|
1013
|
+
_writeContinuation(socket, result.challenge);
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
if (result && result.ok === true && result.actor) {
|
|
1017
|
+
state.actor = result.actor;
|
|
1018
|
+
state.stage = "authenticated";
|
|
1019
|
+
var savedTag = pending.tag;
|
|
1020
|
+
state.authPending = null;
|
|
1021
|
+
_emit("mail.server.imap.auth_success",
|
|
1022
|
+
{ connectionId: state.id, mechanism: pending.mechanism,
|
|
1023
|
+
tenantId: result.actor.tenantId || null });
|
|
1024
|
+
_writeTagged(socket, savedTag, "OK [CAPABILITY " + _capabilityLine(state) + "] AUTHENTICATE completed");
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
var failTag = pending.tag;
|
|
1028
|
+
state.authPending = null;
|
|
1029
|
+
rateLimit.noteAuthFailure(state.remoteAddress);
|
|
1030
|
+
_emit("mail.server.imap.auth_failed",
|
|
1031
|
+
{ connectionId: state.id, mechanism: pending.mechanism,
|
|
1032
|
+
reason: (result && result.reason) || "verify-returned-fail" }, "denied");
|
|
1033
|
+
_writeTagged(socket, failTag, "NO Authentication credentials invalid");
|
|
1034
|
+
})
|
|
1035
|
+
.catch(function (err) {
|
|
1036
|
+
var failTag = pending.tag;
|
|
1037
|
+
state.authPending = null;
|
|
1038
|
+
rateLimit.noteAuthFailure(state.remoteAddress);
|
|
1039
|
+
_emit("mail.server.imap.auth_failed",
|
|
1040
|
+
{ connectionId: state.id, mechanism: pending.mechanism,
|
|
1041
|
+
reason: (err && err.message) || String(err) }, "failure");
|
|
1042
|
+
_writeTagged(socket, failTag, "NO Authentication failed");
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
function _handleLogin(state, socket, tag, args) {
|
|
1047
|
+
// RFC 9051 §6.3.4 — LOGIN is deprecated; new MUAs use AUTHENTICATE.
|
|
1048
|
+
if (state.actor) {
|
|
1049
|
+
_writeTagged(socket, tag, "BAD Already authenticated");
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
if (profile === "strict") {
|
|
1053
|
+
_writeTagged(socket, tag, "BAD LOGIN deprecated under strict profile; use AUTHENTICATE");
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
if (!state.tls && profile !== "permissive") {
|
|
1057
|
+
_writeTagged(socket, tag, "BAD LOGIN requires TLS (use STARTTLS first)");
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
if (!authConfig || typeof authConfig.verify !== "function") {
|
|
1061
|
+
_writeTagged(socket, tag, "NO AUTH not configured");
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
var authAdmit = rateLimit.checkAuthAdmit(state.remoteAddress);
|
|
1065
|
+
if (!authAdmit.ok) {
|
|
1066
|
+
_writeTagged(socket, tag, "NO [ALERT] Too many AUTH failures from your IP");
|
|
1067
|
+
_close(socket, state);
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
// LOGIN args: `user pass` (quoted or atom).
|
|
1071
|
+
var parts = _parseLoginArgs(args);
|
|
1072
|
+
if (!parts) {
|
|
1073
|
+
_writeTagged(socket, tag, "BAD LOGIN expects user + pass");
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
Promise.resolve()
|
|
1077
|
+
.then(function () {
|
|
1078
|
+
return authConfig.verify("LOGIN", {
|
|
1079
|
+
step: 0,
|
|
1080
|
+
username: parts[0],
|
|
1081
|
+
password: parts[1],
|
|
1082
|
+
tls: state.tls,
|
|
1083
|
+
remoteAddress: state.remoteAddress,
|
|
1084
|
+
});
|
|
1085
|
+
})
|
|
1086
|
+
.then(function (result) {
|
|
1087
|
+
if (result && result.ok && result.actor) {
|
|
1088
|
+
state.actor = result.actor;
|
|
1089
|
+
state.stage = "authenticated";
|
|
1090
|
+
_emit("mail.server.imap.auth_success",
|
|
1091
|
+
{ connectionId: state.id, mechanism: "LOGIN", tenantId: result.actor.tenantId || null });
|
|
1092
|
+
_writeTagged(socket, tag, "OK [CAPABILITY " + _capabilityLine(state) + "] LOGIN completed");
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
rateLimit.noteAuthFailure(state.remoteAddress);
|
|
1096
|
+
_emit("mail.server.imap.auth_failed",
|
|
1097
|
+
{ connectionId: state.id, mechanism: "LOGIN", reason: "verify-returned-fail" }, "denied");
|
|
1098
|
+
_writeTagged(socket, tag, "NO LOGIN credentials invalid");
|
|
1099
|
+
})
|
|
1100
|
+
.catch(function () {
|
|
1101
|
+
rateLimit.noteAuthFailure(state.remoteAddress);
|
|
1102
|
+
_writeTagged(socket, tag, "NO LOGIN failed");
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
function _parseLoginArgs(args) {
|
|
1107
|
+
if (typeof args !== "string") return null;
|
|
1108
|
+
// Quoted or atom — RFC 9051 §5.1 quoted ABNF. Inside a quoted
|
|
1109
|
+
// string `\"` and `\\` are escape sequences for `"` and `\`
|
|
1110
|
+
// respectively; any other `\<chr>` is invalid. The earlier shape
|
|
1111
|
+
// terminated the quoted string at the first `"`, so a hostile
|
|
1112
|
+
// client passing `LOGIN "alice\"@example.com" "pw"` would have
|
|
1113
|
+
// its username truncated at `alice` and the rest of the line
|
|
1114
|
+
// reparsed as the password / literal — wrong identity bound to
|
|
1115
|
+
// the AUTH state.
|
|
1116
|
+
var rest = args.trim();
|
|
1117
|
+
function _take() {
|
|
1118
|
+
if (rest[0] === "\"") {
|
|
1119
|
+
// Walk the quoted-string body, accumulating into `out` while
|
|
1120
|
+
// honoring the `\"` / `\\` escape pairs. A bare `\` followed
|
|
1121
|
+
// by any other character is refused (parse fails → null).
|
|
1122
|
+
var out = "";
|
|
1123
|
+
var i = 1;
|
|
1124
|
+
while (i < rest.length) {
|
|
1125
|
+
var ch = rest.charAt(i);
|
|
1126
|
+
if (ch === "\\") {
|
|
1127
|
+
var esc = rest.charAt(i + 1);
|
|
1128
|
+
if (esc !== "\"" && esc !== "\\") return null;
|
|
1129
|
+
out += esc;
|
|
1130
|
+
i += 2;
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
if (ch === "\"") {
|
|
1134
|
+
rest = rest.slice(i + 1).trim();
|
|
1135
|
+
return out;
|
|
1136
|
+
}
|
|
1137
|
+
out += ch;
|
|
1138
|
+
i += 1;
|
|
1139
|
+
}
|
|
1140
|
+
return null; // unterminated quoted string
|
|
1141
|
+
}
|
|
1142
|
+
var sp = rest.indexOf(" ");
|
|
1143
|
+
var v2 = sp === -1 ? rest : rest.slice(0, sp);
|
|
1144
|
+
rest = sp === -1 ? "" : rest.slice(sp + 1).trim();
|
|
1145
|
+
return v2;
|
|
1146
|
+
}
|
|
1147
|
+
var user = _take(); if (user === null) return null;
|
|
1148
|
+
var pass = _take(); if (pass === null) return null;
|
|
1149
|
+
return [user, pass];
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
function _requireAuth(state, socket, tag) {
|
|
1153
|
+
if (!state.actor) {
|
|
1154
|
+
_writeTagged(socket, tag, "NO Login first");
|
|
1155
|
+
return false;
|
|
1156
|
+
}
|
|
1157
|
+
return true;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function _handleSelect(state, socket, tag, args, examine) {
|
|
1161
|
+
if (!_requireAuth(state, socket, tag)) return;
|
|
1162
|
+
var trimmed = (args || "").trim();
|
|
1163
|
+
// RFC 7162 §3.2.4 — `SELECT mailbox (QRESYNC (<uidvalidity>
|
|
1164
|
+
// <modseq> [<knownUids>] [<knownSequenceMatchData>]))`. The
|
|
1165
|
+
// QRESYNC parameter is wrapped in an outer parenthesis pair after
|
|
1166
|
+
// the mailbox name. Extract it before parsing the mailbox so the
|
|
1167
|
+
// mailbox-name validator sees just the name.
|
|
1168
|
+
var qresyncParam = null;
|
|
1169
|
+
var qresyncMatch = trimmed.match(/^(\S+|"[^"]+")\s+\(\s*QRESYNC\s*\(\s*([^)]+)\)\s*(?:\(\s*([^)]+)\)\s*)?\)\s*$/i); // allow:regex-no-length-cap — args length already capped upstream
|
|
1170
|
+
if (qresyncMatch) {
|
|
1171
|
+
var inner = qresyncMatch[2].trim().split(/\s+/);
|
|
1172
|
+
qresyncParam = {
|
|
1173
|
+
uidvalidity: parseInt(inner[0], 10),
|
|
1174
|
+
modseq: parseInt(inner[1], 10),
|
|
1175
|
+
knownUids: inner[2] || null,
|
|
1176
|
+
knownSeq: qresyncMatch[3] || null,
|
|
1177
|
+
};
|
|
1178
|
+
if (!isFinite(qresyncParam.uidvalidity) || !isFinite(qresyncParam.modseq)) {
|
|
1179
|
+
_writeTagged(socket, tag, "BAD SELECT QRESYNC params must be (<uidvalidity> <modseq> ...) numerics");
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
trimmed = qresyncMatch[1];
|
|
1183
|
+
}
|
|
1184
|
+
var name = _unquote(trimmed);
|
|
1185
|
+
if (!_validateMailboxName(name, { allowLegacyMUtf7: allowLegacyMUtf7 })) {
|
|
1186
|
+
_writeTagged(socket, tag, "BAD Mailbox name refused");
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
// QRESYNC requires CONDSTORE to be engaged; if the client sent
|
|
1190
|
+
// the parameter without having issued ENABLE first, RFC 7162
|
|
1191
|
+
// §3.2.4 lets the server flip the flags implicitly.
|
|
1192
|
+
if (qresyncParam && !state.enabledQResync) {
|
|
1193
|
+
state.enabledQResync = true;
|
|
1194
|
+
state.enabledCondStore = true;
|
|
1195
|
+
}
|
|
1196
|
+
Promise.resolve()
|
|
1197
|
+
.then(function () {
|
|
1198
|
+
if (typeof mailStore.selectFolder === "function") {
|
|
1199
|
+
return mailStore.selectFolder(state.actor, name, {
|
|
1200
|
+
readOnly: examine,
|
|
1201
|
+
qresync: qresyncParam,
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
// RFC 9051 §2.3.1.1 — UIDVALIDITY MUST be strictly increasing
|
|
1205
|
+
// and 32-bit unique across the mailbox lifetime. The earlier
|
|
1206
|
+
// fallback returned a sentinel `uidvalidity: 1` to keep tests
|
|
1207
|
+
// green when the operator hadn't wired `selectFolder`, but the
|
|
1208
|
+
// sentinel value collides with any real UIDVALIDITY=1 from a
|
|
1209
|
+
// legitimate backend and tricks clients into believing they
|
|
1210
|
+
// have a valid synced state. Refuse SELECT instead — operators
|
|
1211
|
+
// MUST wire `mailStore.selectFolder` to expose mailboxes.
|
|
1212
|
+
var err = new Error("mailStore.selectFolder is not configured (RFC 9051 §2.3.1.1 requires a unique strictly-increasing UIDVALIDITY)");
|
|
1213
|
+
err.code = "mail-server-imap/no-select-backend";
|
|
1214
|
+
throw err;
|
|
1215
|
+
})
|
|
1216
|
+
.then(function (info) {
|
|
1217
|
+
state.selectedMailbox = name;
|
|
1218
|
+
state.selectedReadOnly = !!examine;
|
|
1219
|
+
state.stage = "selected";
|
|
1220
|
+
var flagsStr = (info.flags && info.flags.length) ? info.flags.join(" ") : "\\Seen \\Answered \\Flagged \\Deleted \\Draft";
|
|
1221
|
+
_writeUntagged(socket, info.exists + " EXISTS");
|
|
1222
|
+
_writeUntagged(socket, info.recent + " RECENT");
|
|
1223
|
+
_writeUntagged(socket, "FLAGS (" + flagsStr + ")");
|
|
1224
|
+
_writeUntagged(socket, "OK [UIDVALIDITY " + info.uidvalidity + "] UIDs valid");
|
|
1225
|
+
_writeUntagged(socket, "OK [UIDNEXT " + info.uidnext + "] Predicted next UID");
|
|
1226
|
+
if (info.modseq !== undefined) {
|
|
1227
|
+
_writeUntagged(socket, "OK [HIGHESTMODSEQ " + info.modseq + "]");
|
|
1228
|
+
}
|
|
1229
|
+
// RFC 7162 §3.2.5 — when SELECT carried a QRESYNC parameter
|
|
1230
|
+
// AND the client's UIDVALIDITY matches, emit a single
|
|
1231
|
+
// `* VANISHED (EARLIER) <uid-set>` listing UIDs the server
|
|
1232
|
+
// expunged since the client's snapshot. The backend supplies
|
|
1233
|
+
// this via `info.vanishedEarlier` (sequence-set string) — the
|
|
1234
|
+
// listener does the wire emission. Mismatched UIDVALIDITY
|
|
1235
|
+
// means the client's cache is stale and MUST re-SELECT; we
|
|
1236
|
+
// skip the VANISHED line in that case so the client falls
|
|
1237
|
+
// through to a full re-sync. RFC 7162 §3.2.5.2 says the
|
|
1238
|
+
// server MAY also include changed-since-modseq FETCH lines
|
|
1239
|
+
// — those flow through the normal FETCH path with
|
|
1240
|
+
// CHANGEDSINCE so we leave them to the operator.
|
|
1241
|
+
if (qresyncParam && info.vanishedEarlier &&
|
|
1242
|
+
info.uidvalidity === qresyncParam.uidvalidity) {
|
|
1243
|
+
_writeUntagged(socket, "VANISHED (EARLIER) " + info.vanishedEarlier);
|
|
1244
|
+
}
|
|
1245
|
+
_emit("mail.server.imap.select", {
|
|
1246
|
+
connectionId: state.id, mailbox: name,
|
|
1247
|
+
modseq: info.modseq || 0, exists: info.exists,
|
|
1248
|
+
qresync: qresyncParam !== null,
|
|
1249
|
+
});
|
|
1250
|
+
_writeTagged(socket, tag, "OK [" + (examine ? "READ-ONLY" : "READ-WRITE") + "] " +
|
|
1251
|
+
(examine ? "EXAMINE" : "SELECT") + " completed");
|
|
1252
|
+
})
|
|
1253
|
+
.catch(function (err) {
|
|
1254
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "Select failed").slice(0, ERR_CLAMP));
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
function _handleList(state, socket, tag, args) {
|
|
1259
|
+
if (!_requireAuth(state, socket, tag)) return;
|
|
1260
|
+
// RFC 9051 §6.3.9 — LIST reference mailbox-pattern. Minimal
|
|
1261
|
+
// implementation delegates to mailStore.listFolders if present.
|
|
1262
|
+
void args;
|
|
1263
|
+
Promise.resolve()
|
|
1264
|
+
.then(function () {
|
|
1265
|
+
if (typeof mailStore.listFolders === "function") {
|
|
1266
|
+
return mailStore.listFolders(state.actor);
|
|
1267
|
+
}
|
|
1268
|
+
return [{ name: "INBOX", attributes: [] }];
|
|
1269
|
+
})
|
|
1270
|
+
.then(function (folders) {
|
|
1271
|
+
for (var i = 0; i < folders.length; i += 1) {
|
|
1272
|
+
var f = folders[i];
|
|
1273
|
+
var attrs = (f.attributes || []).map(function (a) { return "\\" + a; }).join(" ");
|
|
1274
|
+
_writeUntagged(socket, "LIST (" + attrs + ") \"/\" " + _quote(f.name));
|
|
1275
|
+
}
|
|
1276
|
+
_writeTagged(socket, tag, "OK LIST completed");
|
|
1277
|
+
})
|
|
1278
|
+
.catch(function (err) {
|
|
1279
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "List failed").slice(0, ERR_CLAMP));
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
function _handleStatus(state, socket, tag, args) {
|
|
1284
|
+
if (!_requireAuth(state, socket, tag)) return;
|
|
1285
|
+
var match = args.match(/^(\S+|"[^"]+")\s+\(([^)]+)\)$/); // allow:regex-no-length-cap — args length already capped upstream
|
|
1286
|
+
if (!match) {
|
|
1287
|
+
_writeTagged(socket, tag, "BAD STATUS expects mailbox + paren-list of items");
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
var name = _unquote(match[1]);
|
|
1291
|
+
var items = match[2].split(/\s+/);
|
|
1292
|
+
if (!_validateMailboxName(name, { allowLegacyMUtf7: allowLegacyMUtf7 })) {
|
|
1293
|
+
_writeTagged(socket, tag, "BAD Mailbox name refused");
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
Promise.resolve()
|
|
1297
|
+
.then(function () {
|
|
1298
|
+
if (typeof mailStore.statusFolder === "function") {
|
|
1299
|
+
return mailStore.statusFolder(state.actor, name, items);
|
|
1300
|
+
}
|
|
1301
|
+
return { MESSAGES: 0, UIDNEXT: 1, UIDVALIDITY: 1, UNSEEN: 0 };
|
|
1302
|
+
})
|
|
1303
|
+
.then(function (info) {
|
|
1304
|
+
var parts = [];
|
|
1305
|
+
for (var k = 0; k < items.length; k += 1) {
|
|
1306
|
+
var key = items[k].toUpperCase();
|
|
1307
|
+
if (info[key] !== undefined) parts.push(key + " " + info[key]);
|
|
1308
|
+
}
|
|
1309
|
+
_writeUntagged(socket, "STATUS " + _quote(name) + " (" + parts.join(" ") + ")");
|
|
1310
|
+
_writeTagged(socket, tag, "OK STATUS completed");
|
|
1311
|
+
})
|
|
1312
|
+
.catch(function (err) {
|
|
1313
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "Status failed").slice(0, ERR_CLAMP));
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
function _handleAppend(state, socket, tag, args, literalBody) {
|
|
1318
|
+
if (!_requireAuth(state, socket, tag)) return;
|
|
1319
|
+
// RFC 4469 CATENATE — `APPEND mailbox [(flags)] [date-time] CATENATE
|
|
1320
|
+
// (TEXT {literal} URL "imap://...")`. The CATENATE keyword turns the
|
|
1321
|
+
// command body into a list of parts the server stitches into a
|
|
1322
|
+
// single message; backends supply the `appendCatenate(actor,
|
|
1323
|
+
// mailbox, parts, opts) → meta` hook. Without CATENATE, fall
|
|
1324
|
+
// through to the bare APPEND path that already exists.
|
|
1325
|
+
var catenateMatch = args.match(/^(\S+|"[^"]+")(?:\s+\(([^)]*)\))?(?:\s+("[^"]+"))?\s+CATENATE\s+(.+)$/i); // allow:regex-no-length-cap — args length already capped upstream
|
|
1326
|
+
if (catenateMatch) {
|
|
1327
|
+
if (typeof mailStore.appendCatenate !== "function") {
|
|
1328
|
+
_writeTagged(socket, tag, "NO CATENATE backend not configured");
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
var catMailbox = _unquote(catenateMatch[1]);
|
|
1332
|
+
var catFlags = catenateMatch[2] ? catenateMatch[2].split(/\s+/).filter(Boolean) : [];
|
|
1333
|
+
var catDateArg = catenateMatch[3] ? _unquote(catenateMatch[3]) : null;
|
|
1334
|
+
var catInternalDate = null;
|
|
1335
|
+
if (catDateArg) {
|
|
1336
|
+
catInternalDate = _parseImapDateTime(catDateArg);
|
|
1337
|
+
if (catInternalDate === null) {
|
|
1338
|
+
_writeTagged(socket, tag, "BAD APPEND CATENATE date-time invalid");
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (!_validateMailboxName(catMailbox, { allowLegacyMUtf7: allowLegacyMUtf7 })) {
|
|
1343
|
+
_writeTagged(socket, tag, "BAD Mailbox name refused");
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
// Validate the parens are well-formed BEFORE we touch the
|
|
1347
|
+
// backend. The wire-format parts list MUST start with `(` and
|
|
1348
|
+
// end with `)`; a truncated list (e.g. `(TEXT {3}` arriving as
|
|
1349
|
+
// a single literal-completion before the rest of the parts
|
|
1350
|
+
// streams in) is refused. Order-preserving left-to-right token
|
|
1351
|
+
// walk replaces the prior URL-then-TEXT split — CATENATE
|
|
1352
|
+
// semantics depend on the SEQUENCE of parts.
|
|
1353
|
+
var partsBodyRaw = catenateMatch[4];
|
|
1354
|
+
if (partsBodyRaw[0] !== "(" || partsBodyRaw[partsBodyRaw.length - 1] !== ")") {
|
|
1355
|
+
_writeTagged(socket, tag, "BAD APPEND CATENATE parts list missing parens (RFC 4469 §3)");
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
var partsBody = partsBodyRaw.slice(1, -1);
|
|
1359
|
+
var parts = [];
|
|
1360
|
+
var hadTextPart = false;
|
|
1361
|
+
// Tokenise sequentially. Each part is one of:
|
|
1362
|
+
// URL "imap://..."
|
|
1363
|
+
// TEXT {<n>} (literal — multi-literal CATENATE deferred to a
|
|
1364
|
+
// later slice; defer-with-condition: refused
|
|
1365
|
+
// with NO until the multi-literal protocol path
|
|
1366
|
+
// lands).
|
|
1367
|
+
var pi = 0;
|
|
1368
|
+
while (pi < partsBody.length) {
|
|
1369
|
+
while (pi < partsBody.length && /\s/.test(partsBody[pi])) pi += 1;
|
|
1370
|
+
if (pi >= partsBody.length) break;
|
|
1371
|
+
if (/^URL\b/i.test(partsBody.slice(pi))) {
|
|
1372
|
+
pi += 3; // allow:raw-byte-literal — length of literal "URL" keyword
|
|
1373
|
+
while (pi < partsBody.length && /\s/.test(partsBody[pi])) pi += 1;
|
|
1374
|
+
if (partsBody[pi] !== "\"") {
|
|
1375
|
+
_writeTagged(socket, tag, "BAD APPEND CATENATE URL value must be quoted-string");
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
pi += 1;
|
|
1379
|
+
var urlStart = pi;
|
|
1380
|
+
while (pi < partsBody.length && partsBody[pi] !== "\"") pi += 1;
|
|
1381
|
+
if (partsBody[pi] !== "\"") {
|
|
1382
|
+
_writeTagged(socket, tag, "BAD APPEND CATENATE URL value unterminated quoted-string");
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
parts.push({ kind: "URL", url: partsBody.slice(urlStart, pi) });
|
|
1386
|
+
pi += 1;
|
|
1387
|
+
} else if (/^TEXT\b/i.test(partsBody.slice(pi))) {
|
|
1388
|
+
hadTextPart = true;
|
|
1389
|
+
break;
|
|
1390
|
+
} else {
|
|
1391
|
+
_writeTagged(socket, tag, "BAD APPEND CATENATE unknown part (RFC 4469 §3 only URL/TEXT)");
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (hadTextPart) {
|
|
1396
|
+
// Multi-literal CATENATE TEXT parts need a streaming-literal
|
|
1397
|
+
// protocol path the listener doesn't currently expose. RFC
|
|
1398
|
+
// 4469 §3 explicitly permits servers to refuse parts they
|
|
1399
|
+
// can't honour; refusing is correct (better than reordering
|
|
1400
|
+
// and corrupting the message body the client requested).
|
|
1401
|
+
_writeTagged(socket, tag, "NO CATENATE TEXT-literal parts not yet implemented; use APPEND with a single literal");
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
if (parts.length === 0) {
|
|
1405
|
+
_writeTagged(socket, tag, "BAD APPEND CATENATE empty parts list");
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
Promise.resolve()
|
|
1409
|
+
.then(function () {
|
|
1410
|
+
return mailStore.appendCatenate(catMailbox, parts, {
|
|
1411
|
+
actor: state.actor, flags: catFlags, internalDate: catInternalDate });
|
|
1412
|
+
})
|
|
1413
|
+
.then(function (meta) {
|
|
1414
|
+
var ok = "OK APPEND completed";
|
|
1415
|
+
if (meta && meta.uid && meta.uidValidity) {
|
|
1416
|
+
ok = "OK [APPENDUID " + meta.uidValidity + " " + meta.uid + "] APPEND completed";
|
|
1417
|
+
}
|
|
1418
|
+
_writeTagged(socket, tag, ok);
|
|
1419
|
+
})
|
|
1420
|
+
.catch(function (err) {
|
|
1421
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "CATENATE failed").slice(0, ERR_CLAMP));
|
|
1422
|
+
});
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
if (!literalBody) {
|
|
1426
|
+
_writeTagged(socket, tag, "BAD APPEND requires a literal {N} message");
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
// RFC 9051 §6.3.12 — APPEND mailbox [(flags)] [date-time] literal
|
|
1430
|
+
var match = args.match(/^(\S+|"[^"]+")(?:\s+\(([^)]*)\))?(?:\s+("[^"]+"))?$/); // allow:regex-no-length-cap — args length already capped upstream
|
|
1431
|
+
if (!match) {
|
|
1432
|
+
_writeTagged(socket, tag, "BAD APPEND syntax");
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
var name = _unquote(match[1]);
|
|
1436
|
+
var flags = match[2] ? match[2].split(/\s+/).filter(Boolean) : [];
|
|
1437
|
+
// RFC 9051 §6.3.12 — optional date-time argument sets INTERNALDATE
|
|
1438
|
+
// on the appended message. Earlier shape captured the token but
|
|
1439
|
+
// never threaded it; backends now receive it as `internalDate`
|
|
1440
|
+
// (ms-since-epoch) and the mail-store applies it instead of the
|
|
1441
|
+
// append-time clock. Refused as syntax error when the date-time
|
|
1442
|
+
// can't be parsed (rather than silently using the clock).
|
|
1443
|
+
var dateTimeArg = match[3] ? _unquote(match[3]) : null;
|
|
1444
|
+
var internalDate = null;
|
|
1445
|
+
if (dateTimeArg) {
|
|
1446
|
+
internalDate = _parseImapDateTime(dateTimeArg);
|
|
1447
|
+
if (internalDate === null) {
|
|
1448
|
+
_writeTagged(socket, tag, "BAD APPEND date-time '" + dateTimeArg +
|
|
1449
|
+
"' not in RFC 9051 §6.3.12 / RFC 5322 §3.3 date-time grammar");
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
if (!_validateMailboxName(name, { allowLegacyMUtf7: allowLegacyMUtf7 })) {
|
|
1454
|
+
_writeTagged(socket, tag, "BAD Mailbox name refused");
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
Promise.resolve()
|
|
1458
|
+
.then(function () {
|
|
1459
|
+
// RFC 9208 — when the backend exposes a per-mailbox / per-user
|
|
1460
|
+
// quota, APPEND MUST check against it BEFORE writing the
|
|
1461
|
+
// message. The earlier shape called `appendMessage` directly,
|
|
1462
|
+
// leaving quota enforcement entirely up to the backend; an
|
|
1463
|
+
// operator wiring a bare `appendMessage` without quota plumbing
|
|
1464
|
+
// could be DoS'd via unbounded APPENDs filling the mailbox
|
|
1465
|
+
// beyond the advertised QUOTA limit. Honor `mailStore.quota`
|
|
1466
|
+
// (RFC 9208 GETQUOTA / IMAP-QUOTA returns the same shape) and
|
|
1467
|
+
// surface 5.7.4 OVERQUOTA per §5.
|
|
1468
|
+
if (typeof mailStore.quota === "function") {
|
|
1469
|
+
// mailStore.quota(folderName) returns
|
|
1470
|
+
// { usedBytes, usedCount, capBytes, capCount } per the
|
|
1471
|
+
// lib/mail-store.js contract. capBytes is null when no
|
|
1472
|
+
// quota is configured for the folder; honor it only when
|
|
1473
|
+
// it's a positive number.
|
|
1474
|
+
return Promise.resolve(mailStore.quota(name))
|
|
1475
|
+
.then(function (q) {
|
|
1476
|
+
if (q && typeof q.usedBytes === "number" &&
|
|
1477
|
+
typeof q.capBytes === "number" &&
|
|
1478
|
+
q.capBytes > 0 &&
|
|
1479
|
+
q.usedBytes + literalBody.length > q.capBytes) {
|
|
1480
|
+
var err = new Error("APPEND would exceed quota (used " + q.usedBytes +
|
|
1481
|
+
" + " + literalBody.length + " > cap " + q.capBytes + ")");
|
|
1482
|
+
err.code = "mail-server-imap/overquota";
|
|
1483
|
+
err.overquota = true;
|
|
1484
|
+
err.limit = q.capBytes;
|
|
1485
|
+
throw err;
|
|
1486
|
+
}
|
|
1487
|
+
return mailStore.appendMessage(name, literalBody, {
|
|
1488
|
+
actor: state.actor, flags: flags, internalDate: internalDate });
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
return mailStore.appendMessage(name, literalBody, {
|
|
1492
|
+
actor: state.actor, flags: flags, internalDate: internalDate });
|
|
1493
|
+
})
|
|
1494
|
+
.then(function (info) {
|
|
1495
|
+
_emit("mail.server.imap.append",
|
|
1496
|
+
{ connectionId: state.id, mailbox: name, size: literalBody.length, flags: flags });
|
|
1497
|
+
var token = info && info.uid ? "[APPENDUID " + (info.uidvalidity || 0) + " " + info.uid + "] " : "";
|
|
1498
|
+
_writeTagged(socket, tag, "OK " + token + "APPEND completed");
|
|
1499
|
+
})
|
|
1500
|
+
.catch(function (err) {
|
|
1501
|
+
if (err && err.overquota) {
|
|
1502
|
+
_writeTagged(socket, tag, "NO [OVERQUOTA] Quota exceeded (RFC 9208 §5)");
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "Append failed").slice(0, ERR_CLAMP));
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
function _handleClose(state, socket, tag) {
|
|
1510
|
+
state.selectedMailbox = null;
|
|
1511
|
+
state.selectedReadOnly = false;
|
|
1512
|
+
state.stage = "authenticated";
|
|
1513
|
+
_writeTagged(socket, tag, "OK CLOSE completed");
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function _handleExpunge(state, socket, tag) {
|
|
1517
|
+
if (!state.selectedMailbox) {
|
|
1518
|
+
_writeTagged(socket, tag, "NO No mailbox selected");
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
Promise.resolve()
|
|
1522
|
+
.then(function () {
|
|
1523
|
+
if (typeof mailStore.expungeFolder === "function") {
|
|
1524
|
+
return mailStore.expungeFolder(state.actor, state.selectedMailbox);
|
|
1525
|
+
}
|
|
1526
|
+
return { expunged: [], modseq: 0 };
|
|
1527
|
+
})
|
|
1528
|
+
.then(function (info) {
|
|
1529
|
+
var ex = info.expunged || [];
|
|
1530
|
+
for (var i = 0; i < ex.length; i += 1) {
|
|
1531
|
+
_writeUntagged(socket, ex[i] + " EXPUNGE");
|
|
1532
|
+
}
|
|
1533
|
+
_emit("mail.server.imap.expunge",
|
|
1534
|
+
{ connectionId: state.id, mailbox: state.selectedMailbox,
|
|
1535
|
+
count: ex.length, modseq: info.modseq || 0 });
|
|
1536
|
+
_writeTagged(socket, tag, "OK EXPUNGE completed");
|
|
1537
|
+
})
|
|
1538
|
+
.catch(function (err) {
|
|
1539
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "Expunge failed").slice(0, ERR_CLAMP));
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
function _handleFetch(state, socket, tag, args, useUid) {
|
|
1544
|
+
if (!state.selectedMailbox) {
|
|
1545
|
+
// RFC 9051 §6.4.5 — FETCH outside of Selected state is a
|
|
1546
|
+
// protocol-context violation, not a server-policy refusal.
|
|
1547
|
+
// BAD signals the client to fix its dialog rather than retry.
|
|
1548
|
+
_writeTagged(socket, tag, "BAD FETCH only valid in Selected state (RFC 9051 §6.4.5)");
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
if (typeof mailStore.fetchRange !== "function") {
|
|
1552
|
+
_writeTagged(socket, tag, "BAD FETCH backend not configured");
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
var match = args.match(/^(\S+)\s+(.+)$/); // allow:regex-no-length-cap — args length already capped upstream
|
|
1556
|
+
if (!match) {
|
|
1557
|
+
_writeTagged(socket, tag, "BAD FETCH expects sequence-set + parts");
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
var seqSet = match[1];
|
|
1561
|
+
var partsSpec = match[2];
|
|
1562
|
+
// RFC 7162 §3.1.4 — FETCH may carry a CHANGEDSINCE modifier in a
|
|
1563
|
+
// trailing parenthesised list:
|
|
1564
|
+
// FETCH 1:* (FLAGS) (CHANGEDSINCE 12345)
|
|
1565
|
+
// and/or VANISHED (QRESYNC) which is deferred to a later slice.
|
|
1566
|
+
// The modifier list is parsed off the END of partsSpec; what
|
|
1567
|
+
// remains is handed to the backend as the fetch-att spec.
|
|
1568
|
+
var changedSince = null;
|
|
1569
|
+
var includeVanished = false;
|
|
1570
|
+
var modMatch = partsSpec.match(/\s*\(([^)]*)\)\s*$/); // allow:regex-no-length-cap — partsSpec already bounded upstream
|
|
1571
|
+
if (modMatch && /\b(CHANGEDSINCE|VANISHED)\b/i.test(modMatch[1])) {
|
|
1572
|
+
var modBody = modMatch[1];
|
|
1573
|
+
var changedMatch = modBody.match(/CHANGEDSINCE\s+(\d+)/i); // allow:regex-no-length-cap — modBody already bounded
|
|
1574
|
+
if (changedMatch) {
|
|
1575
|
+
var csN = parseInt(changedMatch[1], 10);
|
|
1576
|
+
if (isFinite(csN) && csN >= 0) changedSince = csN;
|
|
1577
|
+
}
|
|
1578
|
+
includeVanished = /\bVANISHED\b/i.test(modBody);
|
|
1579
|
+
partsSpec = partsSpec.slice(0, partsSpec.length - modMatch[0].length).trim();
|
|
1580
|
+
}
|
|
1581
|
+
// RFC 7162 §3.1.2 — any FETCH that uses CHANGEDSINCE implicitly
|
|
1582
|
+
// engages CONDSTORE for the session; the client expects MODSEQ
|
|
1583
|
+
// in responses even without a prior `ENABLE CONDSTORE`. RFC 7162
|
|
1584
|
+
// §3.1.4.1 — when CONDSTORE is engaged (explicit ENABLE OR
|
|
1585
|
+
// implicit via CHANGEDSINCE) OR the client requested MODSEQ as a
|
|
1586
|
+
// fetch-att, every untagged FETCH response includes the MODSEQ
|
|
1587
|
+
// attribute. Engaging CONDSTORE via CHANGEDSINCE also sticks for
|
|
1588
|
+
// the rest of the session.
|
|
1589
|
+
if (changedSince !== null && !state.enabledCondStore) {
|
|
1590
|
+
state.enabledCondStore = true;
|
|
1591
|
+
}
|
|
1592
|
+
var includeModseq = state.enabledCondStore === true ||
|
|
1593
|
+
changedSince !== null ||
|
|
1594
|
+
/\bMODSEQ\b/i.test(partsSpec);
|
|
1595
|
+
Promise.resolve()
|
|
1596
|
+
.then(function () {
|
|
1597
|
+
return mailStore.fetchRange(state.actor, state.selectedMailbox, seqSet, partsSpec,
|
|
1598
|
+
{ useUid: useUid === true, changedSince: changedSince, includeVanished: includeVanished,
|
|
1599
|
+
includeModseq: includeModseq });
|
|
1600
|
+
})
|
|
1601
|
+
.then(function (rows) {
|
|
1602
|
+
var rs = rows || [];
|
|
1603
|
+
_emit("mail.server.imap.fetch_bulk",
|
|
1604
|
+
{ connectionId: state.id, mailbox: state.selectedMailbox, count: rs.length,
|
|
1605
|
+
changedSince: changedSince, condStore: state.enabledCondStore === true });
|
|
1606
|
+
for (var i = 0; i < rs.length; i += 1) {
|
|
1607
|
+
var r = rs[i];
|
|
1608
|
+
var payload = r.payload || "";
|
|
1609
|
+
if (includeModseq && r.modseq !== undefined && !/MODSEQ\s*\(/.test(payload)) {
|
|
1610
|
+
payload = (payload ? payload + " " : "") + "MODSEQ (" + r.modseq + ")";
|
|
1611
|
+
}
|
|
1612
|
+
_writeUntagged(socket, r.seq + " FETCH (" + payload + ")");
|
|
1613
|
+
}
|
|
1614
|
+
_writeTagged(socket, tag, "OK FETCH completed");
|
|
1615
|
+
})
|
|
1616
|
+
.catch(function (err) {
|
|
1617
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "Fetch failed").slice(0, ERR_CLAMP));
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
function _handleStore(state, socket, tag, args, useUid) {
|
|
1622
|
+
if (!state.selectedMailbox) {
|
|
1623
|
+
// RFC 9051 §6.4.6 — STORE outside of Selected state is a
|
|
1624
|
+
// protocol-context violation. BAD (not NO) is the correct
|
|
1625
|
+
// response per the IMAP grammar; UID STORE has the same rule
|
|
1626
|
+
// since the verb is just a `UID` prefix on STORE.
|
|
1627
|
+
_writeTagged(socket, tag, "BAD STORE only valid in Selected state (RFC 9051 §6.4.6)");
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
if (state.selectedReadOnly) {
|
|
1631
|
+
_writeTagged(socket, tag, "NO Mailbox is read-only");
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
if (typeof mailStore.storeFlags !== "function") {
|
|
1635
|
+
_writeTagged(socket, tag, "BAD STORE backend not configured");
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
// RFC 7162 §3.1.3 — STORE may carry a parenthesised UNCHANGEDSINCE
|
|
1639
|
+
// modifier between the sequence-set and the FLAGS op:
|
|
1640
|
+
// STORE 1:* (UNCHANGEDSINCE 12345) +FLAGS (\Deleted)
|
|
1641
|
+
// The backend's response shape is { rows, modified } — `modified`
|
|
1642
|
+
// is the seq-set string of message ids whose modseq advanced past
|
|
1643
|
+
// unchangedSince before this STORE ran. We surface those via
|
|
1644
|
+
// [MODIFIED <set>] OK response (RFC 7162 §3.1.3).
|
|
1645
|
+
var unchangedSince = null;
|
|
1646
|
+
var unchangedMatch = args.match(/^(\S+)\s+\(UNCHANGEDSINCE\s+(\d+)\)\s+(.+)$/i); // allow:regex-no-length-cap — args length already capped upstream
|
|
1647
|
+
if (unchangedMatch) {
|
|
1648
|
+
var usN = parseInt(unchangedMatch[2], 10);
|
|
1649
|
+
if (isFinite(usN) && usN >= 0) unchangedSince = usN;
|
|
1650
|
+
args = unchangedMatch[1] + " " + unchangedMatch[3];
|
|
1651
|
+
}
|
|
1652
|
+
var match = args.match(/^(\S+)\s+([+-]?FLAGS(?:\.SILENT)?)\s+\(([^)]*)\)$/i); // allow:regex-no-length-cap — args length already capped upstream
|
|
1653
|
+
if (!match) {
|
|
1654
|
+
_writeTagged(socket, tag, "BAD STORE expects seq-set FLAGS (...)");
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
var seqSet = match[1];
|
|
1658
|
+
var op = match[2].toUpperCase();
|
|
1659
|
+
var flagsArr = match[3].split(/\s+/).filter(Boolean);
|
|
1660
|
+
var silent = /\.SILENT$/i.test(op);
|
|
1661
|
+
var mode = op[0] === "+" ? "add" : op[0] === "-" ? "remove" : "replace";
|
|
1662
|
+
// RFC 7162 §3.1.2 — UNCHANGEDSINCE in STORE engages CONDSTORE for
|
|
1663
|
+
// the session (same implicit-enable rule as FETCH CHANGEDSINCE).
|
|
1664
|
+
if (unchangedSince !== null && !state.enabledCondStore) {
|
|
1665
|
+
state.enabledCondStore = true;
|
|
1666
|
+
}
|
|
1667
|
+
var includeModseqStore = state.enabledCondStore === true || unchangedSince !== null;
|
|
1668
|
+
Promise.resolve()
|
|
1669
|
+
.then(function () {
|
|
1670
|
+
return mailStore.storeFlags(state.actor, state.selectedMailbox, seqSet, mode, flagsArr,
|
|
1671
|
+
{ useUid: useUid === true, unchangedSince: unchangedSince, includeModseq: includeModseqStore });
|
|
1672
|
+
})
|
|
1673
|
+
.then(function (result) {
|
|
1674
|
+
// Backend may return either an array of rows (legacy shape)
|
|
1675
|
+
// OR an object `{ rows, modified }`. Normalise.
|
|
1676
|
+
var rs, modifiedSet;
|
|
1677
|
+
if (Array.isArray(result)) { rs = result; modifiedSet = null; }
|
|
1678
|
+
else if (result && typeof result === "object") {
|
|
1679
|
+
rs = result.rows || [];
|
|
1680
|
+
modifiedSet = result.modified || null;
|
|
1681
|
+
} else { rs = []; modifiedSet = null; }
|
|
1682
|
+
// RFC 7162 §3.1.3 — under CONDSTORE / UNCHANGEDSINCE, the
|
|
1683
|
+
// server MUST emit a FETCH response carrying the new MODSEQ
|
|
1684
|
+
// for every successfully-updated message EVEN UNDER .SILENT.
|
|
1685
|
+
// Without it, CONDSTORE clients cannot refresh their local
|
|
1686
|
+
// modseq state and drift out of sync. Under non-CONDSTORE
|
|
1687
|
+
// .SILENT, the legacy behaviour stays (no untagged FETCH).
|
|
1688
|
+
var emitFlags = !silent;
|
|
1689
|
+
var emitModseqOnly = silent && includeModseqStore;
|
|
1690
|
+
if (emitFlags || emitModseqOnly) {
|
|
1691
|
+
for (var i = 0; i < rs.length; i += 1) {
|
|
1692
|
+
var r = rs[i];
|
|
1693
|
+
var payload;
|
|
1694
|
+
if (emitFlags) {
|
|
1695
|
+
payload = "FLAGS (" + (r.flags || []).join(" ") + ")";
|
|
1696
|
+
if (includeModseqStore && r.modseq !== undefined) {
|
|
1697
|
+
payload = payload + " MODSEQ (" + r.modseq + ")";
|
|
1698
|
+
}
|
|
1699
|
+
} else if (r.modseq !== undefined) {
|
|
1700
|
+
// SILENT + CONDSTORE — emit MODSEQ alone (no FLAGS).
|
|
1701
|
+
payload = "MODSEQ (" + r.modseq + ")";
|
|
1702
|
+
} else {
|
|
1703
|
+
continue;
|
|
1704
|
+
}
|
|
1705
|
+
_writeUntagged(socket, r.seq + " FETCH (" + payload + ")");
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
var okTag = "OK STORE completed";
|
|
1709
|
+
// RFC 7162 §3.1.3 — MODIFIED carries the set of ids the
|
|
1710
|
+
// conditional STORE refused to update because their modseq
|
|
1711
|
+
// advanced past unchangedSince. Clients re-issue FETCH against
|
|
1712
|
+
// the set to refresh state before retry.
|
|
1713
|
+
if (modifiedSet && String(modifiedSet).length > 0) {
|
|
1714
|
+
okTag = "OK [MODIFIED " + modifiedSet + "] STORE completed";
|
|
1715
|
+
}
|
|
1716
|
+
_writeTagged(socket, tag, okTag);
|
|
1717
|
+
})
|
|
1718
|
+
.catch(function (err) {
|
|
1719
|
+
_writeTagged(socket, tag, "NO " + ((err && err.message) || "Store failed").slice(0, ERR_CLAMP));
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
function _handleUid(state, socket, tag, args) {
|
|
1724
|
+
// UID FETCH / UID STORE / UID SEARCH / UID COPY / UID MOVE per
|
|
1725
|
+
// RFC 9051 §6.4.9. The sub-command's sequence-set is interpreted
|
|
1726
|
+
// as UIDs (not message-sequence-numbers); we pass `useUid: true`
|
|
1727
|
+
// to the sub-handler which threads it through to the backend's
|
|
1728
|
+
// mailStore.fetchRange / storeFlags via opts. Without this, the
|
|
1729
|
+
// backend treats the seq-set as msg-numbers and a client's
|
|
1730
|
+
// `UID FETCH 12345 (BODY[])` returns the WRONG message.
|
|
1731
|
+
var sub = args.match(/^(\S+)\s+(.+)$/); // allow:regex-no-length-cap — args length already capped upstream
|
|
1732
|
+
if (!sub) {
|
|
1733
|
+
_writeTagged(socket, tag, "BAD UID expects a sub-command");
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
var subVerb = sub[1].toUpperCase();
|
|
1737
|
+
var subArgs = sub[2];
|
|
1738
|
+
if (subVerb === "FETCH") return _handleFetch(state, socket, tag, subArgs, true);
|
|
1739
|
+
if (subVerb === "STORE") return _handleStore(state, socket, tag, subArgs, true);
|
|
1740
|
+
// RFC 9051 §6.4.9 also defines UID SEARCH / UID COPY / UID MOVE /
|
|
1741
|
+
// UID EXPUNGE; deferred from the initial listener slice.
|
|
1742
|
+
//
|
|
1743
|
+
// SEARCH: composes with the existing _handleSearch path; needs
|
|
1744
|
+
// the searchRange path threaded through `useUid: true`.
|
|
1745
|
+
// COPY: composes with the existing _handleCopy path; needs
|
|
1746
|
+
// the mailStore.copyRange opt accepted.
|
|
1747
|
+
// MOVE: RFC 6851; same shape as COPY plus an atomic-delete
|
|
1748
|
+
// step on the source mailbox.
|
|
1749
|
+
// EXPUNGE: RFC 4315 UIDPLUS; expunges by uid-set instead of by
|
|
1750
|
+
// \Deleted-flag scan.
|
|
1751
|
+
//
|
|
1752
|
+
// Re-open condition: operator surfaces a real IMAP client that
|
|
1753
|
+
// refuses to fall back to seq-number variants (most modern
|
|
1754
|
+
// clients — mutt / Thunderbird / Apple Mail / Outlook — already
|
|
1755
|
+
// use the seq-number forms when UID variants are unavailable).
|
|
1756
|
+
//
|
|
1757
|
+
// Operator escape hatch today: clients that issue these UID
|
|
1758
|
+
// sub-commands receive `BAD` and retry against the seq-number
|
|
1759
|
+
// variant (SEARCH / COPY / MOVE / EXPUNGE) which the listener
|
|
1760
|
+
// does serve.
|
|
1761
|
+
_writeTagged(socket, tag, "BAD UID " + subVerb +
|
|
1762
|
+
" is not yet implemented; client may retry with the seq-number form");
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
function _handleIdle(state, socket, tag) {
|
|
1766
|
+
if (!_requireAuth(state, socket, tag)) return;
|
|
1767
|
+
_writeContinuation(socket, "idling");
|
|
1768
|
+
// RFC 2177 §3 — IDLE must be terminated with DONE before
|
|
1769
|
+
// bandwidth-timeout. We schedule a soft cutoff 1 min before the
|
|
1770
|
+
// hard 30-min cutoff to force client re-issue.
|
|
1771
|
+
var timer = setTimeout(function () {
|
|
1772
|
+
if (state.idle) {
|
|
1773
|
+
_writeUntagged(socket, "BYE IDLE timed out — re-issue");
|
|
1774
|
+
state.idle = null;
|
|
1775
|
+
_close(socket, state);
|
|
1776
|
+
}
|
|
1777
|
+
}, IDLE_BANDWIDTH_TIMEOUT_MS);
|
|
1778
|
+
state.idle = { tag: tag, timer: timer };
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
function _writeTagged(socket, tag, msg) {
|
|
1782
|
+
try { socket.write(tag + " " + msg + "\r\n"); }
|
|
1783
|
+
catch (_e) { /* socket may be down */ }
|
|
1784
|
+
}
|
|
1785
|
+
function _writeUntagged(socket, msg) {
|
|
1786
|
+
try { socket.write("* " + msg + "\r\n"); }
|
|
1787
|
+
catch (_e) { /* socket may be down */ }
|
|
1788
|
+
}
|
|
1789
|
+
function _writeContinuation(socket, msg) {
|
|
1790
|
+
try { socket.write("+ " + msg + "\r\n"); }
|
|
1791
|
+
catch (_e) { /* socket may be down */ }
|
|
1792
|
+
}
|
|
1793
|
+
function _close(socket, state) {
|
|
1794
|
+
// The drain loop's `if (state.stage === "closed") return;` guard
|
|
1795
|
+
// (around the bottom of _drainBuffer) was dead before this —
|
|
1796
|
+
// _close never wrote the sentinel, so the drain loop kept
|
|
1797
|
+
// processing buffered bytes after the socket was destroyed.
|
|
1798
|
+
// Setting stage="closed" here makes the guard reachable so a
|
|
1799
|
+
// close mid-loop short-circuits the next command dispatch
|
|
1800
|
+
// (defense-in-depth against an exception thrown by a handler
|
|
1801
|
+
// that doesn't tear down the loop).
|
|
1802
|
+
if (state && typeof state === "object") state.stage = "closed";
|
|
1803
|
+
try { socket.end(); } catch (_e) { /* idempotent */ }
|
|
1804
|
+
try { socket.destroy(); } catch (_e2) { /* idempotent */ }
|
|
1805
|
+
connections.delete(socket);
|
|
1806
|
+
}
|
|
1807
|
+
function _quote(s) { return '"' + String(s).replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + '"'; }
|
|
1808
|
+
function _unquote(s) {
|
|
1809
|
+
if (typeof s !== "string") return "";
|
|
1810
|
+
if (s[0] === "\"" && s[s.length - 1] === "\"") return s.slice(1, -1);
|
|
1811
|
+
return s;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// ---- Lifecycle ----------------------------------------------------------
|
|
1815
|
+
async function listen(listenOpts) {
|
|
1816
|
+
listenOpts = listenOpts || {};
|
|
1817
|
+
if (listening) {
|
|
1818
|
+
throw new MailServerImapError("mail-server-imap/already-listening",
|
|
1819
|
+
"listen: already listening");
|
|
1820
|
+
}
|
|
1821
|
+
var port = listenOpts.port === undefined ? 143 : listenOpts.port; // allow:raw-byte-literal — RFC 9051 IMAP port (IANA)
|
|
1822
|
+
var address = listenOpts.address || "0.0.0.0";
|
|
1823
|
+
tcpServer = net.createServer(function (socket) { _handleConnection(socket); });
|
|
1824
|
+
return new Promise(function (resolve, reject) {
|
|
1825
|
+
tcpServer.once("error", reject);
|
|
1826
|
+
tcpServer.listen(port, address, function () {
|
|
1827
|
+
listening = true;
|
|
1828
|
+
tcpServer.removeListener("error", reject);
|
|
1829
|
+
_emit("mail.server.imap.listening",
|
|
1830
|
+
{ port: port, address: address });
|
|
1831
|
+
resolve({ port: tcpServer.address().port, address: address });
|
|
1832
|
+
});
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
async function close() {
|
|
1837
|
+
if (!listening) return;
|
|
1838
|
+
listening = false;
|
|
1839
|
+
for (var s of connections) { try { s.destroy(); } catch (_e) { /* idempotent */ } }
|
|
1840
|
+
connections.clear();
|
|
1841
|
+
return new Promise(function (resolve) {
|
|
1842
|
+
tcpServer.close(function () {
|
|
1843
|
+
_emit("mail.server.imap.closed", {});
|
|
1844
|
+
resolve();
|
|
1845
|
+
});
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
return {
|
|
1850
|
+
listen: listen,
|
|
1851
|
+
close: close,
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
module.exports = {
|
|
1856
|
+
create: create,
|
|
1857
|
+
MailServerImapError: MailServerImapError,
|
|
1858
|
+
};
|