Moderate severityNVD Advisory· Published Mar 16, 2026· Updated Mar 16, 2026
Information Disclosure via WebSocket Event When Deleting Unrevealed Burn on Read Posts
CVE-2026-2578
Description
Mattermost versions 11.3.x <= 11.3.0 fail to preserve the redacted state of burn-on-read posts during deletion which allows channel members to access unrevealed burn-on-read message contents via the WebSocket post deletion event.. Mattermost Advisory ID: MMSA-2026-00579
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/mattermost/mattermost/server/v8Go | < 8.0.0-20260127062706-c6b205f0d770 | 8.0.0-20260127062706-c6b205f0d770 |
github.com/mattermost/mattermost-serverGo | < 5.3.2-0.20260127062706-c6b205f0d770 | 5.3.2-0.20260127062706-c6b205f0d770 |
github.com/mattermost/mattermost-serverGo | >= 10.11.0-rc1, < 10.11.11 | 10.11.11 |
github.com/mattermost/mattermost-serverGo | >= 11.2.0-rc1, < 11.2.3 | 11.2.3 |
github.com/mattermost/mattermost-serverGo | >= 11.3.0-rc1, < 11.3.1 | 11.3.1 |
Affected products
1- Range: 11.3.0
Patches
1c6b205f0d770Fixed WS payload for post burn event (#34936)
2 files changed · +57 −4
server/channels/app/post.go+6 −4 modified@@ -2956,18 +2956,20 @@ func (a *App) PermanentDeletePost(rctx request.CTX, postID, deleteByID string) * return model.NewAppError("DeletePost", "app.post.get.app_error", nil, "", http.StatusBadRequest).Wrap(err) } + postHasFiles := len(post.FileIds) > 0 + // If the post is a burn-on-read post, we should get the original post contents if post.Type == model.PostTypeBurnOnRead { - tmpPost, appErr := a.getBurnOnReadPost(rctx, post) + revealedPost, appErr := a.getBurnOnReadPost(rctx, post) if appErr != nil { rctx.Logger().Warn("Failed to get burn-on-read post", mlog.Err(appErr)) } - if tmpPost != nil { - post = tmpPost + if revealedPost != nil { + postHasFiles = len(revealedPost.FileIds) > 0 } } - if len(post.FileIds) > 0 { + if postHasFiles { appErr := a.PermanentDeleteFilesByPost(rctx, post.Id) if appErr != nil { return appErr
server/channels/app/post_test.go+51 −0 modified@@ -4,6 +4,7 @@ package app import ( + "encoding/json" "errors" "fmt" "net/http" @@ -4475,6 +4476,56 @@ func TestPermanentDeletePost(t *testing.T) { assert.Error(t, tmpErr) assert.True(t, store.IsErrNotFound(tmpErr)) }) + + t.Run("should send unrevealed post in websocket broadcast", func(t *testing.T) { + // Enable feature with license + th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced)) + th.App.UpdateConfig(func(cfg *model.Config) { + cfg.ServiceSettings.EnableBurnOnRead = model.NewPointer(true) + }) + + // Create a burn-on-read post + //teamID := th.BasicTeam.Id + channelID := th.BasicChannel.Id + userID := th.BasicUser.Id + + wsMessages, closeWS := connectFakeWebSocket(t, th, userID, "", []model.WebsocketEventType{model.WebsocketEventPostDeleted}) + defer closeWS() + + post := &model.Post{ + Message: "burn on read message with file", + ChannelId: channelID, + PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), + UserId: userID, + CreateAt: 0, + Type: model.PostTypeBurnOnRead, + } + post.AddProp(model.PostPropsExpireAt, model.GetMillis()+int64(model.DefaultExpirySeconds*1000)) + + post, _, appErr := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) + require.Nil(t, appErr) + require.Equal(t, model.PostTypeBurnOnRead, post.Type) + + appErr = th.App.PermanentDeletePost(th.Context, post.Id, userID) + require.Nil(t, appErr) + + var received *model.WebSocketEvent + select { + case received = <-wsMessages: + // the post sent in websocket payload shouldn't contain message or file IDs + data := received.GetData() + postJSON, ok := data["post"].(string) + require.True(t, ok) + var receivedPost model.Post + err := json.Unmarshal([]byte(postJSON), &receivedPost) + require.NoError(t, err) + require.Equal(t, post.Id, receivedPost.Id) + require.Equal(t, "", receivedPost.Message) + require.Equal(t, 0, len(receivedPost.FileIds)) + case <-time.After(10 * time.Second): + require.Fail(t, "Did not receive websocket message in time") + } + }) } func TestSendTestMessage(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
4- github.com/advisories/GHSA-3rhr-jr63-hwq5ghsaADVISORY
- mattermost.com/security-updatesghsavendor-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-2578ghsaADVISORY
- github.com/mattermost/mattermost/commit/c6b205f0d77080ef805783de0628b9526af7faecghsaWEB
News mentions
0No linked articles in our index yet.