Skip to content

[Bug]: Missing team authorization on deleteWebhookAction and updateWebhookSecretAction #250

@taheerahmed

Description

@taheerahmed

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

  1. Update DeleteWebhookSchema and UpdateWebhookSecretSchema to use teamIdOrSlug: TeamIdOrSlugSchema instead of teamId: z.uuid()
  2. Add .use(withTeamIdResolution) to both actions
  3. Read teamId from ctx (middleware-validated) instead of parsedInput

Metadata

Metadata

Assignees

No one assigned

    Labels

    APIbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions