Skip to content

Security: Cross-Space IDOR in Webhook and Invitation Delete/Update (Missing space_id filter)Β #2236

@lighthousekeeper1212

Description

@lighthousekeeper1212

Summary

The webhook and invitation repositories have findOneById(), deleteOneById(), and updateOneById() methods that query by resource ID without space_id filtering, allowing authenticated users to delete/update webhooks and invitations belonging to other spaces.

Details

Webhook IDOR

File: packages/persistence/src/webhook/webhook.repository.ts

findOneById() (line 22-31), deleteOneById() (line 70-72), and updateOneById() (line 58-68) all query by id only without space scoping:

// VULNERABLE - line 22
async findOneById(id: string): Promise<Option<WebhookDo>> {
  const wb = await this.txContext
    .getCurrentTransaction()
    .selectFrom("undb_webhook")
    .selectAll()
    .where((eb) => eb.eb("id", "=", id))  // NO space_id filter
    .executeTakeFirst()
}

Compare with the secure find() method (line 37-50):

// SECURE - uses space scoping
.where((eb) => {
  const visitor = new WebhookFilterVisitor(eb, this.context.mustGetCurrentSpaceId())
  spec.accept(visitor)
  return visitor.cond
})

Invitation IDOR

File: packages/persistence/src/member/invitation.repository.ts

deleteOneById() (line 19-23) and updateOneById() (line 25-37) query by id only. Compare with insert() and upsert() which correctly use this.context.mustGetCurrentSpaceId().

Command Handlers

  • delete-webhook.command-handler.ts: Directly calls this.repository.deleteOneById(command.input.id) without space check
  • delete-invitation.command-handler.ts: Directly calls this.repo.deleteOneById(command.id) without space check

Impact

An authenticated user in Space A can delete/update webhooks and invitations belonging to Space B.

Suggested Fix

Add space_id to the WHERE clause in findOneById(), deleteOneById(), and updateOneById() for both repositories, using this.context.mustGetCurrentSpaceId() consistent with how the find() method already scopes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions