Skip to content

Webhook tenant_id scoping not enforced during dispatch #58

Description

@Dhruwang

Description

When creating a webhook with a tenant_id filter (e.g., org-123), the webhook is expected to only fire for events belonging to that tenant. However, the webhook fires for all tenants, ignoring the tenant_id scope entirely.

This is a data leak across tenants — Tenant A receives webhook notifications containing Tenant B's feedback data.

Steps to reproduce

  1. Start Hub locally with docker compose up -d

  2. Create a webhook scoped to org-123:

curl -X POST http://localhost:8080/v1/webhooks \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://smee.io/YOUR_CHANNEL",
    "event_types": ["feedback_record.created"],
    "enabled": true,
    "tenant_id": "org-123"
  }'
  1. Create a feedback record in a different tenant (org-OTHER):
curl -X POST http://localhost:8080/v1/feedback-records \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source_type": "survey",
    "field_id": "scope-test",
    "field_type": "text",
    "value_text": "This belongs to org-OTHER, not org-123",
    "tenant_id": "org-OTHER",
    "submission_id": "sub-scope-test"
  }'
  1. Check the River job queue:
SELECT id, args->>'webhook_id' as webhook_id,
       args->>'event_type' as event_type,
       args->'data'->>'tenant_id' as record_tenant
FROM river_job ORDER BY id DESC LIMIT 5;

Expected behavior

The org-123 webhook should not fire for a record with tenant_id: "org-OTHER". The dispatch logic should filter webhooks by matching tenant_id before enqueuing jobs.

Actual behavior

The org-123 webhook fires for the org-OTHER record. The River job queue shows the webhook dispatched with the org-123 webhook ID but containing org-OTHER data:

 id | webhook_id                           | event_type              | record_tenant
----+--------------------------------------+-------------------------+--------------
 37 | 019dcdea-3991-73cb-a837-0d7fc3a789f1 | feedback_record.created | org-OTHER

This was also verified visually via smee.io — the payload arrived at the org-123 webhook endpoint containing org-OTHER's data.

Impact

  • Security: Cross-tenant data leak. A tenant's webhook endpoint receives another tenant's feedback data.
  • Privacy: Violates tenant isolation guarantees in a multi-tenant system.

Context

Found during end-to-end QA testing of the Hub quickstart flow.

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