Repository navigation
fix(api): reject another org's segment, topic and template ids on POST /v1/batch - #221
Merged
Merged
Conversation
…POST /v1/batch
The batch route stored segmentId, topicId and templateId from the body
without checking they belong to the caller's organization. An org-1 key
could create a broadcast pointing at org 2's segment; the recipient count
came back 0 and the row was inserted anyway.
Each id is now resolved through an org-scoped lookup before anything is
counted or written. An unknown or foreign id returns 404 with
{ error: "Segment|Topic|Template not found" }, matching the segments
routes so the CLI maps it to NOT_FOUND.
Send-time paths already scope these lookups by organization, so existing
rows with a foreign id send to nobody rather than leaking contacts.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GxJAQNUP4FKZHL1yzPDsGd
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
POST /v1/batch(the route behindwraps broadcasts create) stored whateversegmentIdthe caller sent. An org-1 key created a broadcast with org 2's segment id and got a 200. This breaks invariant 4 (scope every lookup byorganizationId).topicIdandtemplateIdhad the same gap. Ids are checked before anything is counted, written or enqueued, and the 404 matches the segments routes, so the CLI reportsNOT_FOUND.Evidence
segmentId->expected 200 to be 404(3 mocked tests), and the same on the real DB (rejects another org's segmentId and writes no broadcast). Found by running the CLI against the real API.After: all pass.
batch-idor+batch.integration16 passed; 40 related files / 266 tests pass; typecheck and biome clean.Send time: a foreign
segmentIdalready stored resolves to nobody ingetContactsChunk/countBroadcastRecipients(both org-scoped), so it sends zero and cannot reach another org's contacts. The raw-HTML fallback when a template lookup is empty was not traced to the end.Merge Danger
Door: two-way
Every id that is present is now validated, even when
audienceTypedoes not use it, so a stale id on an"all"broadcast now 404s instead of being stored.Blast Radius: batch API clients
Existing bad rows are not cleaned up. Find them with:
select id, organization_id, segment_id from batch_send bs where segment_id is not null and not exists (select 1 from segment s where s.id = bs.segment_id and s.organization_id = bs.organization_id);🤖 Generated with Claude Code
https://claude.ai/code/session_01GxJAQNUP4FKZHL1yzPDsGd