Low severityNVD Advisory· Published Oct 16, 2025· Updated Oct 16, 2025
Guest user can add unauthorized team users to private channels
CVE-2025-10545
Description
Mattermost versions 10.5.x <= 10.5.10, 10.11.x <= 10.11.2 fail to properly validate guest user permissions when adding channel members which allows guest users to add any team members to their private channels via the /api/v4/channels/{channel_id}/members endpoint
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/mattermost/mattermost/server/v8Go | < 8.0.0-20250820115038-ff30b84049f0 | 8.0.0-20250820115038-ff30b84049f0 |
github.com/mattermost/mattermost-serverGo | >= 10.5.0, < 10.5.11 | 10.5.11 |
github.com/mattermost/mattermost-serverGo | >= 10.11.0, < 10.11.3 | 10.11.3 |
Affected products
1- Range: 10.5.0
Patches
2fb9c583f5e46[MM-64445] api4/channels_test: Add tests cases for guest user private channels (#31319) (#33827)
2 files changed · +106 −5
server/channels/api4/channel.go+16 −3 modified@@ -1831,9 +1831,22 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { // Security check: if the user is a guest, they must have access to the channel // to view its members - if c.AppContext.Session().IsGuest() && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { - c.SetPermissionError(model.PermissionReadChannel) - return + if c.AppContext.Session().IsGuest() { + if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + c.SetPermissionError(model.PermissionReadChannel) + return + } + for _, userId := range userIds { + allowed, appErr := c.App.UserCanSeeOtherUser(c.AppContext, c.AppContext.Session().UserId, userId) + if appErr != nil { + c.Err = appErr + return + } + if !allowed { + c.SetPermissionError(model.PermissionInviteUser) + return + } + } } if channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup {
server/channels/api4/channel_test.go+90 −2 modified@@ -149,18 +149,106 @@ func TestCreateChannel(t *testing.T) { t.Run("Test create channel with missing team id", func(t *testing.T) { channel := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypeOpen, TeamId: ""} - _, resp, err := client.CreateChannel(context.Background(), channel) + _, resp, err = client.CreateChannel(context.Background(), channel) CheckErrorID(t, err, "api.context.invalid_body_param.app_error") CheckBadRequestStatus(t, resp) }) t.Run("Test create channel with missing display name", func(t *testing.T) { channel := &model.Channel{DisplayName: "", Name: GenerateTestChannelName(), Type: model.ChannelTypeOpen, TeamId: team.Id} - _, resp, err := client.CreateChannel(context.Background(), channel) + _, resp, err = client.CreateChannel(context.Background(), channel) CheckErrorID(t, err, "api.context.invalid_body_param.app_error") CheckBadRequestStatus(t, resp) }) + + t.Run("Guest users", func(t *testing.T) { + th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterprise)) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.AllowEmailAccounts = true }) + + guestUser := th.CreateUser() + appErr := th.App.VerifyUserEmail(guestUser.Id, guestUser.Email) + require.Nil(t, appErr) + + appErr = th.App.DemoteUserToGuest(th.Context, guestUser) + require.Nil(t, appErr) + + _, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, guestUser.Id, "") + require.Nil(t, appErr) + + guestClient := th.CreateClient() + _, _, err := guestClient.Login(context.Background(), guestUser.Username, guestUser.Password) + require.NoError(t, err) + t.Cleanup(func() { + _, lErr := guestClient.Logout(context.Background()) + require.NoError(t, lErr) + }) + + userOutsideOfChannels := th.CreateUser() + _, _, err = th.Client.AddTeamMember(context.Background(), team.Id, userOutsideOfChannels.Id) + require.NoError(t, err) + + public := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypeOpen, TeamId: team.Id} + private := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypePrivate, TeamId: team.Id} + + t.Run("Guest user should not be able to create channels", func(t *testing.T) { + _, resp, err = guestClient.CreateChannel(context.Background(), public) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + + private.Name = GenerateTestChannelName() + _, resp, err = guestClient.CreateChannel(context.Background(), private) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + }) + + t.Run("Guest user should not be able to add channel members if they have no common channels", func(t *testing.T) { + // Now actually create the channels with the main client + public, _, err = th.Client.CreateChannel(context.Background(), public) + require.NoError(t, err) + private, _, err = th.Client.CreateChannel(context.Background(), private) + require.NoError(t, err) + + // Add the guest user to the private channel + _, _, err = th.Client.AddChannelMember(context.Background(), private.Id, guestUser.Id) + require.NoError(t, err) + + // Verify that the guest user can access the private channel they were added to + _, _, err = guestClient.GetChannel(context.Background(), private.Id, "") + require.NoError(t, err) + + // Verify that the guest user cannot add members to the private channel + _, resp, err = guestClient.AddChannelMember(context.Background(), private.Id, userOutsideOfChannels.Id) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + + // Add the guest user to the public channel + _, _, err = th.Client.AddChannelMember(context.Background(), public.Id, guestUser.Id) + require.NoError(t, err) + + // Verify that the guest user can access the public channel they were added to + _, _, err = guestClient.GetChannel(context.Background(), public.Id, "") + require.NoError(t, err) + + // Verify that the guest user cannot add members to the public channel + _, resp, err = guestClient.AddChannelMember(context.Background(), public.Id, userOutsideOfChannels.Id) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + + // Update team guest permissions to allow creating private channels + th.AddPermissionToRole(model.PermissionCreatePrivateChannel.Id, model.TeamGuestRoleId) + privateGuest := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypePrivate, TeamId: team.Id} + privateGuest, resp, err = guestClient.CreateChannel(context.Background(), privateGuest) + require.NoError(t, err) + CheckCreatedStatus(t, resp) + + // Verify that the guest user can't add users they have no visibility to + _, resp, err = guestClient.AddChannelMember(context.Background(), privateGuest.Id, userOutsideOfChannels.Id) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + }) + }) } func TestUpdateChannel(t *testing.T) {
ff30b84049f0[MM-64445] api4/channels_test: Add tests cases for guest user private channels (#31319)
2 files changed · +109 −7
server/channels/api4/channel.go+16 −3 modified@@ -1857,9 +1857,22 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { // Security check: if the user is a guest, they must have access to the channel // to view its members - if c.AppContext.Session().IsGuest() && !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { - c.SetPermissionError(model.PermissionReadChannel) - return + if c.AppContext.Session().IsGuest() { + if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionReadChannel) { + c.SetPermissionError(model.PermissionReadChannel) + return + } + for _, userId := range userIds { + allowed, appErr := c.App.UserCanSeeOtherUser(c.AppContext, c.AppContext.Session().UserId, userId) + if appErr != nil { + c.Err = appErr + return + } + if !allowed { + c.SetPermissionError(model.PermissionInviteUser) + return + } + } } if channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup {
server/channels/api4/channel_test.go+93 −4 modified@@ -152,15 +152,15 @@ func TestCreateChannel(t *testing.T) { t.Run("Test create channel with missing team id", func(t *testing.T) { channel := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypeOpen, TeamId: ""} - _, resp, err := client.CreateChannel(context.Background(), channel) + _, resp, err = client.CreateChannel(context.Background(), channel) CheckErrorID(t, err, "api.context.invalid_body_param.app_error") CheckBadRequestStatus(t, resp) }) t.Run("Test create channel with missing display name", func(t *testing.T) { channel := &model.Channel{DisplayName: "", Name: GenerateTestChannelName(), Type: model.ChannelTypeOpen, TeamId: team.Id} - _, resp, err := client.CreateChannel(context.Background(), channel) + _, resp, err = client.CreateChannel(context.Background(), channel) CheckErrorID(t, err, "api.context.invalid_body_param.app_error") CheckBadRequestStatus(t, resp) }) @@ -178,7 +178,8 @@ func TestCreateChannel(t *testing.T) { }, } - createdChannel, resp, err := client.CreateChannel(context.Background(), channel) + var createdChannel *model.Channel + createdChannel, resp, err = client.CreateChannel(context.Background(), channel) require.NoError(t, err) CheckCreatedStatus(t, resp) @@ -198,10 +199,98 @@ func TestCreateChannel(t *testing.T) { }, } - _, resp, err := client.CreateChannel(context.Background(), channel) + _, resp, err = client.CreateChannel(context.Background(), channel) CheckErrorID(t, err, "api.context.invalid_body_param.app_error") CheckBadRequestStatus(t, resp) }) + + t.Run("Guest users", func(t *testing.T) { + th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterprise)) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.AllowEmailAccounts = true }) + + guestUser := th.CreateUser() + appErr := th.App.VerifyUserEmail(guestUser.Id, guestUser.Email) + require.Nil(t, appErr) + + appErr = th.App.DemoteUserToGuest(th.Context, guestUser) + require.Nil(t, appErr) + + _, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, guestUser.Id, "") + require.Nil(t, appErr) + + guestClient := th.CreateClient() + _, _, err := guestClient.Login(context.Background(), guestUser.Username, guestUser.Password) + require.NoError(t, err) + t.Cleanup(func() { + _, lErr := guestClient.Logout(context.Background()) + require.NoError(t, lErr) + }) + + userOutsideOfChannels := th.CreateUser() + _, _, err = th.Client.AddTeamMember(context.Background(), team.Id, userOutsideOfChannels.Id) + require.NoError(t, err) + + public := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypeOpen, TeamId: team.Id} + private := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypePrivate, TeamId: team.Id} + + t.Run("Guest user should not be able to create channels", func(t *testing.T) { + _, resp, err = guestClient.CreateChannel(context.Background(), public) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + + private.Name = GenerateTestChannelName() + _, resp, err = guestClient.CreateChannel(context.Background(), private) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + }) + + t.Run("Guest user should not be able to add channel members if they have no common channels", func(t *testing.T) { + // Now actually create the channels with the main client + public, _, err = th.Client.CreateChannel(context.Background(), public) + require.NoError(t, err) + private, _, err = th.Client.CreateChannel(context.Background(), private) + require.NoError(t, err) + + // Add the guest user to the private channel + _, _, err = th.Client.AddChannelMember(context.Background(), private.Id, guestUser.Id) + require.NoError(t, err) + + // Verify that the guest user can access the private channel they were added to + _, _, err = guestClient.GetChannel(context.Background(), private.Id, "") + require.NoError(t, err) + + // Verify that the guest user cannot add members to the private channel + _, resp, err = guestClient.AddChannelMember(context.Background(), private.Id, userOutsideOfChannels.Id) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + + // Add the guest user to the public channel + _, _, err = th.Client.AddChannelMember(context.Background(), public.Id, guestUser.Id) + require.NoError(t, err) + + // Verify that the guest user can access the public channel they were added to + _, _, err = guestClient.GetChannel(context.Background(), public.Id, "") + require.NoError(t, err) + + // Verify that the guest user cannot add members to the public channel + _, resp, err = guestClient.AddChannelMember(context.Background(), public.Id, userOutsideOfChannels.Id) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + + // Update team guest permissions to allow creating private channels + th.AddPermissionToRole(model.PermissionCreatePrivateChannel.Id, model.TeamGuestRoleId) + privateGuest := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.ChannelTypePrivate, TeamId: team.Id} + privateGuest, resp, err = guestClient.CreateChannel(context.Background(), privateGuest) + require.NoError(t, err) + CheckCreatedStatus(t, resp) + + // Verify that the guest user can't add users they have no visibility to + _, resp, err = guestClient.AddChannelMember(context.Background(), privateGuest.Id, userOutsideOfChannels.Id) + require.Error(t, err) + CheckForbiddenStatus(t, resp) + }) + }) } func TestUpdateChannel(t *testing.T) {
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
7- github.com/advisories/GHSA-424h-xj87-m937ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-10545ghsaADVISORY
- github.com/mattermost/mattermost/commit/fb9c583f5e466a566a5122154ef337bbf2238902ghsaWEB
- github.com/mattermost/mattermost/commit/ff30b84049f0193f0570d30e46cffc3602298c67ghsaWEB
- github.com/mattermost/mattermost/pull/31319ghsaWEB
- github.com/mattermost/mattermost/pull/33827ghsaWEB
- mattermost.com/security-updatesghsaWEB
News mentions
0No linked articles in our index yet.