diff --git a/server/channels/api4/data_retention.go b/server/channels/api4/data_retention.go index 4ffd8b1e60a..a95f20b297e 100644 --- a/server/channels/api4/data_retention.go +++ b/server/channels/api4/data_retention.go @@ -229,6 +229,7 @@ func getTeamsForPolicy(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = appErr return } + c.App.SanitizeTeams(*c.AppContext.Session(), teams.Teams) b, err := json.Marshal(teams) if err != nil { diff --git a/server/channels/api4/data_retention_test.go b/server/channels/api4/data_retention_test.go index c22cac200b2..83fc850757d 100644 --- a/server/channels/api4/data_retention_test.go +++ b/server/channels/api4/data_retention_test.go @@ -709,12 +709,17 @@ func TestGetTeamsForPolicy(t *testing.T) { // Create and set up the mock mockDataRetentionInterface := &mocks.DataRetentionInterface{} - // Set up the mock to return sample teams + // Set up the mock to return sample teams. team1 carries a secret InviteId and Email, + // mirroring an invite-only private team, to verify the endpoint sanitizes them for + // callers who lack team-scoped permissions on that team (MM-69394). sampleTeams := &model.TeamsWithCount{ Teams: []*model.Team{ { - Id: model.NewId(), - Name: "team1", + Id: model.NewId(), + Name: "team1", + Type: model.TeamInvite, + InviteId: model.NewId(), + Email: "team1-secret@example.com", }, { Id: model.NewId(), @@ -770,6 +775,23 @@ func TestGetTeamsForPolicy(t *testing.T) { assert.Nil(t, teams, "Teams should be nil when user has no permission") }) + t.Run("SanitizesInviteIDAndEmailForUserWithoutTeamAccess", func(t *testing.T) { + // A user holding ONLY the read-only Data Retention Policy permission, who is not a + // member of the team and holds no team-scoped permissions on it, must not be able to + // read the team's secret invite_id or email via this endpoint (MM-69394). + th.AddPermissionToRole(t, model.PermissionSysconsoleReadComplianceDataRetentionPolicy.Id, model.SystemUserRoleId) + defer th.RemovePermissionFromRole(t, model.PermissionSysconsoleReadComplianceDataRetentionPolicy.Id, model.SystemUserRoleId) + + teams, resp, err := th.Client.GetTeamsForRetentionPolicy(context.Background(), validPolicyId, 0, 100) + require.NoError(t, err) + CheckOKStatus(t, resp) + require.NotNil(t, teams, "Teams should not be nil") + require.Len(t, teams.Teams, 2, "Should return 2 teams") + assert.Equal(t, "team1", teams.Teams[0].Name, "Non-secret team fields should still be returned") + assert.Empty(t, teams.Teams[0].InviteId, "InviteId must be sanitized for a caller without team access") + assert.Empty(t, teams.Teams[0].Email, "Email must be sanitized for a caller without team access") + }) + t.Run("NotLoggedIn", func(t *testing.T) { resp, err := th.Client.Logout(context.Background()) require.NoError(t, err)