Low severityNVD Advisory· Published Jun 11, 2025· Updated Jun 11, 2025
Mattermost Guest User Information Disclosure Vulnerability
CVE-2025-4128
Description
Mattermost versions 10.5.x <= 10.5.4, 9.11.x <= 9.11.13 fail to properly restrict API access to team information, allowing guest users to bypass permissions and view information about public teams they are not members of via a direct API call to /api/v4/teams/{team_id}.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/mattermost/mattermost/server/v8Go | < 8.0.0-20250422131222-701ddc896a10 | 8.0.0-20250422131222-701ddc896a10 |
github.com/mattermost/mattermost-serverGo | >= 10.5.0, < 10.5.5 | 10.5.5 |
github.com/mattermost/mattermost-serverGo | >= 9.11.0, < 9.11.14 | 9.11.14 |
Affected products
1- Range: 10.5.0
Patches
22138a5f2ca6fMM-63791: guest permissions to teams (#30789) (#30874)
5 files changed · +122 −19
server/channels/api4/apitestlib.go+7 −12 modified@@ -656,7 +656,8 @@ func (th *TestHelper) CreateUserWithAuth(authService string) *model.User { // CreateGuestAndClient creates a guest user, adds them to the basic // team, basic channel and basic private channel, and generates an API // client ready to use -func (th *TestHelper) CreateGuestAndClient() (*model.User, *model.Client4) { +func (th *TestHelper) CreateGuestAndClient(tb testing.TB) (*model.User, *model.Client4) { + tb.Helper() id := model.NewId() // create a guest user and add it to the basic team and public/private channels @@ -667,23 +668,17 @@ func (th *TestHelper) CreateGuestAndClient() (*model.User, *model.Client4) { Password: "Password1", EmailVerified: true, }) - if cgErr != nil { - panic(cgErr) - } + require.Nil(tb, cgErr) - _, _, tErr := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, guest.Id, th.SystemAdminUser.Id) - if tErr != nil { - panic(tErr) - } + _, _, appErr := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, guest.Id, th.SystemAdminUser.Id) + require.Nil(tb, appErr) th.AddUserToChannel(guest, th.BasicChannel) th.AddUserToChannel(guest, th.BasicPrivateChannel) // create a client and login the guest guestClient := th.CreateClient() - _, _, lErr := guestClient.Login(context.Background(), guest.Username, "Password1") - if lErr != nil { - panic(lErr) - } + _, _, err := guestClient.Login(context.Background(), guest.Email, "Password1") + require.NoError(tb, err) return guest, guestClient }
server/channels/api4/channel_bookmark_test.go+5 −5 modified@@ -41,7 +41,7 @@ func TestCreateChannelBookmark(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) th.App.Srv().SetLicense(model.NewTestLicense()) - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) t.Run("a user should be able to create a channel bookmark in a public channel", func(t *testing.T) { channelBookmark := &model.ChannelBookmark{ @@ -290,7 +290,7 @@ func TestEditChannelBookmark(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) th.App.Srv().SetLicense(model.NewTestLicense()) - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) t.Run("a user editing a channel bookmark in public and private channels", func(t *testing.T) { testCases := []struct { @@ -720,7 +720,7 @@ func TestUpdateChannelBookmarkSortOrder(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) th.App.Srv().SetLicense(model.NewTestLicense()) - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) t.Run("a user updating a bookmark's order in public and private channels", func(t *testing.T) { testCases := []struct { @@ -1085,7 +1085,7 @@ func TestDeleteChannelBookmark(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) th.App.Srv().SetLicense(model.NewTestLicense()) - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) t.Run("a user deleting bookmarks in public and private channels", func(t *testing.T) { testCases := []struct { @@ -1452,7 +1452,7 @@ func TestListChannelBookmarksForChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) th.App.Srv().SetLicense(model.NewTestLicense()) - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) publicBookmark1 := createBookmark("one", th.BasicChannel.Id) publicBookmark2 := createBookmark("two", th.BasicChannel.Id)
server/channels/api4/channel_test.go+1 −1 modified@@ -3607,7 +3607,7 @@ func TestAddChannelMemberGuestAccessControl(t *testing.T) { th.App.Srv().SetLicense(model.NewTestLicense()) // Create a guest user - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) // Create a public channel to which the guest doesn't belong publicChannel := th.CreatePublicChannel()
server/channels/api4/team.go+10 −1 modified@@ -151,7 +151,16 @@ func getTeam(c *Context, w http.ResponseWriter, r *http.Request) { return } - if (!team.AllowOpenInvite || team.Type != model.TeamOpen) && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) { + isPublicTeam := team.AllowOpenInvite && team.Type == model.TeamOpen + hasPermissionViewTeam := c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) + + if !isPublicTeam && !hasPermissionViewTeam { + c.SetPermissionError(model.PermissionViewTeam) + return + } + + if isPublicTeam && !hasPermissionViewTeam && !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionListPublicTeams) { + // Fail with PermissionViewTeam, not PermissionListPublicTeams. c.SetPermissionError(model.PermissionViewTeam) return }
server/channels/api4/team_guest_test.go+99 −0 added@@ -0,0 +1,99 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package api4 + +import ( + "context" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/mattermost/mattermost/server/public/model" +) + +func TestGetTeamAsGuest(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterprise)) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) + + guest, guestClient := th.CreateGuestAndClient(t) + + publicTeamNotAMember := &model.Team{ + DisplayName: "Public Team (guest is not a member)", + Name: GenerateTestTeamName(), + Email: th.GenerateTestEmail(), + Type: model.TeamOpen, + AllowOpenInvite: true, + } + publicTeamNotAMember, _, err := th.SystemAdminClient.CreateTeam(context.Background(), publicTeamNotAMember) + require.NoError(t, err) + + publicTeamIsAMember := &model.Team{ + DisplayName: "Public Team (guest is a member)", + Name: GenerateTestTeamName(), + Email: th.GenerateTestEmail(), + Type: model.TeamOpen, + AllowOpenInvite: true, + } + publicTeamIsAMember, _, err = th.SystemAdminClient.CreateTeam(context.Background(), publicTeamIsAMember) + require.NoError(t, err) + + _, _, err = th.SystemAdminClient.AddTeamMember(context.Background(), publicTeamIsAMember.Id, guest.Id) + require.NoError(t, err) + + privateTeamNotAMember := &model.Team{ + DisplayName: "Private Team (guest is not a member)", + Name: GenerateTestTeamName(), + Email: th.GenerateTestEmail(), + Type: model.TeamInvite, + AllowOpenInvite: false, + } + privateTeamNotAMember, _, err = th.SystemAdminClient.CreateTeam(context.Background(), privateTeamNotAMember) + require.NoError(t, err) + + privateTeamIsAMember := &model.Team{ + DisplayName: "Private Team (guest is not a member)", + Name: GenerateTestTeamName(), + Email: th.GenerateTestEmail(), + Type: model.TeamInvite, + AllowOpenInvite: false, + } + privateTeamIsAMember, _, err = th.SystemAdminClient.CreateTeam(context.Background(), privateTeamIsAMember) + require.NoError(t, err) + + _, _, err = th.SystemAdminClient.AddTeamMember(context.Background(), privateTeamIsAMember.Id, guest.Id) + require.NoError(t, err) + + t.Run("guest cannot view public team they are not a member of", func(t *testing.T) { + team, resp, err := guestClient.GetTeam(context.Background(), publicTeamNotAMember.Id, "") + require.Error(t, err) + require.Equal(t, http.StatusForbidden, resp.StatusCode) + assert.Nil(t, team) + }) + + t.Run("guest can view public team they are a member of", func(t *testing.T) { + team, resp, err := guestClient.GetTeam(context.Background(), publicTeamIsAMember.Id, "") + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, publicTeamIsAMember.Id, team.Id) + }) + + t.Run("guest can not view private team they are not a member of", func(t *testing.T) { + team, resp, err := guestClient.GetTeam(context.Background(), privateTeamNotAMember.Id, "") + require.Error(t, err) + require.Equal(t, http.StatusForbidden, resp.StatusCode) + assert.Nil(t, team) + }) + + t.Run("guest can view private team they are a member of", func(t *testing.T) { + team, resp, err := guestClient.GetTeam(context.Background(), privateTeamIsAMember.Id, "") + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, privateTeamIsAMember.Id, team.Id) + }) +}
701ddc896a10MM-63791: guest permissions to teams (#30789)
5 files changed · +122 −19
server/channels/api4/apitestlib.go+7 −12 modified@@ -695,7 +695,8 @@ func (th *TestHelper) CreateUserWithAuth(authService string) *model.User { // CreateGuestAndClient creates a guest user, adds them to the basic // team, basic channel and basic private channel, and generates an API // client ready to use -func (th *TestHelper) CreateGuestAndClient() (*model.User, *model.Client4) { +func (th *TestHelper) CreateGuestAndClient(tb testing.TB) (*model.User, *model.Client4) { + tb.Helper() id := model.NewId() // create a guest user and add it to the basic team and public/private channels @@ -706,23 +707,17 @@ func (th *TestHelper) CreateGuestAndClient() (*model.User, *model.Client4) { Password: "Password1", EmailVerified: true, }) - if cgErr != nil { - panic(cgErr) - } + require.Nil(tb, cgErr) - _, _, tErr := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, guest.Id, th.SystemAdminUser.Id) - if tErr != nil { - panic(tErr) - } + _, _, appErr := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, guest.Id, th.SystemAdminUser.Id) + require.Nil(tb, appErr) th.AddUserToChannel(guest, th.BasicChannel) th.AddUserToChannel(guest, th.BasicPrivateChannel) // create a client and login the guest guestClient := th.CreateClient() - _, _, lErr := guestClient.Login(context.Background(), guest.Username, "Password1") - if lErr != nil { - panic(lErr) - } + _, _, err := guestClient.Login(context.Background(), guest.Email, "Password1") + require.NoError(tb, err) return guest, guestClient }
server/channels/api4/channel_bookmark_test.go+5 −5 modified@@ -41,7 +41,7 @@ func TestCreateChannelBookmark(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) th.App.Srv().SetLicense(model.NewTestLicense()) - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) t.Run("a user should be able to create a channel bookmark in a public channel", func(t *testing.T) { channelBookmark := &model.ChannelBookmark{ @@ -297,7 +297,7 @@ func TestEditChannelBookmark(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) th.App.Srv().SetLicense(model.NewTestLicense()) - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) t.Run("a user editing a channel bookmark in public and private channels", func(t *testing.T) { testCases := []struct { @@ -732,7 +732,7 @@ func TestUpdateChannelBookmarkSortOrder(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) th.App.Srv().SetLicense(model.NewTestLicense()) - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) t.Run("a user updating a bookmark's order in public and private channels", func(t *testing.T) { testCases := []struct { @@ -1121,7 +1121,7 @@ func TestDeleteChannelBookmark(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) th.App.Srv().SetLicense(model.NewTestLicense()) - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) t.Run("a user deleting bookmarks in public and private channels", func(t *testing.T) { testCases := []struct { @@ -1494,7 +1494,7 @@ func TestListChannelBookmarksForChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) th.App.Srv().SetLicense(model.NewTestLicense()) - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) publicBookmark1 := createBookmark("one", th.BasicChannel.Id) publicBookmark2 := createBookmark("two", th.BasicChannel.Id)
server/channels/api4/channel_test.go+1 −1 modified@@ -4102,7 +4102,7 @@ func TestAddChannelMemberGuestAccessControl(t *testing.T) { th.App.Srv().SetLicense(model.NewTestLicense()) // Create a guest user - guest, guestClient := th.CreateGuestAndClient() + guest, guestClient := th.CreateGuestAndClient(t) // Create a public channel to which the guest doesn't belong publicChannel := th.CreatePublicChannel()
server/channels/api4/team.go+10 −1 modified@@ -151,7 +151,16 @@ func getTeam(c *Context, w http.ResponseWriter, r *http.Request) { return } - if (!team.AllowOpenInvite || team.Type != model.TeamOpen) && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) { + isPublicTeam := team.AllowOpenInvite && team.Type == model.TeamOpen + hasPermissionViewTeam := c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), team.Id, model.PermissionViewTeam) + + if !isPublicTeam && !hasPermissionViewTeam { + c.SetPermissionError(model.PermissionViewTeam) + return + } + + if isPublicTeam && !hasPermissionViewTeam && !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionListPublicTeams) { + // Fail with PermissionViewTeam, not PermissionListPublicTeams. c.SetPermissionError(model.PermissionViewTeam) return }
server/channels/api4/team_guest_test.go+99 −0 added@@ -0,0 +1,99 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package api4 + +import ( + "context" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/mattermost/mattermost/server/public/model" +) + +func TestGetTeamAsGuest(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterprise)) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.Enable = true }) + + guest, guestClient := th.CreateGuestAndClient(t) + + publicTeamNotAMember := &model.Team{ + DisplayName: "Public Team (guest is not a member)", + Name: GenerateTestTeamName(), + Email: th.GenerateTestEmail(), + Type: model.TeamOpen, + AllowOpenInvite: true, + } + publicTeamNotAMember, _, err := th.SystemAdminClient.CreateTeam(context.Background(), publicTeamNotAMember) + require.NoError(t, err) + + publicTeamIsAMember := &model.Team{ + DisplayName: "Public Team (guest is a member)", + Name: GenerateTestTeamName(), + Email: th.GenerateTestEmail(), + Type: model.TeamOpen, + AllowOpenInvite: true, + } + publicTeamIsAMember, _, err = th.SystemAdminClient.CreateTeam(context.Background(), publicTeamIsAMember) + require.NoError(t, err) + + _, _, err = th.SystemAdminClient.AddTeamMember(context.Background(), publicTeamIsAMember.Id, guest.Id) + require.NoError(t, err) + + privateTeamNotAMember := &model.Team{ + DisplayName: "Private Team (guest is not a member)", + Name: GenerateTestTeamName(), + Email: th.GenerateTestEmail(), + Type: model.TeamInvite, + AllowOpenInvite: false, + } + privateTeamNotAMember, _, err = th.SystemAdminClient.CreateTeam(context.Background(), privateTeamNotAMember) + require.NoError(t, err) + + privateTeamIsAMember := &model.Team{ + DisplayName: "Private Team (guest is not a member)", + Name: GenerateTestTeamName(), + Email: th.GenerateTestEmail(), + Type: model.TeamInvite, + AllowOpenInvite: false, + } + privateTeamIsAMember, _, err = th.SystemAdminClient.CreateTeam(context.Background(), privateTeamIsAMember) + require.NoError(t, err) + + _, _, err = th.SystemAdminClient.AddTeamMember(context.Background(), privateTeamIsAMember.Id, guest.Id) + require.NoError(t, err) + + t.Run("guest cannot view public team they are not a member of", func(t *testing.T) { + team, resp, err := guestClient.GetTeam(context.Background(), publicTeamNotAMember.Id, "") + require.Error(t, err) + require.Equal(t, http.StatusForbidden, resp.StatusCode) + assert.Nil(t, team) + }) + + t.Run("guest can view public team they are a member of", func(t *testing.T) { + team, resp, err := guestClient.GetTeam(context.Background(), publicTeamIsAMember.Id, "") + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, publicTeamIsAMember.Id, team.Id) + }) + + t.Run("guest can not view private team they are not a member of", func(t *testing.T) { + team, resp, err := guestClient.GetTeam(context.Background(), privateTeamNotAMember.Id, "") + require.Error(t, err) + require.Equal(t, http.StatusForbidden, resp.StatusCode) + assert.Nil(t, team) + }) + + t.Run("guest can view private team they are a member of", func(t *testing.T) { + team, resp, err := guestClient.GetTeam(context.Background(), privateTeamIsAMember.Id, "") + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, privateTeamIsAMember.Id, team.Id) + }) +}
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
5- github.com/advisories/GHSA-jwhw-xf5v-qgxcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-4128ghsaADVISORY
- github.com/mattermost/mattermost/commit/2138a5f2ca6f75e2b99f6a04ea569d0f680c4fabghsaWEB
- github.com/mattermost/mattermost/commit/701ddc896a107b13f457fbdbe229bce5019fc516ghsaWEB
- mattermost.com/security-updatesghsaWEB
News mentions
0No linked articles in our index yet.