Skip to content

fix: enforce project_ref scope on destructive branch tools - #351

Open
mansueli wants to merge 1 commit into
mainfrom
fix/branch-scope-enforcement
Open

fix: enforce project_ref scope on destructive branch tools#351
mansueli wants to merge 1 commit into
mainfrom
fix/branch-scope-enforcement

Conversation

@mansueli

Copy link
Copy Markdown
Member

Summary

When the MCP server is scoped with project_ref / projectId, create_branch and list_branches already inject that project ID. Destructive branch tools (delete_branch, merge_branch, reset_branch, rebase_branch) only accepted a caller-controlled branch_id and forwarded it to the Management API with no check that the branch’s parent_project_ref matched the scoped project.

That breaks the documented project-scoping guarantee: a client scoped to Project A could mutate a development branch belonging to Project B if both were covered by the same org OAuth grant and the caller knew the foreign branch_id.

Fix

Before any destructive branch mutation, if the server is project-scoped:

  1. List branches for the configured project
  2. Allow the operation only if branch_id matches a branch id or project_ref under that project
  3. Otherwise reject with a clear error

Unscoped servers keep existing org-wide behavior.

Test plan

  • Unit: project-scoped MCP rejects foreign-project branch_id for delete/merge/reset/rebase and does not delete the foreign branch
  • Unit: project-scoped MCP still allows delete for a branch of the scoped project
  • Existing branch tool unit tests still pass (create / list / merge / reset / rebase / read-only)
  • CI green on this PR

Notes

  • GET /v1/branches/{branch_id_or_ref} returns BranchDetailResponse without parent_project_ref, so ownership is validated via the project’s branch list (same filter as list_branches).
  • No production Management API endpoints were changed; this is MCP-layer containment only.

When the MCP server is configured with projectId / project_ref, create_branch
and list_branches already inject that project, but delete_branch, merge_branch,
reset_branch, and rebase_branch accepted any branch_id and forwarded it to the
Management API without checking parent_project_ref.

Before mutating, resolve the scoped project's branches and require the target
branch id (or project_ref) to belong to that project. Unscoped servers keep
org-wide behavior. Adds regression tests for cross-project rejection and
same-project delete.
@mansueli
mansueli requested a review from a team as a code owner July 30, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant