You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The public sign-up form has no bot protection (no CAPTCHA, no meaningful signup rate limit), and prod data shows it is being used to send confirmation email to people who never requested an account — the signature of subscription / email bombing, where an open signup form is used as a free relay against third parties.
Our domain and our Resend account are the sender, so the exposure is ours: deliverability damage, spam complaints, and possible sender-account suspension.
Note: deliberately no email addresses in this issue — the affected parties are non-consenting third parties and this repo is public. Details are aggregated. The raw list is available to the maintainer via the Supabase admin API.
Evidence (prod, as of 2026-07-25)
17 non-owner, non-test accounts, all created in a 7-day window: 2026-07-19 → 2026-07-25. That window starts ~2 days after Resend SMTP went live (#290) and sign-up actually began working (#288) — i.e. the abuse began as soon as the path became functional.
Signal
Count
Created in the 7-day window
17
Never confirmed AND never signed in
13
Confirmed + signed in (possibly genuine)
4 (one via Google OAuth)
For those 13, our server sent a confirmation email to a real mailbox whose owner never asked for it.
Address composition (redacted to category):
A US county-government domain, a Fortune-500 energy company, a law firm, an international shipping/chartering firm, a design agency, a German business domain — unrelated industries and geographies, which is not an organic signup pattern.
One carrier email-to-SMS gateway address. Signing that up sends a text message to a phone number — a strong bombing indicator, since the payload is a text, not an email.
Six dotted-Gmail aliases (f.i.r.s.t.l.a.s.t@gmail.com-style dot insertion) — classic alias multiplication / obfuscation.
Root cause
scripts/supabase/auth-config.json manages only six keys:
There is no CAPTCHA setting and no signup rate limit. Note this also means the auth-config drift gate shipped in #350 will not catch it — the gate only compares keys that are present in the tracked config, so an unset protection is invisible to it.
Proposed fix
Enable CAPTCHA on Supabase Auth (hCaptcha or Cloudflare Turnstile) — the actual mitigation. Wire the provider token into the sign-up form.
Track the CAPTCHA + signup-rate-limit keys in auth-config.json so the ci(#288): wire the auth-config drift gate into CI — closes #288 #350 drift gate guards them from silently regressing. (Generalises: the gate protects only what it knows about — worth auditing which other security-relevant keys are untracked.)
The original read ("bot-generated, never consented, delete them") was overconfident. It rested entirely on how the addresses look — dotted-Gmail aliases, unrelated corporate domains, one carrier email-to-SMS gateway. The evidence that would actually settle it does not exist: auth.audit_log_entries has 0 rows, so there are no IPs, user-agents or timings to distinguish a script from a person.
A plausible alternative fits the data at least as well: Resend SMTP went live 2026-07-17 and the first of these signups is 07-19. A brand-new sending domain has no reputation, so its mail lands in spam — which predicts exactly this pattern (people sign up, never see the confirmation, never confirm) with no bots required. A 76% non-confirmation rate is unremarkable for a new sender.
Some remain genuinely odd (the SMS-gateway address especially), so it is likely a mix — and there is no way to sort them. The asymmetry decides it: keeping 13 inert rows costs nothing, while deleting one genuinely interested person is silent and unrecoverable for them. Keep them.
#288 closed on "the suite is green while the deployed product is unusable." This is the mirror image: sign-up now works, and the first thing that found it was a bot. An enterprise-ready template needs the sign-up path to be both functional and abuse-resistant — shipping a template whose default signup is an open mail relay would propagate the flaw to every fork.
Summary
The public sign-up form has no bot protection (no CAPTCHA, no meaningful signup rate limit), and prod data shows it is being used to send confirmation email to people who never requested an account — the signature of subscription / email bombing, where an open signup form is used as a free relay against third parties.
Our domain and our Resend account are the sender, so the exposure is ours: deliverability damage, spam complaints, and possible sender-account suspension.
Evidence (prod, as of 2026-07-25)
17 non-owner, non-test accounts, all created in a 7-day window: 2026-07-19 → 2026-07-25. That window starts ~2 days after Resend SMTP went live (#290) and sign-up actually began working (#288) — i.e. the abuse began as soon as the path became functional.
For those 13, our server sent a confirmation email to a real mailbox whose owner never asked for it.
Address composition (redacted to category):
f.i.r.s.t.l.a.s.t@gmail.com-style dot insertion) — classic alias multiplication / obfuscation.Root cause
scripts/supabase/auth-config.jsonmanages only six keys:There is no CAPTCHA setting and no signup rate limit. Note this also means the auth-config drift gate shipped in #350 will not catch it — the gate only compares keys that are present in the tracked config, so an unset protection is invisible to it.
Proposed fix
Enable CAPTCHA on Supabase Auth (hCaptcha or Cloudflare Turnstile) — the actual mitigation. Wire the provider token into the sign-up form.
Track the CAPTCHA + signup-rate-limit keys in
auth-config.jsonso the ci(#288): wire the auth-config drift gate into CI — closes #288 #350 drift gate guards them from silently regressing. (Generalises: the gate protects only what it knows about — worth auditing which other security-relevant keys are untracked.)Decide on the 13 unconfirmed accounts.WITHDRAWN 2026-07-25 — do not delete. Investigate deliverability first: Sign-up confirmation email deliverability is unverified — DMARC has no reporting, and dead Mailgun SPF/MX remain after the Resend cutover #356.The original read ("bot-generated, never consented, delete them") was overconfident. It rested entirely on how the addresses look — dotted-Gmail aliases, unrelated corporate domains, one carrier email-to-SMS gateway. The evidence that would actually settle it does not exist:
auth.audit_log_entrieshas 0 rows, so there are no IPs, user-agents or timings to distinguish a script from a person.A plausible alternative fits the data at least as well: Resend SMTP went live 2026-07-17 and the first of these signups is 07-19. A brand-new sending domain has no reputation, so its mail lands in spam — which predicts exactly this pattern (people sign up, never see the confirmation, never confirm) with no bots required. A 76% non-confirmation rate is unremarkable for a new sender.
Some remain genuinely odd (the SMS-gateway address especially), so it is likely a mix — and there is no way to sort them. The asymmetry decides it: keeping 13 inert rows costs nothing, while deleting one genuinely interested person is silent and unrecoverable for them. Keep them.
Sign-up confirmation email deliverability is unverified — DMARC has no reporting, and dead Mailgun SPF/MX remain after the Resend cutover #356 tracks the real question — are confirmation emails actually being delivered? If they are not, that is a bug hurting real users and it outranks this.
Add an E2E/smoke assertion that sign-up rejects a request with no/invalid CAPTCHA token, so the protection can't regress unnoticed — the
@smokesuite from feat(#288): post-deploy @smoke suite + fix(#348) the CSS-as-script bug it caught #349 already hits live prod and is the natural home.Why this belongs to the #280 arc
#288 closed on "the suite is green while the deployed product is unusable." This is the mirror image: sign-up now works, and the first thing that found it was a bot. An enterprise-ready template needs the sign-up path to be both functional and abuse-resistant — shipping a template whose default signup is an open mail relay would propagate the flaw to every fork.
Related: #290 (Resend SMTP live), #288 (real-signup integrity), #350 (auth-config drift gate), #349 (
@smokesuite).