Moderate severityNVD Advisory· Published Mar 16, 2026· Updated Mar 17, 2026
Permission Bypass in Playbook Run Creation
CVE-2026-26304
Description
Mattermost versions 11.3.x <= 11.3.0, 11.2.x <= 11.2.2 fail to verify run_create permission for empty playbookId, which allows team members to create unauthorized runs via the playbook run API. Mattermost Advisory ID: MMSA-2025-00542
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/mattermost/mattermost-plugin-playbooksGo | < 1.41.1-0.20260316224925-705f54a81841 | 1.41.1-0.20260316224925-705f54a81841 |
Affected products
1- Range: 11.3.0
Patches
1705f54a81841MM-67867: Enforce target team permission check on cross-team run creation (#2212)
4 files changed · +94 −6
server/api/playbook_runs.go+1 −1 modified@@ -525,7 +525,7 @@ func (h *PlaybookRunHandler) createPlaybookRun(playbookRun app.PlaybookRun, user return nil, errors.New("playbook is archived, cannot create a new run using an archived playbook") } - if err = h.permissions.RunCreate(userID, *playbook); err != nil { + if err = h.permissions.RunCreate(userID, *playbook, playbookRun.TeamID); err != nil { return nil, err }
server/api_runs_test.go+82 −0 modified@@ -2900,3 +2900,85 @@ func TestMemberCannotCreateRunWithoutPlaybookIDToBypassPermissions(t *testing.T) require.Error(t, err, "creating a checklist in a channel where the user cannot post should fail") }) } + +// TestCrossTeamRunCreationPermission verifies that a user cannot bypass team-level +// run_create permissions by referencing a playbook from a different team. +// MM-67867 +func TestCrossTeamRunCreationPermission(t *testing.T) { + e := Setup(t) + e.CreateBasic() + + // Remove run_create from the default team_user role so that team-level + // permission is absent; only playbook-level membership grants run_create. + roles, _, err := e.ServerAdminClient.GetRolesByNames(context.Background(), []string{model.TeamUserRoleId}) + require.NoError(t, err) + require.Len(t, roles, 1) + memberRole := roles[0] + originalPermissions := memberRole.Permissions + + updatedPermissions := []string{} + for _, perm := range memberRole.Permissions { + if perm != model.PermissionRunCreate.Id { + updatedPermissions = append(updatedPermissions, perm) + } + } + _, _, err = e.ServerAdminClient.PatchRole(context.Background(), memberRole.Id, &model.RolePatch{ + Permissions: &updatedPermissions, + }) + require.NoError(t, err) + defer func() { + _, _, _ = e.ServerAdminClient.PatchRole(context.Background(), memberRole.Id, &model.RolePatch{ + Permissions: &originalPermissions, + }) + }() + + t.Run("same-team run creation still works via playbook membership", func(t *testing.T) { + run, err := e.PlaybooksClient.PlaybookRuns.Create(context.Background(), client.PlaybookRunCreateOptions{ + Name: "Same-team run", + OwnerUserID: e.RegularUser.Id, + TeamID: e.BasicTeam.Id, + PlaybookID: e.BasicPlaybook.ID, + }) + require.NoError(t, err) + require.NotNil(t, run) + }) + + t.Run("cross-team run creation is blocked without target team permission", func(t *testing.T) { + // BasicPlaybook belongs to BasicTeam. RegularUser has playbook-level + // run_create via membership. But BasicTeam2 has no team-level run_create + // (removed above) and no playbook-level grant, so this must fail. + _, err := e.PlaybooksClient.PlaybookRuns.Create(context.Background(), client.PlaybookRunCreateOptions{ + Name: "Cross-team run", + OwnerUserID: e.RegularUser.Id, + TeamID: e.BasicTeam2.Id, + PlaybookID: e.BasicPlaybook.ID, + }) + require.Error(t, err, "should not be able to create a run in a team where user lacks run_create permission") + }) +} + +// TestCrossTeamRunCreationWithPermission verifies that cross-team run creation +// succeeds when the user has run_create permission in the target team. +// By default team_user does not have run_create (it lives on playbook_member), +// so we grant it before any run creation to avoid role-cache timing issues. +// MM-67867 +func TestCrossTeamRunCreationWithPermission(t *testing.T) { + e := Setup(t) + e.CreateBasic() + + // Grant run_create at the team level before any run operations so the + // server's role cache is primed before the plugin checks permissions. + defaultRolePermissions := e.Permissions.SaveDefaultRolePermissions(t) + defer e.Permissions.RestoreDefaultRolePermissions(t, defaultRolePermissions) + e.Permissions.AddPermissionToRole(t, model.PermissionRunCreate.Id, model.TeamUserRoleId) + + run, err := e.PlaybooksClient.PlaybookRuns.Create(context.Background(), client.PlaybookRunCreateOptions{ + Name: "Cross-team run with team-level permission", + OwnerUserID: e.RegularUser.Id, + TeamID: e.BasicTeam2.Id, + PlaybookID: e.BasicPlaybook.ID, + }) + require.NoError(t, err, "cross-team run creation should succeed when user has run_create in the target team") + require.NotNil(t, run) + assert.Equal(t, e.BasicTeam2.Id, run.TeamID) +}
server/app/permissions_service.go+10 −4 modified@@ -442,12 +442,18 @@ func (p *PermissionsService) PlaybookMakePublic(userID string, playbook Playbook return errors.Wrapf(ErrNoPermissions, "user `%s` does not have permission to make playbook `%s` public", userID, playbook.ID) } -func (p *PermissionsService) RunCreate(userID string, playbook Playbook) error { - if p.hasPermissionsToPlaybook(userID, playbook, model.PermissionRunCreate) { - return nil +func (p *PermissionsService) RunCreate(userID string, playbook Playbook, targetTeamID string) error { + if !p.hasPermissionsToPlaybook(userID, playbook, model.PermissionRunCreate) { + return errors.Wrapf(ErrNoPermissions, "user `%s` does not have permission to run playbook `%s`", userID, playbook.ID) } - return errors.Wrapf(ErrNoPermissions, "user `%s` does not have permission to run playbook `%s`", userID, playbook.ID) + if targetTeamID != "" && targetTeamID != playbook.TeamID { + if !p.pluginAPI.User.HasPermissionToTeam(userID, targetTeamID, model.PermissionRunCreate) { + return errors.Wrapf(ErrNoPermissions, "user `%s` does not have permission to create a run in team `%s`", userID, targetTeamID) + } + } + + return nil } func (p *PermissionsService) RunManageProperties(userID, runID string) error {
server/app/playbook_run_service.go+1 −1 modified@@ -703,7 +703,7 @@ func (s *PlaybookRunServiceImpl) OpenCreatePlaybookRunDialog(teamID, requesterID filteredPlaybooks := make([]Playbook, 0, len(playbooks)) for _, playbook := range playbooks { - if err := s.permissions.RunCreate(requesterID, playbook); err == nil { + if err := s.permissions.RunCreate(requesterID, playbook, ""); err == nil { filteredPlaybooks = append(filteredPlaybooks, playbook) } }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-4pmx-622h-x359ghsaADVISORY
- mattermost.com/security-updatesghsavendor-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-26304ghsaADVISORY
- github.com/mattermost/mattermost-plugin-playbooks/commit/705f54a818410f3612df3865bfde608ed471037eghsaWEB
News mentions
0No linked articles in our index yet.