Skip to content

fix(decopilot): resolve model permissions off the target org's role in dispatch-run#4947

Merged
pedrofrxncx merged 1 commit into
mainfrom
fix/dispatch-run-model-permissions-active-org-role-w1
Jul 21, 2026
Merged

fix(decopilot): resolve model permissions off the target org's role in dispatch-run#4947
pedrofrxncx merged 1 commit into
mainfrom
fix/dispatch-run-model-permissions-active-org-role-w1

Conversation

@pedrofrxncx

@pedrofrxncx pedrofrxncx commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Follows #4943 (org-SSO gate fix) and the just-opened #4944 (model-permissions active-org-role fix in routes.ts/key-list.ts/list-models.ts) — same bug class, a call site those PRs missed.

dispatchRunAndWait's model-permission check (in apps/mesh/src/api/routes/decopilot/dispatch-run.ts) calls fetchModelPermissions(ctx.db, input.organizationId, ctx.auth.user?.role). ctx.auth.user?.role reflects the session's ACTIVE org, not input.organizationId (the dispatch target) — the code comment right above it even says this is the resume/automation re-entry path that bypasses the HTTP-layer gate in routes.ts. An owner/admin of one org whose active session role is owner/admin could dispatch a run against a different org where they hold only a lower-privileged custom role, and fetchModelPermissions would treat them as admin (bypassing the role's model-tier restrictions) for that other org's models.

Fix: read ctx.organization?.role ?? ctx.auth.user?.role instead — ctx.organization.role is the path-resolved role for the target org (set by resolveOrgFromPath for HTTP-originated calls); for background automation contexts (automationContextFactory) ctx.organization.role is unset but ctx.auth.user.role is already correctly scoped to input.organizationId there, so the fallback preserves existing behavior. This mirrors the exact fix already applied to routes.ts/key-list.ts/list-models.ts in #4944.

No test added — the underlying fetchModelPermissions logic already has unit coverage (model-permissions.test.ts); this is a one-line call-site correction, same precedent as #4944 (which also shipped without a new test since dispatchRunAndWait requires heavy StudioContext mocking to invoke directly).

To verify: cd apps/mesh && bunx tsc --noEmit (clean) and read the diff against apps/mesh/src/api/routes/decopilot/routes.ts's equivalent fix in #4944 to confirm the same pattern is applied correctly.

Locally ran: bun run fmt and bunx tsc --noEmit in apps/mesh (both clean). Full CI validates the rest.


Summary by cubic

Fixes a permission gap in Decopilot dispatch-run by resolving model permissions using the target org’s role. This prevents cross-org privilege escalation when the active session org differs from the dispatch target.

  • Bug Fixes
    • Resolve permissions in dispatchRunAndWait with ctx.organization?.role ?? ctx.auth.user?.role when calling fetchModelPermissions.
    • Preserves automation behavior by falling back to ctx.auth.user.role if ctx.organization.role is unset.

Written for commit 929ad80. Summary will update on new commits.

Review in cubic

…n dispatch-run

Follows #4943/#4944 hardening. `fetchModelPermissions` in dispatchRunAndWait's
resume/automation re-entry path used `ctx.auth.user?.role` (the session's
active-org role) instead of the path-resolved role for `input.organizationId`,
letting an owner/admin of one org bypass model-tier restrictions when a
dispatch targets a different, lower-privileged org. #4944 already fixed the
same fetchModelPermissions call in routes.ts/key-list.ts/list-models.ts but
missed this call site.
@pedrofrxncx
pedrofrxncx merged commit 23694bf into main Jul 21, 2026
15 checks passed
@pedrofrxncx
pedrofrxncx deleted the fix/dispatch-run-model-permissions-active-org-role-w1 branch July 21, 2026 17:34
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