Summary
deleteWebhookAction and updateWebhookSecretAction in src/server/webhooks/webhooks-actions.ts accept a raw teamId from client input without verifying that the authenticated user belongs to that team.
The sibling action upsertWebhookAction correctly uses .use(withTeamIdResolution) middleware for this check — the other two do not.
Expected behavior
All three webhook actions should verify team membership before making infra API calls, consistent with how upsertWebhookAction already works.
Actual behavior
deleteWebhookAction (line 99) and updateWebhookSecretAction (line 148) skip the withTeamIdResolution middleware. The teamId from user input is passed directly to the infra API via SUPABASE_AUTH_HEADERS without calling checkUserTeamAuthCached.
upsertWebhookAction — correct:
export const upsertWebhookAction = authActionClient
.schema(UpsertWebhookSchema) // uses teamIdOrSlug
.metadata({ actionName: 'upsertWebhook' })
.use(withTeamIdResolution) // validates team membership
.action(...)
deleteWebhookAction — missing auth:
export const deleteWebhookAction = authActionClient
.schema(DeleteWebhookSchema) // uses raw teamId (z.uuid)
.metadata({ actionName: 'deleteWebhook' })
// no .use(withTeamIdResolution)
.action(...)
The schemas also differ — UpsertWebhookSchema uses teamIdOrSlug: TeamIdOrSlugSchema while DeleteWebhookSchema and UpdateWebhookSecretSchema use teamId: z.uuid(), bypassing the resolution + auth middleware entirely.
Potential impact
An authenticated user could supply another team's ID when calling these actions, potentially deleting webhooks or overwriting signing secrets belonging to a different team.
Suggested fix
- Update
DeleteWebhookSchema and UpdateWebhookSecretSchema to use teamIdOrSlug: TeamIdOrSlugSchema instead of teamId: z.uuid()
- Add
.use(withTeamIdResolution) to both actions
- Read
teamId from ctx (middleware-validated) instead of parsedInput
Summary
deleteWebhookActionandupdateWebhookSecretActioninsrc/server/webhooks/webhooks-actions.tsaccept a rawteamIdfrom client input without verifying that the authenticated user belongs to that team.The sibling action
upsertWebhookActioncorrectly uses.use(withTeamIdResolution)middleware for this check — the other two do not.Expected behavior
All three webhook actions should verify team membership before making infra API calls, consistent with how
upsertWebhookActionalready works.Actual behavior
deleteWebhookAction(line 99) andupdateWebhookSecretAction(line 148) skip thewithTeamIdResolutionmiddleware. TheteamIdfrom user input is passed directly to the infra API viaSUPABASE_AUTH_HEADERSwithout callingcheckUserTeamAuthCached.upsertWebhookAction— correct:deleteWebhookAction— missing auth:The schemas also differ —
UpsertWebhookSchemausesteamIdOrSlug: TeamIdOrSlugSchemawhileDeleteWebhookSchemaandUpdateWebhookSecretSchemauseteamId: z.uuid(), bypassing the resolution + auth middleware entirely.Potential impact
An authenticated user could supply another team's ID when calling these actions, potentially deleting webhooks or overwriting signing secrets belonging to a different team.
Suggested fix
DeleteWebhookSchemaandUpdateWebhookSecretSchemato useteamIdOrSlug: TeamIdOrSlugSchemainstead ofteamId: z.uuid().use(withTeamIdResolution)to both actionsteamIdfromctx(middleware-validated) instead ofparsedInput