Uh oh!
There was an error while loading. Please reload this page.
feat(mailer): gate outbound email on AppConfig access-control ban list - #5018
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryMedium Risk Overview Signup email validation is simplified: the custom Reviewed by Cursor Bugbot for commit c9a340c. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f331a0b. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
TheodoreSpeaks
commented
Jun 13, 2026
@greptile review |
Greptile SummaryThis PR gates all outbound email through the AppConfig access-control ban list by introducing an
Confidence Score: 5/5Safe to merge. The ban-list gate is correctly threaded through both the single-send and batch paths, AppConfig failures truly fail open, and the new tests cover the key scenarios. The core change is straightforward: a synchronous filter on a cached config is inserted before the unsubscribe check and send. The AppConfig layer already handles errors by returning the last-known value or falling back to env vars, so the fails-open guarantee holds. The two noted issues are both message-accuracy gaps that do not affect delivery correctness or security. No files require special attention beyond the two minor observability gaps in mailer.ts. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["sendEmail(options)"] --> B["applyBanList(options)"]
B --> C["getAccessControlConfig()"]
C --> D{"AppConfig enabled?"}
D -- No --> E["fromEnv() fallback"]
D -- Yes --> F["fetchAppConfigProfile (cached ~30s TTL)"]
F --> G{"Fetch succeeded?"}
G -- No --> H["return last known value ?? fromEnv()"]
G -- Yes --> I["parseConfig(json)"]
E --> J["AccessControlConfig"]
H --> J
I --> J
J --> K["filter via isEmailBlockedByAccessControl"]
K --> L{"all banned?"}
L -- Yes --> M["return null -> SKIPPED_BANNED_RESULT"]
L -- "partial (no audit log)" --> N["return filtered options"]
L -- None banned --> O["return original options"]
N --> P["shouldSkipForUnsubscribe"]
O --> P
P -- unsubscribed --> Q["SKIPPED_UNSUBSCRIBED_RESULT"]
P -- allowed --> R["processEmailData -> dispatchWithFallback"]
Reviews (2): Last reviewed commit: "ci(migrations): restore dev schema-push ..." | Re-trigger Greptile |
| }), | ||
| ] | ||
| : []), | ||
| ...(isSignupEmailValidationEnabled ? [emailHarmony()] : []), |
There was a problem hiding this comment.
Silent removal of
disposable-email-domains signup check
emailHarmony() with no validator option uses only better-auth-harmony's built-in Mailchecker list. The previous custom validator explicitly called validateEmailWithMailchecker(email) && !(await isDisposableEmailDomain(email)), layering the disposable-email-domains dataset (~120K exact domains) on top of Mailchecker. Dropping the custom validator quietly narrows signup validation to Mailchecker alone — any disposable provider covered by disposable-email-domains but not by Mailchecker can now register. This change isn't mentioned in the PR description; if it's intentional (e.g., the AppConfig ban list now handles this), it should be documented.
Uh oh!
There was an error while loading. Please reload this page.
TheodoreSpeaks
commented
Jun 13, 2026
@greptile review |
Uh oh!
There was an error while loading. Please reload this page.

Summary
sendEmail/sendBatchEmailsso block-lifecycle emails (payment-failure, dispute, credit-purchase, usage-threshold) — and all other outbound mail — skip recipients on the AppConfig access-control ban listapplyBanListhelper filters recipients viaisEmailBlockedByAccessControl(same predicate used at signup/login/execution); drops only banned addresses from multi-recipient sends, skips entirely when all are bannedType of Change
Testing
Tested manually.
bun run lintclean,bun run check:api-validation:strictpassed. Added mailer tests (single banned recipient, partial-array filtering, batch skip) — 18/18 pass; billing webhook tests 3/3 pass.Checklist