feat(auth): enforce domain and account bans on sign-in and workflow executions#4948
Conversation
|
@greptile review |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryHigh Risk Overview Access control adds
Execution adds a preprocessing ban gate (step 3.5) that checks billing actor, caller Unit tests cover access-control, ban helpers, and preprocessing; Reviewed by Cursor Bugbot for commit f460845. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryThis PR introduces a comprehensive ban-enforcement layer that blocks sign-in and workflow executions for email-address and domain-blocked accounts, in addition to the existing DB-level better-auth ban mechanism. A new
Confidence Score: 5/5Safe to merge. The ban gates are fail-closed across all execution paths, the session hook correctly propagates errors, and the inbox executor suppresses error emails on every banned-user exit path including infrastructure failures. The changes are additive security guards with no modifications to existing happy-path logic. Each new gate consistently fails closed on lookup errors, is covered by dedicated unit tests including failure propagation and edge cases, and matches the pattern used by the surrounding code. No execution path was found that could bypass the ban check once a block is in effect. No files require special attention. All changed files implement the new ban policy consistently. Important Files Changed
Sequence DiagramsequenceDiagram
participant C as Client
participant A as auth.ts
participant S as session.create.before
participant P as preprocessExecution
participant B as ban.ts
participant DB as Database
Note over C,DB: Sign-in (email/password)
C->>A: POST /sign-in/email
A->>A: isEmailBlockedByAccessControl(requestEmail)
alt email blocked
A-->>C: 403 FORBIDDEN
else
A->>S: session.create.before
S->>DB: "SELECT email WHERE id=session.userId"
S->>S: isEmailBlockedByAccessControl
alt blocked
S-->>C: 403 FORBIDDEN
else
S-->>C: Session created
end
end
Note over C,DB: Workflow execution
C->>P: preprocessExecution
P->>B: getActivelyBannedUserIds([actor, userId, owner])
B->>DB: SELECT ban fields WHERE id IN (...)
alt any banned
P-->>C: 403 Account suspended
else DB error
P-->>C: 500 fail-closed
else clean
P-->>C: success
end
Note over C,DB: Inbox execution
C->>P: executeInboxTask
P->>B: isEmailBlocked(senderEmail)
B->>DB: "SELECT ban WHERE lower(email)=sender"
P->>B: getActivelyBannedUserIds([userId, billedId])
alt blocked
P-->>C: markTaskFailed (no email)
else DB error
P-->>C: markTaskFailed fail-closed
else clean
P->>P: run orchestrator
end
Reviews (5): Last reviewed commit: "fix(mothership): ban-check the workspace..." | Re-trigger Greptile |
Greptile SummaryThis PR enforces domain bans and account bans across all sign-in paths and workflow execution entry points. A new
Confidence Score: 3/5The auth and preprocessing layers are solid, but the inbox executor has a defect where a DB failure during the ban check causes the outer catch to send an error email to a sender who may be banned — directly contradicting the stated design intent. The core ban logic in ban.ts, the preprocessing gate, and the auth-layer hooks are well-designed and correctly fail-closed. The issue is in executor.ts: the ban check lives inside the outer try block, so any DB error during that check propagates to the catch handler which then calls sendInboxResponse if no response has been sent yet. The code comment explicitly calls out that no email should ever be sent to a suspended account, but a transient DB outage breaks that guarantee silently. apps/sim/lib/mothership/inbox/executor.ts needs the most attention — the ban-check placement inside the try block allows the catch handler to email potentially-banned senders on DB failure. Important Files Changed
Sequence DiagramsequenceDiagram
participant C as Client / Email Sender
participant Auth as Auth Layer (auth.ts)
participant Pre as preprocessExecution
participant Inbox as InboxExecutor
participant Ban as getActivelyBannedUserIds
participant DB as Database
Note over Auth: Email/password sign-in
C->>Auth: POST /sign-in/email
Auth->>Auth: isEmailInDenylist(requestEmail)
alt domain blocked
Auth-->>C: 403 FORBIDDEN
end
Note over Auth: OAuth/SSO sign-in
C->>Auth: OAuth callback
Auth->>Auth: session.create.before hook
Auth->>DB: "SELECT email WHERE userId = session.userId"
Auth->>Auth: isEmailInDenylist(email, blockedDomains)
alt domain blocked
Auth-->>C: 403 FORBIDDEN
end
Note over Pre: Any workflow execution
C->>Pre: preprocessExecution(options)
Pre->>Pre: Step 3 resolve actorUserId
Pre->>Ban: getActivelyBannedUserIds([actorUserId, userId])
Ban->>DB: SELECT id,email,banned,banExpires
alt banned
Pre-->>C: success false statusCode 403
else DB error
Pre-->>C: success false statusCode 500
end
Note over Inbox: Inbox task execution
C->>Inbox: executeInboxTask(taskId)
Inbox->>Inbox: claim task + resolveUserId
Inbox->>Ban: getActivelyBannedUserIds([userId])
alt banned
Inbox->>DB: markTaskFailed no email sent
else DB error catch block
Inbox->>C: error email sent unintended
end
Reviews (2): Last reviewed commit: "feat(auth): enforce domain and account b..." | Re-trigger Greptile |
|
@greptile review |
|
@greptile review |
…count # Conflicts: # apps/sim/hooks/use-inline-rename.ts
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 5e5c03e. Configure here.
|
@greptile review |

Summary
blockedSignupDomainslist now block existing accounts too, not just signups: sign-in is rejected across all providers (email/password, OAuth, SSO via asession.create.beforegate) and every workflow execution is blockedblockedEmailslist in the same appconfig profile (env fallbackBLOCKED_EMAILS) bans specific email addresses with identical enforcement: signup, sign-in, executions, inboxlib/auth/ban.tshelper resolves effective ban status (active DB ban honoringbanExpires, or blocked email/domain) in one querypreprocessExecutiongains a Step 3.5 ban gate covering all execution entry points (manual, API, webhooks, schedules, deployed chats, resume/HITL, table columns, async jobs); checks the billing actor, the workflow owner, and the caller-provided user, fails closed on lookup errorsisEmailInDenylisttoaccess-control.tsso the ban helper doesn't pull the better-auth init into background workersuse-inline-rename.tsthat was failinglint:checkNote: every domain already in the prod blocked-domains list starts hard-blocking its existing users on deploy. Already-signed-in users keep UI access for up to ~24h (session cookie cache) but executions/API keys/billing are cut immediately.
Type of Change
Testing
New unit tests for the ban helpers, access-control parsing, and the preprocessing gate (403, fail-closed 500, candidate-id dedup); 175 tests passing across touched dirs.
bun run lint:check,tsc --noEmit, andcheck:api-validation:strictall green.Checklist