fix(organization): scope ORGANIZATION_MEMBER_UPDATE_ROLE to the authenticated org - #7000
Merged
Merged
Conversation
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.
Follows the org-scoping lane (#3388, and the same class already fixed in
update.ts/member-add.ts/member-remove.ts/delete.ts).ORGANIZATION_MEMBER_UPDATE_ROLEaccepts an optionalorganizationIdinput, but unlike its siblings it never checked that value againstctx.organization?.id(the path-resolved orgctx.access.check()actually authorized the caller against). Every other org-mutation tool (member-add.ts,member-remove.ts,update.ts,delete.ts) has this exact guard; this one was missed.Why it matters:
ctx.access.check()verifies the caller's permission inctx.organization(resolved from the URL slug), not in whateverorganizationIdthe request body carries. Without the guard, a caller authorized for org A could passorganizationId: <org B>and have the tool operate against org B instead of the org the request was actually authorized for — the same scope-confusion bug class fixed inORGANIZATION_UPDATE(#6541-era fix). The existingcanAssignRolecheck against the target org's real membership row still gates privilege escalation, but doesn't stop the tool acting on an org outside the onectx.access.check()validated.Fix: add the identical
if (organizationId !== ctx.organization?.id) throw ...guard already present in the sibling tools, plus a regression test asserting the mismatch is rejected beforeupdateMemberRoleis ever called.Verify:
cd apps/api && bun test src/tools/organization/member-update-role.test.tsLocally ran:
bun run fmt,bunx tsc --noEmit(apps/api), the targeted test above, andbunx oxlinton both changed files — all clean. Full CI validates the rest.Summary by cubic
Fixes a missing org-scoping guard in
ORGANIZATION_MEMBER_UPDATE_ROLEso callers can no longer act on an organization other than the one the request was authorized for. Previously the tool accepted anorganizationIdwithout checking it against the authenticated org, so a caller authorized for org A could pass org B's ID and the tool would operate on org B.organizationId !== ctx.organization?.idguard already present inmember-add.ts,member-remove.ts,update.ts, anddelete.ts.updateMemberRoleis called.Written for commit 641419d. Summary will update on new commits.