Moderate severityNVD Advisory· Published Feb 24, 2025· Updated Feb 24, 2025
Channel export permitted on archived channel when viewing archived channels is disabled
CVE-2025-24526
Description
Mattermost versions 10.1.x <= 10.1.3, 10.4.x <= 10.4.1, 9.11.x <= 9.11.7, 10.3.x <= 10.3.2, 10.2.x <= 10.2.2 fail to restrict channel export of archived channels when the "Allow users to view archived channels" is disabled which allows a user to export channel contents when they shouldn't have access to it
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/mattermost/mattermost/server/v8Go | < 8.0.0-20250110161910-96195f1bd746 | 8.0.0-20250110161910-96195f1bd746 |
github.com/mattermost/mattermost/server/v8Go | >= 9.11.0-rc1, < 9.11.8 | 9.11.8 |
github.com/mattermost/mattermost/server/v8Go | >= 10.2.0-rc1, < 10.2.3 | 10.2.3 |
github.com/mattermost/mattermost/server/v8Go | >= 10.3.0-rc1, < 10.3.3 | 10.3.3 |
github.com/mattermost/mattermost/server/v8Go | >= 10.4.0-rc1, < 10.4.2 | 10.4.2 |
Affected products
1- Range: 10.1.0
Patches
296195f1bd746Prepackage Channel Export plugin v1.2.1 (#29638)
1 file changed · +1 −0
server/Makefile+1 −0 modified@@ -161,6 +161,7 @@ PLUGIN_PACKAGES += mattermost-plugin-user-survey-v1.1.1 PLUGIN_PACKAGES += mattermost-plugin-mscalendar-v1.3.4 PLUGIN_PACKAGES += mattermost-plugin-msteams-meetings-v2.2.0 PLUGIN_PACKAGES += mattermost-plugin-metrics-v0.5.3 +PLUGIN_PACKAGES += mattermost-plugin-channel-export-v1.2.1 EE_PACKAGES=$(shell $(GO) list $(BUILD_ENTERPRISE_DIR)/...)
3c052b66207f[GH-51] Added conditional to disallow export in archived channel when it is not visible to users (#55)
2 files changed · +132 −0
server/api.go+10 −0 modified@@ -143,6 +143,16 @@ func (h *Handler) Export(w http.ResponseWriter, r *http.Request) { return } + areArchivedChannelsVisible := true + if h.client.Configuration.GetConfig().TeamSettings.ExperimentalViewArchivedChannels == nil || !*h.client.Configuration.GetConfig().TeamSettings.ExperimentalViewArchivedChannels { + areArchivedChannelsVisible = false + } + + if channel.DeleteAt > 0 && !areArchivedChannelsVisible { + handleError(w, http.StatusNotFound, "channel '%s' is archived and not visible anymore", channelID) + return + } + if !h.plugin.hasPermissionToExportChannel(userID, channelID) { handleError(w, http.StatusForbidden, "user does not have permission to export channels") return
server/api_test.go+122 −0 modified@@ -2,6 +2,7 @@ package main import ( "bytes" + "fmt" "io" "io/ioutil" "net/http" @@ -138,6 +139,11 @@ func TestHandler(t *testing.T) { mockChannel.EXPECT().Get(channelID).Return(&model.Channel{Id: channelID}, nil).Times(1) mockUser.EXPECT().HasPermissionToChannel(userID, channelID, model.PermissionReadChannel).Return(true).Times(1) mockUser.EXPECT().HasPermissionTo(userID, model.PermissionManageSystem).Return(false).Times(1) + mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ + TeamSettings: model.TeamSettings{ + ExperimentalViewArchivedChannels: &trueValue, + }, + }).Times(2) mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ PrivacySettings: model.PrivacySettings{ ShowEmailAddress: &trueValue, @@ -367,6 +373,11 @@ func TestHandler(t *testing.T) { mockChannel.EXPECT().Get(channelID).Return(&model.Channel{Id: channelID}, nil).Times(1) mockUser.EXPECT().HasPermissionToChannel(userID, channelID, model.PermissionReadChannel).Return(true).Times(1) mockUser.EXPECT().HasPermissionTo(userID, model.PermissionManageSystem).Return(false).Times(1) + mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ + TeamSettings: model.TeamSettings{ + ExperimentalViewArchivedChannels: &trueValue, + }, + }).Times(2) mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ PrivacySettings: model.PrivacySettings{ ShowEmailAddress: &falseValue, @@ -414,6 +425,11 @@ func TestHandler(t *testing.T) { mockChannel.EXPECT().Get(channelID).Return(&model.Channel{Id: channelID}, nil).Times(1) mockUser.EXPECT().HasPermissionToChannel(userID, channelID, model.PermissionReadChannel).Return(true).Times(1) mockUser.EXPECT().HasPermissionTo(userID, model.PermissionManageSystem).Return(false).Times(1) + mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ + TeamSettings: model.TeamSettings{ + ExperimentalViewArchivedChannels: &trueValue, + }, + }).Times(2) mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ PrivacySettings: model.PrivacySettings{ ShowEmailAddress: &trueValue, @@ -464,6 +480,11 @@ func TestHandler(t *testing.T) { mockChannel.EXPECT().Get(channelID).Return(&model.Channel{Id: channelID}, nil).Times(1) mockUser.EXPECT().HasPermissionToChannel(userID, channelID, model.PermissionReadChannel).Return(true).Times(1) mockUser.EXPECT().HasPermissionTo(userID, model.PermissionManageSystem).Return(false).Times(1) + mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ + TeamSettings: model.TeamSettings{ + ExperimentalViewArchivedChannels: &trueValue, + }, + }).Times(2) mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ PrivacySettings: model.PrivacySettings{ ShowEmailAddress: &trueValue, @@ -497,4 +518,105 @@ func TestHandler(t *testing.T) { require.Equal(t, "a channel export is already running.", err.Error()) } }) + + t.Run("export when channel is archived and not visible", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + + mockChannel := mock_pluginapi.NewMockChannel(mockCtrl) + mockFile := mock_pluginapi.NewMockFile(mockCtrl) + mockLog := mock_pluginapi.NewMockLog(mockCtrl) + mockPost := mock_pluginapi.NewMockPost(mockCtrl) + mockSlashCommand := mock_pluginapi.NewMockSlashCommand(mockCtrl) + mockUser := mock_pluginapi.NewMockUser(mockCtrl) + mockSystem := mock_pluginapi.NewMockSystem(mockCtrl) + mockConfiguration := mock_pluginapi.NewMockConfiguration(mockCtrl) + mockCluster := mock_pluginapi.NewMockCluster(mockCtrl) + mockCluster.EXPECT().NewMutex(gomock.Eq(KeyClusterMutex)).Return(pluginapi.NewClusterMutexMock(), nil) + + mockAPI := pluginapi.CustomWrapper(mockChannel, mockFile, mockLog, mockPost, mockSlashCommand, mockUser, mockSystem, mockConfiguration, mockCluster) + + now := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.FixedZone("UTC-8", -8*60*60)) + userID := "user_id" + channelID := "channel_id" + address := setupAPI(t, mockAPI, now, userID, channelID) + client := NewClient(address) + client.SetToken("token") + + mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{ + FutureFeatures: &trueValue, + }}).Times(2) + mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1) + mockChannel.EXPECT().Get(channelID).Return(&model.Channel{Id: channelID, DeleteAt: 1}, nil).Times(1) + mockUser.EXPECT().HasPermissionToChannel(userID, channelID, model.PermissionReadChannel).Return(true).Times(1) + mockUser.EXPECT().HasPermissionTo(userID, model.PermissionManageSystem).Return(false).Times(1) + mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ + TeamSettings: model.TeamSettings{ + ExperimentalViewArchivedChannels: &falseValue, + }, + }).Times(2) + mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ + PrivacySettings: model.PrivacySettings{ + ShowEmailAddress: &trueValue, + }, + }) + + var buffer bytes.Buffer + err := client.ExportChannel(&buffer, channelID, FormatCSV) + require.EqualValues(t, "", buffer.String()) + + expectedErr := fmt.Sprintf("channel '%s' is archived and not visible anymore", channelID) + + require.EqualError(t, err, expectedErr) + }) + + t.Run("export when channel is archived and visible", func(t *testing.T) { + mockCtrl := gomock.NewController(t) + + mockChannel := mock_pluginapi.NewMockChannel(mockCtrl) + mockFile := mock_pluginapi.NewMockFile(mockCtrl) + mockLog := mock_pluginapi.NewMockLog(mockCtrl) + mockPost := mock_pluginapi.NewMockPost(mockCtrl) + mockSlashCommand := mock_pluginapi.NewMockSlashCommand(mockCtrl) + mockUser := mock_pluginapi.NewMockUser(mockCtrl) + mockSystem := mock_pluginapi.NewMockSystem(mockCtrl) + mockConfiguration := mock_pluginapi.NewMockConfiguration(mockCtrl) + mockCluster := mock_pluginapi.NewMockCluster(mockCtrl) + mockCluster.EXPECT().NewMutex(gomock.Eq(KeyClusterMutex)).Return(pluginapi.NewClusterMutexMock(), nil) + + mockAPI := pluginapi.CustomWrapper(mockChannel, mockFile, mockLog, mockPost, mockSlashCommand, mockUser, mockSystem, mockConfiguration, mockCluster) + + now := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.FixedZone("UTC-8", -8*60*60)) + userID := "user_id" + channelID := "channel_id" + address := setupAPI(t, mockAPI, now, userID, channelID) + client := NewClient(address) + client.SetToken("token") + + mockSystem.EXPECT().GetLicense().Return(&model.License{Features: &model.Features{ + FutureFeatures: &trueValue, + }}).Times(2) + mockConfiguration.EXPECT().GetConfig().Return(&model.Config{}).Times(1) + mockChannel.EXPECT().Get(channelID).Return(&model.Channel{Id: channelID, DeleteAt: 1}, nil).Times(1) + mockUser.EXPECT().HasPermissionToChannel(userID, channelID, model.PermissionReadChannel).Return(true).Times(1) + mockUser.EXPECT().HasPermissionTo(userID, model.PermissionManageSystem).Return(false).Times(1) + mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ + TeamSettings: model.TeamSettings{ + ExperimentalViewArchivedChannels: &trueValue, + }, + }).Times(2) + mockConfiguration.EXPECT().GetConfig().Return(&model.Config{ + PrivacySettings: model.PrivacySettings{ + ShowEmailAddress: &trueValue, + }, + }) + + var buffer bytes.Buffer + err := client.ExportChannel(&buffer, channelID, FormatCSV) + require.Nil(t, err) + + expected := `Post Creation Time,User Id,User Email,User Type,User Name,Post Id,Parent Post Id,Post Message,Post Type +2009-11-11 07:00:00 +0000 UTC,post_user_id,post_user_email,user,post_user_nickname,post_id,post_parent_id,post_message,message +` + require.EqualValues(t, expected, buffer.String()) + }) }
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
6- github.com/advisories/GHSA-q8p2-2hwc-jw64ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-24526ghsaADVISORY
- github.com/mattermost/mattermost-plugin-channel-export/commit/3c052b66207fb734bfc4c948941e7f7522a82550ghsaWEB
- github.com/mattermost/mattermost-plugin-channel-export/issues/51ghsaWEB
- github.com/mattermost/mattermost/commit/96195f1bd7467f572525c35b5087acaeb53daa63ghsaWEB
- mattermost.com/security-updatesghsaWEB
News mentions
0No linked articles in our index yet.