Invite ID available to team admins even without the "Add Members" permission
Description
Improper Access Control in Mattermost Server versions 9.5.x before 9.5.2, 9.4.x before 9.4.4, 9.3.x before 9.3.3, 8.1.x before 8.1.11 lacked proper access control in the /api/v4/users/me/teams endpoint allowing a team admin to get the invite ID of their team, thus allowing them to invite users, even if the "Add Members" permission was explicitly removed from team admins.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/mattermost/mattermost/server/v8Go | >= 8.1.0, < 8.1.11 | 8.1.11 |
github.com/mattermost/mattermost/server/v8Go | >= 9.5.0, < 9.5.2 | 9.5.2 |
github.com/mattermost/mattermost/server/v8Go | >= 9.4.0, < 9.4.4 | 9.4.4 |
github.com/mattermost/mattermost/server/v8Go | >= 9.3.0, < 9.3.3 | 9.3.3 |
Affected products
1- Range: 9.5.0
Patches
40dc03fbc6e3cMM-56822 Update logic around permissions and sanitization (#26227) (#26344)
2 files changed · +39 −10
server/channels/api4/team_test.go+28 −2 modified@@ -327,7 +327,19 @@ func TestGetTeamSanitization(t *testing.T) { require.Empty(t, rteam.InviteId, "should have sanitized inviteid") }) - t.Run("team admin", func(t *testing.T) { + t.Run("team admin default removed", func(t *testing.T) { + // the above test removes PermissionInviteUser from TeamUser, + // which also removes it from TeamAdmin. By default, TeamAdmin + // permission is inherited from TeamUser. + rteam, _, err := th.Client.GetTeam(context.Background(), team.Id, "") + require.NoError(t, err) + + require.NotEmpty(t, rteam.Email, "should not have sanitized email") + require.Empty(t, rteam.InviteId, "should have sanitized inviteid") + }) + + t.Run("team admin permission re-added", func(t *testing.T) { + th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamAdminRoleId) rteam, _, err := th.Client.GetTeam(context.Background(), team.Id, "") require.NoError(t, err) @@ -1454,7 +1466,19 @@ func TestGetTeamByNameSanitization(t *testing.T) { require.Empty(t, rteam.InviteId, "should have sanitized inviteid") }) - t.Run("team admin/non-admin", func(t *testing.T) { + t.Run("team admin/non-admin without invite permission", func(t *testing.T) { + // the above test removes PermissionInviteUser from TeamUser, + // which also removes it from TeamAdmin. By default, TeamAdmin + // permission is inherited from TeamUser. + rteam, _, err := th.Client.GetTeamByName(context.Background(), team.Name, "") + require.NoError(t, err) + + require.NotEmpty(t, rteam.Email, "should not have sanitized email") + require.Empty(t, rteam.InviteId, "should have sanitized inviteid") + }) + + t.Run("team admin/non-admin with invite permission", func(t *testing.T) { + th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamAdminRoleId) rteam, _, err := th.Client.GetTeamByName(context.Background(), team.Name, "") require.NoError(t, err) @@ -1863,6 +1887,8 @@ func TestGetTeamsForUserSanitization(t *testing.T) { client := th.CreateClient() th.RemovePermissionFromRole(model.PermissionInviteUser.Id, model.TeamUserRoleId) + defer th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamUserRoleId) + th.LoginBasic2WithClient(client) rteams, _, err := client.GetTeamsForUser(context.Background(), th.BasicUser2.Id, "")
server/channels/app/team.go+11 −8 modified@@ -1930,19 +1930,22 @@ func (a *App) GetTeamIdFromQuery(query url.Values) (string, *model.AppError) { } func (a *App) SanitizeTeam(session model.Session, team *model.Team) *model.Team { - if a.SessionHasPermissionToTeam(session, team.Id, model.PermissionManageTeam) { - return team - } + manageTeamPermission := a.SessionHasPermissionToTeam(session, team.Id, model.PermissionManageTeam) + inviteUserPermission := a.SessionHasPermissionToTeam(session, team.Id, model.PermissionInviteUser) - if a.SessionHasPermissionToTeam(session, team.Id, model.PermissionInviteUser) { - inviteId := team.InviteId - team.Sanitize() - team.InviteId = inviteId + if manageTeamPermission && inviteUserPermission { return team } - + email := team.Email + inviteId := team.InviteId team.Sanitize() + if manageTeamPermission { + team.Email = email + } + if inviteUserPermission { + team.InviteId = inviteId + } return team }
a5784f34ba65MM-56822 Update logic around permissions and sanitization (#26227) (#26343)
2 files changed · +39 −10
server/channels/api4/team_test.go+28 −2 modified@@ -326,7 +326,19 @@ func TestGetTeamSanitization(t *testing.T) { require.Empty(t, rteam.InviteId, "should have sanitized inviteid") }) - t.Run("team admin", func(t *testing.T) { + t.Run("team admin default removed", func(t *testing.T) { + // the above test removes PermissionInviteUser from TeamUser, + // which also removes it from TeamAdmin. By default, TeamAdmin + // permission is inherited from TeamUser. + rteam, _, err := th.Client.GetTeam(context.Background(), team.Id, "") + require.NoError(t, err) + + require.NotEmpty(t, rteam.Email, "should not have sanitized email") + require.Empty(t, rteam.InviteId, "should have sanitized inviteid") + }) + + t.Run("team admin permission re-added", func(t *testing.T) { + th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamAdminRoleId) rteam, _, err := th.Client.GetTeam(context.Background(), team.Id, "") require.NoError(t, err) @@ -1452,7 +1464,19 @@ func TestGetTeamByNameSanitization(t *testing.T) { require.Empty(t, rteam.InviteId, "should have sanitized inviteid") }) - t.Run("team admin/non-admin", func(t *testing.T) { + t.Run("team admin/non-admin without invite permission", func(t *testing.T) { + // the above test removes PermissionInviteUser from TeamUser, + // which also removes it from TeamAdmin. By default, TeamAdmin + // permission is inherited from TeamUser. + rteam, _, err := th.Client.GetTeamByName(context.Background(), team.Name, "") + require.NoError(t, err) + + require.NotEmpty(t, rteam.Email, "should not have sanitized email") + require.Empty(t, rteam.InviteId, "should have sanitized inviteid") + }) + + t.Run("team admin/non-admin with invite permission", func(t *testing.T) { + th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamAdminRoleId) rteam, _, err := th.Client.GetTeamByName(context.Background(), team.Name, "") require.NoError(t, err) @@ -1861,6 +1885,8 @@ func TestGetTeamsForUserSanitization(t *testing.T) { client := th.CreateClient() th.RemovePermissionFromRole(model.PermissionInviteUser.Id, model.TeamUserRoleId) + defer th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamUserRoleId) + th.LoginBasic2WithClient(client) rteams, _, err := client.GetTeamsForUser(context.Background(), th.BasicUser2.Id, "")
server/channels/app/team.go+11 −8 modified@@ -1928,19 +1928,22 @@ func (a *App) GetTeamIdFromQuery(query url.Values) (string, *model.AppError) { } func (a *App) SanitizeTeam(session model.Session, team *model.Team) *model.Team { - if a.SessionHasPermissionToTeam(session, team.Id, model.PermissionManageTeam) { - return team - } + manageTeamPermission := a.SessionHasPermissionToTeam(session, team.Id, model.PermissionManageTeam) + inviteUserPermission := a.SessionHasPermissionToTeam(session, team.Id, model.PermissionInviteUser) - if a.SessionHasPermissionToTeam(session, team.Id, model.PermissionInviteUser) { - inviteId := team.InviteId - team.Sanitize() - team.InviteId = inviteId + if manageTeamPermission && inviteUserPermission { return team } - + email := team.Email + inviteId := team.InviteId team.Sanitize() + if manageTeamPermission { + team.Email = email + } + if inviteUserPermission { + team.InviteId = inviteId + } return team }
5cce9fed7363MM-56822 Update logic around permissions and sanitization (#26227) (#26342)
2 files changed · +39 −10
server/channels/api4/team_test.go+28 −2 modified@@ -326,7 +326,19 @@ func TestGetTeamSanitization(t *testing.T) { require.Empty(t, rteam.InviteId, "should have sanitized inviteid") }) - t.Run("team admin", func(t *testing.T) { + t.Run("team admin default removed", func(t *testing.T) { + // the above test removes PermissionInviteUser from TeamUser, + // which also removes it from TeamAdmin. By default, TeamAdmin + // permission is inherited from TeamUser. + rteam, _, err := th.Client.GetTeam(context.Background(), team.Id, "") + require.NoError(t, err) + + require.NotEmpty(t, rteam.Email, "should not have sanitized email") + require.Empty(t, rteam.InviteId, "should have sanitized inviteid") + }) + + t.Run("team admin permission re-added", func(t *testing.T) { + th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamAdminRoleId) rteam, _, err := th.Client.GetTeam(context.Background(), team.Id, "") require.NoError(t, err) @@ -1452,7 +1464,19 @@ func TestGetTeamByNameSanitization(t *testing.T) { require.Empty(t, rteam.InviteId, "should have sanitized inviteid") }) - t.Run("team admin/non-admin", func(t *testing.T) { + t.Run("team admin/non-admin without invite permission", func(t *testing.T) { + // the above test removes PermissionInviteUser from TeamUser, + // which also removes it from TeamAdmin. By default, TeamAdmin + // permission is inherited from TeamUser. + rteam, _, err := th.Client.GetTeamByName(context.Background(), team.Name, "") + require.NoError(t, err) + + require.NotEmpty(t, rteam.Email, "should not have sanitized email") + require.Empty(t, rteam.InviteId, "should have sanitized inviteid") + }) + + t.Run("team admin/non-admin with invite permission", func(t *testing.T) { + th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamAdminRoleId) rteam, _, err := th.Client.GetTeamByName(context.Background(), team.Name, "") require.NoError(t, err) @@ -1861,6 +1885,8 @@ func TestGetTeamsForUserSanitization(t *testing.T) { client := th.CreateClient() th.RemovePermissionFromRole(model.PermissionInviteUser.Id, model.TeamUserRoleId) + defer th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamUserRoleId) + th.LoginBasic2WithClient(client) rteams, _, err := client.GetTeamsForUser(context.Background(), th.BasicUser2.Id, "")
server/channels/app/team.go+11 −8 modified@@ -1928,19 +1928,22 @@ func (a *App) GetTeamIdFromQuery(query url.Values) (string, *model.AppError) { } func (a *App) SanitizeTeam(session model.Session, team *model.Team) *model.Team { - if a.SessionHasPermissionToTeam(session, team.Id, model.PermissionManageTeam) { - return team - } + manageTeamPermission := a.SessionHasPermissionToTeam(session, team.Id, model.PermissionManageTeam) + inviteUserPermission := a.SessionHasPermissionToTeam(session, team.Id, model.PermissionInviteUser) - if a.SessionHasPermissionToTeam(session, team.Id, model.PermissionInviteUser) { - inviteId := team.InviteId - team.Sanitize() - team.InviteId = inviteId + if manageTeamPermission && inviteUserPermission { return team } - + email := team.Email + inviteId := team.InviteId team.Sanitize() + if manageTeamPermission { + team.Email = email + } + if inviteUserPermission { + team.InviteId = inviteId + } return team }
dd3fe2991a70MM-56822 Update logic around permissions and sanitization (#26227) (#26337)
2 files changed · +39 −10
server/channels/api4/team_test.go+28 −2 modified@@ -326,7 +326,19 @@ func TestGetTeamSanitization(t *testing.T) { require.Empty(t, rteam.InviteId, "should have sanitized inviteid") }) - t.Run("team admin", func(t *testing.T) { + t.Run("team admin default removed", func(t *testing.T) { + // the above test removes PermissionInviteUser from TeamUser, + // which also removes it from TeamAdmin. By default, TeamAdmin + // permission is inherited from TeamUser. + rteam, _, err := th.Client.GetTeam(context.Background(), team.Id, "") + require.NoError(t, err) + + require.NotEmpty(t, rteam.Email, "should not have sanitized email") + require.Empty(t, rteam.InviteId, "should have sanitized inviteid") + }) + + t.Run("team admin permission re-added", func(t *testing.T) { + th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamAdminRoleId) rteam, _, err := th.Client.GetTeam(context.Background(), team.Id, "") require.NoError(t, err) @@ -1452,7 +1464,19 @@ func TestGetTeamByNameSanitization(t *testing.T) { require.Empty(t, rteam.InviteId, "should have sanitized inviteid") }) - t.Run("team admin/non-admin", func(t *testing.T) { + t.Run("team admin/non-admin without invite permission", func(t *testing.T) { + // the above test removes PermissionInviteUser from TeamUser, + // which also removes it from TeamAdmin. By default, TeamAdmin + // permission is inherited from TeamUser. + rteam, _, err := th.Client.GetTeamByName(context.Background(), team.Name, "") + require.NoError(t, err) + + require.NotEmpty(t, rteam.Email, "should not have sanitized email") + require.Empty(t, rteam.InviteId, "should have sanitized inviteid") + }) + + t.Run("team admin/non-admin with invite permission", func(t *testing.T) { + th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamAdminRoleId) rteam, _, err := th.Client.GetTeamByName(context.Background(), team.Name, "") require.NoError(t, err) @@ -1861,6 +1885,8 @@ func TestGetTeamsForUserSanitization(t *testing.T) { client := th.CreateClient() th.RemovePermissionFromRole(model.PermissionInviteUser.Id, model.TeamUserRoleId) + defer th.AddPermissionToRole(model.PermissionInviteUser.Id, model.TeamUserRoleId) + th.LoginBasic2WithClient(client) rteams, _, err := client.GetTeamsForUser(context.Background(), th.BasicUser2.Id, "")
server/channels/app/team.go+11 −8 modified@@ -1928,19 +1928,22 @@ func (a *App) GetTeamIdFromQuery(query url.Values) (string, *model.AppError) { } func (a *App) SanitizeTeam(session model.Session, team *model.Team) *model.Team { - if a.SessionHasPermissionToTeam(session, team.Id, model.PermissionManageTeam) { - return team - } + manageTeamPermission := a.SessionHasPermissionToTeam(session, team.Id, model.PermissionManageTeam) + inviteUserPermission := a.SessionHasPermissionToTeam(session, team.Id, model.PermissionInviteUser) - if a.SessionHasPermissionToTeam(session, team.Id, model.PermissionInviteUser) { - inviteId := team.InviteId - team.Sanitize() - team.InviteId = inviteId + if manageTeamPermission && inviteUserPermission { return team } - + email := team.Email + inviteId := team.InviteId team.Sanitize() + if manageTeamPermission { + team.Email = email + } + if inviteUserPermission { + team.InviteId = inviteId + } return team }
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
8- github.com/advisories/GHSA-w67v-ph4x-f48qghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-29221ghsaADVISORY
- github.com/mattermost/mattermost/commit/0dc03fbc6e3c9afb14137e72ab3fa6f5a0125b9cghsaWEB
- github.com/mattermost/mattermost/commit/5cce9fed7363386afebd81a58fb5fab7d2729c8fghsaWEB
- github.com/mattermost/mattermost/commit/a5784f34ba6592c6454b8742f24af9d06279e347ghsaWEB
- github.com/mattermost/mattermost/commit/dd3fe2991a70a41790d6bef5d31afc5957525f3cghsaWEB
- mattermost.com/security-updatesghsaWEB
- pkg.go.dev/vuln/GO-2024-2706ghsaWEB
News mentions
0No linked articles in our index yet.