Add disposable/high-risk email signup scrutiny via check-mail.org

Classifies each signup's email domain (domain-only, GDPR-friendly) alongside
the existing IP scrutiny signal, with matching held-welcome and auto
email_domain_block behavior. A report against an account with a flagged
domain suspends immediately (no reporter-count threshold) and blocks the
domain, classified live from the report payload rather than any signup-time
record so it also covers pre-existing accounts. Ships CHECK_MAIL_DRY_RUN=true
by default, independent of ABUSE_DRY_RUN, so the new report-triggered suspend
path stays inert until watched.
This commit is contained in:
waffle2k
2026-07-03 09:08:30 -07:00
parent 6ad9a93f38
commit 656eec09c0
5 changed files with 452 additions and 40 deletions
+46 -1
View File
@@ -9,7 +9,9 @@ A small FastAPI webhook server that receives Mastodon **admin webhooks** for
- **`report.created`** — evaluates the reported account and, when enough
**distinct** reporters have open reports against a *young* or *dormant*
account, auto-**silences** it (reversible) and DMs a moderator for review
(the abuse bot).
(the abuse bot). Special case: a report against an account whose email
domain is flagged high-risk (see below) auto-**suspends** it immediately
(no reporter-count threshold) and blocks the domain.
- **Runs on:** `admin.yttrx.com` (Docker container, nginx-proxied at
`https://hooks.yttrx.com`)
@@ -85,6 +87,43 @@ regexes for a while. Registering ip_blocks requires the `ABUSE_BOT_TOKEN` to
carry the `admin:write:ip_blocks` scope in addition to its existing scopes
(see `CLAUDE.md`).
## Disposable/high-risk email signup scrutiny
Every `account.created` delivery already carries the signup email for free
(`Admin::Account.email`). On each new local signup, the bot:
1. **Classifies the domain only** (never the full email address — GDPR-
friendly) via [check-mail.org](https://check-mail.org/), cached in sqlite.
Flagged if the API marks it `is_disposable` or its `risk` score (0-100) is
at or above `CHECK_MAIL_RISK_THRESHOLD` (default 80).
2. If flagged, same scrutiny shape as the IP signal:
- **Moderator DM** with the domain and risk score.
- **Held welcome** (`CHECK_MAIL_HOLD_WELCOME`) — same semantics as
`IP_SCRUTINY_HOLD_WELCOME`.
- **Auto-registered email domain block** (`CHECK_MAIL_AUTO_DOMAIN_BLOCK`)
— the domain is added to Mastodon's native `Admin::EmailDomainBlock`,
blocking future signups from it.
A **report** against an account whose email domain is flagged is a special,
stronger case (not merely a lowered threshold like the IP signal): it
auto-**suspends** the account on the very first report — no distinct-reporter
threshold — and (re-)registers the email domain block. This is classified
**live** from the report payload's `target_account.email` (a full
`Admin::Account`, always present), not from any record made at signup time —
so it also catches accounts that signed up before this feature existed or
while `CHECK_MAIL_ENABLED`/`CHECK_MAIL_API_KEY` was off. Gated by
`ABUSE_DRY_RUN` **or** `CHECK_MAIL_DRY_RUN` (either one holds it) —
deliberately independent of `ABUSE_DRY_RUN` alone, so this brand-new action
ships inert by `CHECK_MAIL_DRY_RUN`'s own default even once the general
abuse-bot is live.
`CHECK_MAIL_DRY_RUN=true` (the shipped default) classifies and DMs a
moderator without holding any welcome or writing any email_domain_block —
keep it on until you've watched the false-positive rate for a while.
Registering email_domain_blocks requires the `ABUSE_BOT_TOKEN` to carry the
`admin:write:email_domain_blocks` scope in addition to its existing scopes
(see `CLAUDE.md`).
## Layout
| Path | Purpose |
@@ -127,6 +166,12 @@ Copy `.env.example` to `.env` and fill in:
| `IP_SCRUTINY_AUTO_IPBLOCK` | Auto-register a flagged IP into Mastodon's `Admin::IpBlock` |
| `IP_SCRUTINY_IPBLOCK_SEVERITY` | `sign_up_requires_approval` (default), `sign_up_block`, or `no_access` |
| `IP_SCRUTINY_HOSTING_RE` / `IP_SCRUTINY_MOBILE_RE` | Keyword regexes matched against the RDAP org/ASN description |
| `CHECK_MAIL_ENABLED` | Master switch for disposable/high-risk email signup scrutiny |
| `CHECK_MAIL_API_KEY` | check-mail.org API key; blank disables the check |
| `CHECK_MAIL_DRY_RUN` | `true` — classify + DM only, no held welcome, no email_domain_block write, no report-triggered suspend |
| `CHECK_MAIL_RISK_THRESHOLD` | Flag if `is_disposable` or `risk` ≥ this (default 80) |
| `CHECK_MAIL_HOLD_WELCOME` | `true` — hold the welcome for a flagged signup until `account.approved` |
| `CHECK_MAIL_AUTO_DOMAIN_BLOCK` | Auto-register a flagged domain into Mastodon's `Admin::EmailDomainBlock`; also gates the report-triggered override |
## Local test