VYPR
Low severityNVD Advisory· Published Feb 16, 2026· Updated Feb 17, 2026

Team Admin Bypass of Invite Permissions via allow_open_invite Field

CVE-2025-14573

Description

Mattermost versions 10.11.x <= 10.11.9 fail to enforce invite permissions when updating team settings, which allows team administrators without proper permissions to bypass restrictions and add users to their team via API requests. Mattermost Advisory ID: MMSA-2025-00561

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/mattermost/mattermost/server/v8Go
< 8.0.0-20251215190648-6404ab29acc08.0.0-20251215190648-6404ab29acc0
github.com/mattermost/mattermost-serverGo
>= 11.1.0
github.com/mattermost/mattermost-serverGo
>= 10.11.0
github.com/mattermost/mattermost-serverGo
>= 11.2.0
github.com/mattermost/mattermost-serverGo
< 5.3.2-0.20251215190648-6404ab29acc05.3.2-0.20251215190648-6404ab29acc0

Affected products

1

Patches

1
6404ab29acc0

MM-66424: Improve team filtering in common teams API (#34454)

https://github.com/mattermost/mattermostJesse HallamDec 15, 2025via ghsa
6 files changed · +438 38
  • server/channels/api4/channel_common_teams_test.go+180 0 added
    @@ -0,0 +1,180 @@
    +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
    +// See LICENSE.txt for license information.
    +
    +package api4
    +
    +import (
    +	"context"
    +	"testing"
    +
    +	"github.com/mattermost/mattermost/server/public/model"
    +	"github.com/stretchr/testify/assert"
    +	"github.com/stretchr/testify/require"
    +)
    +
    +func TestGetDirectOrGroupMessageMembersCommonTeams(t *testing.T) {
    +	mainHelper.Parallel(t)
    +
    +	th := Setup(t).InitBasic(t)
    +	client := th.Client
    +
    +	t.Run("requires authentication", func(t *testing.T) {
    +		user1 := th.BasicUser
    +		user2 := th.BasicUser2
    +
    +		testClient := th.CreateClient()
    +		_, _, err := testClient.Login(context.Background(), user1.Email, user1.Password)
    +		require.NoError(t, err)
    +
    +		dmChannel, _, err := testClient.CreateDirectChannel(context.Background(), user1.Id, user2.Id)
    +		require.NoError(t, err)
    +
    +		_, err = testClient.Logout(context.Background())
    +		require.NoError(t, err)
    +
    +		_, resp, err := testClient.GetDirectOrGroupMessageMembersCommonTeams(context.Background(), dmChannel.Id)
    +		require.Error(t, err)
    +		CheckUnauthorizedStatus(t, resp)
    +	})
    +
    +	t.Run("forbids guest users", func(t *testing.T) {
    +		th.App.UpdateConfig(func(cfg *model.Config) {
    +			*cfg.GuestAccountsSettings.Enable = true
    +		})
    +		th.App.Srv().SetLicense(model.NewTestLicense())
    +
    +		guestUser, guestClient := th.CreateGuestAndClient(t)
    +		team1 := th.BasicTeam
    +		th.LinkUserToTeam(t, guestUser, team1)
    +
    +		user2 := th.BasicUser2
    +		th.LinkUserToTeam(t, user2, team1)
    +
    +		dmChannel, _, err := th.SystemAdminClient.CreateDirectChannel(context.Background(), guestUser.Id, user2.Id)
    +		require.NoError(t, err)
    +
    +		_, resp, err := guestClient.GetDirectOrGroupMessageMembersCommonTeams(context.Background(), dmChannel.Id)
    +		require.Error(t, err)
    +		CheckForbiddenStatus(t, resp)
    +	})
    +
    +	t.Run("requires read permission on channel", func(t *testing.T) {
    +		user1 := th.CreateUser(t)
    +		user2 := th.CreateUser(t)
    +		team1 := th.CreateTeam(t)
    +
    +		th.LinkUserToTeam(t, user1, team1)
    +		th.LinkUserToTeam(t, user2, team1)
    +
    +		dmChannel, _, err := th.SystemAdminClient.CreateDirectChannel(context.Background(), user1.Id, user2.Id)
    +		require.NoError(t, err)
    +
    +		otherUser := th.CreateUser(t)
    +		th.LinkUserToTeam(t, otherUser, team1)
    +
    +		otherClient := th.CreateClient()
    +		_, _, err = otherClient.Login(context.Background(), otherUser.Email, otherUser.Password)
    +		require.NoError(t, err)
    +
    +		_, resp, err := otherClient.GetDirectOrGroupMessageMembersCommonTeams(context.Background(), dmChannel.Id)
    +		require.Error(t, err)
    +		CheckForbiddenStatus(t, resp)
    +	})
    +
    +	t.Run("returns bad request for non-DM/GM channel", func(t *testing.T) {
    +		testClient := th.CreateClient()
    +		_, _, err := testClient.Login(context.Background(), th.BasicUser.Email, th.BasicUser.Password)
    +		require.NoError(t, err)
    +
    +		_, resp, err := testClient.GetDirectOrGroupMessageMembersCommonTeams(context.Background(), th.BasicChannel.Id)
    +		require.Error(t, err)
    +		CheckBadRequestStatus(t, resp)
    +	})
    +
    +	t.Run("returns common teams for DM channel members", func(t *testing.T) {
    +		user1 := th.BasicUser
    +		user2 := th.BasicUser2
    +		team1 := th.BasicTeam
    +		team2 := th.CreateTeam(t)
    +
    +		th.LinkUserToTeam(t, user1, team1)
    +		th.LinkUserToTeam(t, user1, team2)
    +		th.LinkUserToTeam(t, user2, team1)
    +
    +		dmChannel, _, err := client.CreateDirectChannel(context.Background(), user1.Id, user2.Id)
    +		require.NoError(t, err)
    +
    +		teams, _, err := client.GetDirectOrGroupMessageMembersCommonTeams(context.Background(), dmChannel.Id)
    +		require.NoError(t, err)
    +		require.Len(t, teams, 1, "should only return team1 since user2 is not in team2")
    +		assert.Equal(t, team1.Id, teams[0].Id)
    +	})
    +
    +	t.Run("returns common teams for GM channel members", func(t *testing.T) {
    +		user1 := th.BasicUser
    +		user2 := th.BasicUser2
    +		user3 := th.CreateUser(t)
    +		team1 := th.BasicTeam
    +		team2 := th.CreateTeam(t)
    +		team3 := th.CreateTeam(t)
    +
    +		th.LinkUserToTeam(t, user1, team1)
    +		th.LinkUserToTeam(t, user1, team2)
    +		th.LinkUserToTeam(t, user2, team1)
    +		th.LinkUserToTeam(t, user2, team3)
    +		th.LinkUserToTeam(t, user3, team1)
    +
    +		gmChannel, _, err := client.CreateGroupChannel(context.Background(), []string{user1.Id, user2.Id, user3.Id})
    +		require.NoError(t, err)
    +
    +		teams, _, err := client.GetDirectOrGroupMessageMembersCommonTeams(context.Background(), gmChannel.Id)
    +		require.NoError(t, err)
    +		require.Len(t, teams, 1, "should only return team1 since it's the only team all three users share")
    +		assert.Equal(t, team1.Id, teams[0].Id)
    +	})
    +
    +	t.Run("returns empty list when requesting user in channel but has no common teams with other members", func(t *testing.T) {
    +		user1 := th.CreateUser(t)
    +		user2 := th.CreateUser(t)
    +		team1 := th.CreateTeam(t)
    +		team2 := th.CreateTeam(t)
    +
    +		th.LinkUserToTeam(t, user1, team1)
    +		th.LinkUserToTeam(t, user2, team2)
    +
    +		testClient := th.CreateClient()
    +		_, _, err := testClient.Login(context.Background(), user1.Email, user1.Password)
    +		require.NoError(t, err)
    +
    +		dmChannel, _, err := testClient.CreateDirectChannel(context.Background(), user1.Id, user2.Id)
    +		require.NoError(t, err)
    +
    +		teams, _, err := testClient.GetDirectOrGroupMessageMembersCommonTeams(context.Background(), dmChannel.Id)
    +		require.NoError(t, err)
    +		require.Empty(t, teams)
    +	})
    +
    +	t.Run("filters teams to only those common with requesting user", func(t *testing.T) {
    +		user1 := th.CreateUser(t)
    +		user2 := th.CreateUser(t)
    +		user3 := th.BasicUser
    +		team1 := th.CreateTeam(t)
    +		team2 := th.CreateTeam(t)
    +		team3 := th.CreateTeam(t)
    +
    +		th.LinkUserToTeam(t, user1, team1)
    +		th.LinkUserToTeam(t, user1, team2)
    +		th.LinkUserToTeam(t, user2, team1)
    +		th.LinkUserToTeam(t, user2, team3)
    +		th.LinkUserToTeam(t, user3, team1)
    +		th.LinkUserToTeam(t, user3, team3)
    +
    +		gmChannel, _, err := client.CreateGroupChannel(context.Background(), []string{user1.Id, user2.Id, user3.Id})
    +		require.NoError(t, err)
    +
    +		teams, _, err := client.GetDirectOrGroupMessageMembersCommonTeams(context.Background(), gmChannel.Id)
    +		require.NoError(t, err)
    +		require.Len(t, teams, 1)
    +		assert.Equal(t, team1.Id, teams[0].Id)
    +	})
    +}
    
  • server/channels/api4/channel.go+1 1 modified
    @@ -2462,7 +2462,7 @@ func getDirectOrGroupMessageMembersCommonTeams(c *Context, w http.ResponseWriter
     		return
     	}
     
    -	teams, appErr := c.App.GetDirectOrGroupMessageMembersCommonTeams(c.AppContext, c.Params.ChannelId)
    +	teams, appErr := c.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(c.AppContext, c.Params.ChannelId)
     	if appErr != nil {
     		c.Err = appErr
     		return
    
  • server/channels/app/channel.go+28 0 modified
    @@ -3689,7 +3689,25 @@ func (a *App) getDirectChannel(rctx request.CTX, userID, otherUserID string) (*m
     	return a.Srv().getDirectChannel(rctx, userID, otherUserID)
     }
     
    +// GetDirectOrGroupMessageMembersCommonTeamsAsUser is a variant of GetDirectOrGroupMessageMembersCommonTeams
    +// that returns results relative to the requesting user from the session in the request context.
    +func (a *App) GetDirectOrGroupMessageMembersCommonTeamsAsUser(rctx request.CTX, channelID string) ([]*model.Team, *model.AppError) {
    +	return a.getDirectOrGroupMessageMembersCommonTeams(rctx, rctx.Session().UserId, channelID)
    +}
    +
    +// GetDirectOrGroupMessageMembersCommonTeams returns the set of teams in common for the members of the given DM/GM channel.
    +//
    +// Prefer GetDirectOrGroupMessageMembersCommonTeamsAsUser unless the request context is independent of any given user.
     func (a *App) GetDirectOrGroupMessageMembersCommonTeams(rctx request.CTX, channelID string) ([]*model.Team, *model.AppError) {
    +	return a.getDirectOrGroupMessageMembersCommonTeams(rctx, "", channelID)
    +}
    +
    +// getDirectOrGroupMessageMembersCommonTeams returns the set teams common to the members of the given channel.
    +//
    +// If a requesting user id is specified, but the user isn't an active member of the channel, we return an empty
    +// set of channels. We don't just exclude all inactive users to offer more flexibility to the remaining users
    +// on where to create the replacement channel.
    +func (a *App) getDirectOrGroupMessageMembersCommonTeams(rctx request.CTX, requestingUserID, channelID string) ([]*model.Team, *model.AppError) {
     	channel, appErr := a.GetChannel(rctx, channelID)
     	if appErr != nil {
     		return nil, appErr
    @@ -3706,6 +3724,9 @@ func (a *App) GetDirectOrGroupMessageMembersCommonTeams(rctx request.CTX, channe
     		Inactive:    false,
     		Active:      true,
     	})
    +	if appErr != nil {
    +		return nil, appErr
    +	}
     
     	userIDs := make([]string, 0, len(users))
     	for _, user := range users {
    @@ -3721,6 +3742,13 @@ func (a *App) GetDirectOrGroupMessageMembersCommonTeams(rctx request.CTX, channe
     		userIDs = append(userIDs, user.Id)
     	}
     
    +	// If a requesting user is specified, but we don't find them above as an active member
    +	// of the channel, just short-circuit and return an empty set. We don't return an error
    +	// as this is a valid result for some callers.
    +	if requestingUserID != "" && !slices.Contains(userIDs, requestingUserID) {
    +		return nil, nil
    +	}
    +
     	commonTeamIDs, err := a.Srv().Store().Team().GetCommonTeamIDsForMultipleUsers(userIDs)
     	if err != nil {
     		return nil, model.NewAppError("GetDirectOrGroupMessageMembersCommonTeams", "app.channel.get_common_teams.store_get_common_teams_error", nil, "", http.StatusInternalServerError).Wrap(err)
    
  • server/channels/app/channel_test.go+203 37 modified
    @@ -8,6 +8,7 @@ import (
     	"errors"
     	"fmt"
     	"net/http"
    +	"slices"
     	"sort"
     	"strings"
     	"sync"
    @@ -2829,57 +2830,222 @@ func TestGetDirectOrGroupMessageMembersCommonTeams(t *testing.T) {
     	mainHelper.Parallel(t)
     	th := Setup(t).InitBasic(t)
     
    -	teamsToCreate := 2
    -	usersToCreate := 4 // at least 3 users to create a GM channel, last user is not in any team
    -	teams := make([]string, 0, teamsToCreate)
    -	for i := 0; i < cap(teams); i++ {
    -		team := th.CreateTeam(t)
    -		defer func(team *model.Team) {
    -			appErr := th.App.PermanentDeleteTeam(th.Context, team)
    -			require.Nil(t, appErr)
    -		}(team)
    -		teams = append(teams, team.Id)
    +	team1 := th.CreateTeam(t)
    +	team2 := th.CreateTeam(t)
    +
    +	user1 := th.CreateUser(t)
    +	user2 := th.CreateUser(t)
    +	user3 := th.CreateUser(t)
    +	user4NotInAnyTeams := th.CreateUser(t)
    +	unrelatedUser := th.CreateUser(t)
    +
    +	// All of user1, user2 and user3, and unrelatedUser on team1
    +	th.LinkUserToTeam(t, user1, team1)
    +	th.LinkUserToTeam(t, user2, team1)
    +	th.LinkUserToTeam(t, user3, team1)
    +	th.LinkUserToTeam(t, unrelatedUser, team1)
    +
    +	// Only user2, user3, and unrelatedUser on team2
    +	th.LinkUserToTeam(t, user2, team2)
    +	th.LinkUserToTeam(t, user3, team2)
    +	th.LinkUserToTeam(t, unrelatedUser, team2)
    +
    +	assertNoTeamsInCommon := func(t *testing.T, commonTeams []*model.Team) {
    +		t.Helper()
    +		assert.Empty(t, commonTeams, "expected no teams in common")
     	}
     
    -	users := make([]string, 0, usersToCreate)
    -	for i := 0; i < cap(users); i++ {
    -		user := th.CreateUser(t)
    -		defer func(user *model.User) {
    -			appErr := th.App.PermanentDeleteUser(th.Context, user)
    -			require.Nil(t, appErr)
    -		}(user)
    -		users = append(users, user.Id)
    +	assertTeam1InCommon := func(t *testing.T, commonTeams []*model.Team) {
    +		t.Helper()
    +		if assert.Len(t, commonTeams, 1, "expected 1 team in common") {
    +			assert.Equal(t, team1.Id, commonTeams[0].Id, "expected team1 in common")
    +		}
     	}
     
    -	for _, teamId := range teams {
    -		// add first 3 users to each team, last user is not in any team
    -		for i := range 3 {
    -			_, _, appErr := th.App.AddUserToTeam(th.Context, teamId, users[i], "")
    -			require.Nil(t, appErr)
    +	assertTeam1And2InCommon := func(t *testing.T, commonTeams []*model.Team) {
    +		t.Helper()
    +		if assert.Len(t, commonTeams, 2, "expected 2 teams in common") {
    +			assert.True(t, slices.ContainsFunc(commonTeams, func(team *model.Team) bool {
    +				return team.Id == team1.Id
    +			}), "expected team1 in common")
    +			assert.True(t, slices.ContainsFunc(commonTeams, func(team *model.Team) bool {
    +				return team.Id == team2.Id
    +			}), "expected team2 in common")
     		}
     	}
     
    -	// create GM channel with first 3 users who share common teams
    -	gmChannel, appErr := th.App.createGroupChannel(th.Context, users[:3], users[0])
    -	require.Nil(t, appErr)
    -	require.NotNil(t, gmChannel)
    +	t.Run("teams for dm with user1 and user2", func(t *testing.T) {
    +		dmChannel, appErr := th.App.createDirectChannel(th.Context, user1.Id, user2.Id)
    +		require.Nil(t, appErr)
    +		require.NotNil(t, dmChannel)
     
    -	// normally you can't create a GM channel with users that don't share any teams, but we do it here to test the edge case
    -	// create GM channel with last 3 users, where last member is not in any team
    -	otherGMChannel, appErr := th.App.createGroupChannel(th.Context, users[1:], users[0])
    -	require.Nil(t, appErr)
    -	require.NotNil(t, otherGMChannel)
    +		commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeams(th.Context, dmChannel.Id)
    +		require.Nil(t, appErr)
    +		assertTeam1InCommon(t, commonTeams)
    +
    +		t.Run("as user1", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: user1.Id}), dmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertTeam1InCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as user2", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: user2.Id}), dmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertTeam1InCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as unrelatedUser", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: unrelatedUser.Id}), dmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertNoTeamsInCommon(t, commonTeams)
    +		})
    +	})
    +
    +	t.Run("teams for dm with user1 and deactivatedUser", func(t *testing.T) {
    +		deactivatedUser := th.CreateUser(t)
    +
    +		// deactiverUser on team1 only
    +		th.LinkUserToTeam(t, deactivatedUser, team1)
    +
    +		dmChannel, appErr := th.App.createDirectChannel(th.Context, user1.Id, deactivatedUser.Id)
    +		require.Nil(t, appErr)
    +		require.NotNil(t, dmChannel)
    +
    +		_, appErr = th.App.UpdateActive(th.Context, deactivatedUser, false)
    +		require.Nil(t, appErr)
    +
    +		commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeams(th.Context, dmChannel.Id)
    +		require.Nil(t, appErr)
    +		// By default, we return the teams common only to active users.
    +		assertTeam1InCommon(t, commonTeams)
    +
    +		t.Run("as user1", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: user1.Id}), dmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertTeam1InCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as deactivatedUser", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: deactivatedUser.Id}), dmChannel.Id)
    +			require.Nil(t, appErr)
    +
    +			// When requesting as deactivated user in the dm, no teams are considered in common.
    +			assertNoTeamsInCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as unrelatedUser", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: unrelatedUser.Id}), dmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertNoTeamsInCommon(t, commonTeams)
    +		})
    +	})
    +
    +	t.Run("teams for gm with user1, user2 and user3", func(t *testing.T) {
    +		gmChannel, appErr := th.App.createGroupChannel(th.Context, []string{user1.Id, user2.Id, user3.Id}, user1.Id)
    +		require.Nil(t, appErr)
    +		require.NotNil(t, gmChannel)
    +
    +		commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeams(th.Context, gmChannel.Id)
    +		require.Nil(t, appErr)
    +		assertTeam1InCommon(t, commonTeams)
    +
    +		t.Run("as user1", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: user1.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertTeam1InCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as user2", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: user2.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertTeam1InCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as user3", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: user3.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertTeam1InCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as unrelatedUser", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: unrelatedUser.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertNoTeamsInCommon(t, commonTeams)
    +		})
    +	})
    +
    +	t.Run("teams for gm with user2, user3, and user4NotInAnyTeams", func(t *testing.T) {
    +		gmChannel, appErr := th.App.createGroupChannel(th.Context, []string{user2.Id, user3.Id, user4NotInAnyTeams.Id}, user1.Id)
    +		require.Nil(t, appErr)
    +		require.NotNil(t, gmChannel)
     
    -	t.Run("Get teams for GM channel", func(t *testing.T) {
     		commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeams(th.Context, gmChannel.Id)
     		require.Nil(t, appErr)
    -		require.Equal(t, 2, len(commonTeams))
    +		assertNoTeamsInCommon(t, commonTeams)
    +
    +		t.Run("as user2", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: user2.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertNoTeamsInCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as user3", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: user3.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertNoTeamsInCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as unrelatedUser", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: unrelatedUser.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertNoTeamsInCommon(t, commonTeams)
    +		})
     	})
     
    -	t.Run("No common teams", func(t *testing.T) {
    -		commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeams(th.Context, otherGMChannel.Id)
    +	t.Run("teams for gm with user2, user3, and deactivatedUser", func(t *testing.T) {
    +		deactivatedUser := th.CreateUser(t)
    +
    +		// deactiverUser on team2 only
    +		th.LinkUserToTeam(t, deactivatedUser, team2)
    +
    +		gmChannel, appErr := th.App.createGroupChannel(th.Context, []string{user2.Id, user3.Id, deactivatedUser.Id}, user1.Id)
    +		require.Nil(t, appErr)
    +		require.NotNil(t, gmChannel)
    +
    +		_, appErr = th.App.UpdateActive(th.Context, deactivatedUser, false)
    +		require.Nil(t, appErr)
    +
    +		commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeams(th.Context, gmChannel.Id)
     		require.Nil(t, appErr)
    -		require.Equal(t, 0, len(commonTeams))
    +		// By default, we return the teams common only to active users.
    +		assertTeam1And2InCommon(t, commonTeams)
    +
    +		t.Run("as user2", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: user2.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertTeam1And2InCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as user3", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: user3.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertTeam1And2InCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as deactivatedUser", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: deactivatedUser.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +
    +			// When requesting as deactivated user in the gm, no teams are considered in common.
    +			assertNoTeamsInCommon(t, commonTeams)
    +		})
    +
    +		t.Run("as unrelatedUser", func(t *testing.T) {
    +			commonTeams, appErr := th.App.GetDirectOrGroupMessageMembersCommonTeamsAsUser(th.Context.WithSession(&model.Session{UserId: unrelatedUser.Id}), gmChannel.Id)
    +			require.Nil(t, appErr)
    +			assertNoTeamsInCommon(t, commonTeams)
    +		})
     	})
     }
     
    
  • server/public/model/client4.go+10 0 modified
    @@ -2886,6 +2886,16 @@ func (c *Client4) MoveChannel(ctx context.Context, channelId, teamId string, for
     	return DecodeJSONFromResponse[*Channel](r)
     }
     
    +// GetDirectOrGroupMessageMembersCommonTeams returns the set of teams in common for members of a DM/GM channel.
    +func (c *Client4) GetDirectOrGroupMessageMembersCommonTeams(ctx context.Context, channelId string) ([]*Team, *Response, error) {
    +	r, err := c.DoAPIGet(ctx, c.channelRoute(channelId)+"/common_teams", "")
    +	if err != nil {
    +		return nil, BuildResponse(r), err
    +	}
    +	defer closeBody(r)
    +	return DecodeJSONFromResponse[[]*Team](r)
    +}
    +
     // GetChannelByName returns a channel based on the provided channel name and team id strings.
     func (c *Client4) GetChannelByName(ctx context.Context, channelName, teamId string, etag string) (*Channel, *Response, error) {
     	r, err := c.DoAPIGet(ctx, c.channelByNameRoute(channelName, teamId), etag)
    
  • server/public/model/example_channel_test.go+16 0 modified
    @@ -475,3 +475,19 @@ func ExampleClient4_UpdateChannelScheme() {
     		log.Fatal(err)
     	}
     }
    +
    +func ExampleClient4_GetDirectOrGroupMessageMembersCommonTeams() {
    +	client := model.NewAPIv4Client(os.Getenv("MM_SERVICESETTINGS_SITEURL"))
    +	client.SetToken(os.Getenv("MM_AUTHTOKEN"))
    +
    +	channelID := "dm_or_gm_channel_id"
    +	teams, _, err := client.GetDirectOrGroupMessageMembersCommonTeams(context.Background(), channelID)
    +	if err != nil {
    +		log.Fatal(err)
    +	}
    +
    +	fmt.Printf("Found %d common teams for members of channel %s\n", len(teams), channelID)
    +	for _, team := range teams {
    +		fmt.Printf("  - %s (%s)\n", team.DisplayName, team.Name)
    +	}
    +}
    

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

News mentions

0

No linked articles in our index yet.