fix(webhooks): drop the AgentMail-specific webhook body cap - #6436
Conversation
The receiver had its own 4 MB cap on the grounds that it is the only one that parses an unverified body before authenticating. It is not — the generic path receiver parses at the shared cap well before verifying auth, so the tighter limit was inconsistent rather than principled, and it could reject a large-but-legitimate email outright. The one-signature-check-per -request property comes from the tenant lookup and its unique index, not from the cap. Also records why the routing id is bounded at 320 characters.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryLow Risk Overview Deletes Clarifies Reviewed by Cursor Bugbot for commit 7f2aba0. Configure here. |
Greptile SummaryThe PR removes AgentMail’s provider-specific 4 MiB webhook limit and applies the shared webhook body cap instead.
Confidence Score: 5/5The PR appears safe to merge because the AgentMail receiver consistently adopts the existing bounded shared webhook limit and no concrete regression remains. The changed route preserves both Content-Length and streaming enforcement, continues to reject bodies above the configured shared cap before database or signature work, and removes an export with no remaining consumers.
|
| Filename | Overview |
|---|---|
| apps/sim/app/api/webhooks/agentmail/route.ts | Replaces the AgentMail-specific body cap with the shared webhook limit consistently across header and streaming checks. |
| apps/sim/app/api/webhooks/agentmail/route.test.ts | Updates the oversized-body test to exercise the shared cap and confirms rejection occurs before database lookup or signature verification. |
| apps/sim/lib/api/contracts/webhooks.ts | Documents the routing identifier’s RFC-derived bound and adds a clearer maximum-length validation error. |
| apps/sim/lib/webhooks/constants.ts | Removes the now-unused AgentMail-specific body-limit export without leaving repository consumers. |
Sequence Diagram
sequenceDiagram
participant Sender as AgentMail
participant Route as AgentMail webhook route
participant Limit as Shared body-limit helpers
participant DB as Workspace lookup
participant Svix as Signature verifier
Sender->>Route: POST webhook
Route->>Limit: Enforce WEBHOOK_MAX_BODY_BYTES
Limit-->>Route: Bounded raw body
Route->>Route: Parse routing inbox_id
Route->>DB: Find workspace by inbox provider id
DB-->>Route: Workspace secret
Route->>Svix: Verify one signature against raw body
Svix-->>Route: Verified payload or rejection
Reviews (1): Last reviewed commit: "fix(webhooks): drop the AgentMail-specif..." | Re-trigger Greptile
Follow-up audit of #6431. The receiver carried its own 4 MB body cap, justified on the grounds that it is the only public receiver that parses an unverified body before authenticating.
That justification was wrong.
app/api/webhooks/trigger/[path]/route.tscallsparseWebhookBody(whichJSON.parses up to the shared cap) well beforeverifyProviderAuth, on an equally public path. The AgentMail receiver has the same exposure profile as its sibling, so a separate, tighter cap was an inconsistent limit rather than a principled one — and it could reject a large-but-legitimate email outright, which the shared cap would have accepted.Removing it costs nothing security-wise: the one-signature-check-per-request property comes from the tenant lookup and its unique index, not from the body cap. This also removes the only backwards-incompatible behavior change in #6431 and avoids setting a per-provider-constant precedent in a shared module.
Also documents why the routing id is bounded at 320 characters (RFC 5321 max address length, since an AgentMail inbox id is an email address) so it doesn't read as arbitrary.
Audit findings that needed no change
workspace.inbox_provider_id(enableInboxsets a freshly minted id,disableInboxnulls it), so the unique index cannot be violated by any existing flow.inboxProviderIdmismatch check; the new code fails the lookup. Both return 401 and both rely on provider retry.EXPLAINagainst an existingWHERE col IS NOT NULLpartial index on the same server, which plans an index scan without the query restating the predicate.Testing
Existing suites pass (620 across webhooks/mothership). The body-cap test now asserts the route rejects an oversized body before reaching either the database or the hash. Type-check, lint,
check:api-validation, andcheck:migrationspass.