fix: enforce tenant scope on webhooks#65
Conversation
Carry tenant_id through webhook dispatch jobs and re-check scope in the worker so tenant-scoped webhooks cannot receive another tenant's payload. Allow worker and embedding backfill commands to use an explicitly configured local DATABASE_URL while still rejecting implicit defaults. Refs ENG-796
Carry tenant_id through webhook dispatch jobs and re-check scope in the worker so tenant-scoped webhooks cannot receive another tenant's payload. Allow worker and embedding backfill commands to use an explicitly configured local DATABASE_URL while still rejecting implicit defaults. Refs ENG-796
WalkthroughThis pull request introduces tenant-scoping enforcement throughout the webhook dispatch system. Documentation clarifies multi-tenant constraints for reads, queues, caching, and other operations. Database URL configuration validation is refactored with a new 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@AGENTS.md`:
- Around line 28-29: Add a blank line immediately after the markdown heading "##
Multi-Tenancy & Data Isolation" so the heading is separated from the following
list item (to satisfy markdownlint MD022); update the AGENTS.md content by
inserting one empty line between that heading and the subsequent line starting
with "- Treat `tenant_id`..." so the heading and list are properly separated.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 8bfac291-6031-40e0-b3ec-80c1fd2fd5f8
📒 Files selected for processing (18)
AGENTS.mdcmd/backfill-embeddings/main.gocmd/worker/main.gointernal/config/config.gointernal/config/config_test.gointernal/observability/names.gointernal/repository/webhooks_repository.gointernal/repository/webhooks_repository_test.gointernal/service/webhook_dispatch_args.gointernal/service/webhook_provider.gointernal/service/webhook_provider_test.gointernal/service/webhook_sender_test.gointernal/service/webhook_tenant.gointernal/service/webhook_tenant_test.gointernal/service/webhooks_service.gointernal/service/webhooks_service_test.gointernal/workers/webhook_dispatch.gointernal/workers/webhook_dispatch_test.go
✱ Stainless preview buildsThis PR will update the
|
Summary
Fixes ENG-796 by enforcing tenant isolation for webhook dispatch end to end.
tenant_idfor webhook creation and prevent clearing it on update.tenant_idconflicts with payloadtenant_id.dataas an ID array while using tenant-aware internal delete event data.tenant_idfor feedback bulk delete so delete events cannot fan out across tenants.AGENTS.mdwith access-path parity rules to prevent future tenant leaks through alternate paths.Root Cause
Webhook dispatch treated
tenant_idlike an optional filter/global fallback. Feedback records became tenant-required, but the webhook fan-out path could still select enabled webhooks without enforcing the same tenant boundary, so async dispatch was not equivalent to the primary tenant-scoped data paths.User Impact
Tenant-scoped webhooks now only receive events for their exact tenant. Missing or conflicting tenant context skips dispatch and logs the problem instead of sending cross-tenant data.
Migration
Adds
008_require_webhook_tenant_id.sql:tenant_idNo new index is required for this change; existing webhook tenant/event/enabled indexes already cover the dispatch query shape.
Testing
go test ./...✅golangci-lint run ./...✅make migrate-validate✅make lint-openapi✅133/164lines,81.1%✅Pre-commit also ran migration validation, fmt, lint, and unit tests successfully, then failed on a missing local Make target
check-coverage-staged; the commit was created with--no-verifyafter the equivalent checks and coverage measurement passed manually.