Skip to content

Webhook tenant_id scoping not enforced during dispatch #58

@Dhruwang

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions