VYPR
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.

PackageAffected versionsPatched versions
github.com/mattermost/mattermost/server/v8Go
< 8.0.0-20260127062706-c6b205f0d7708.0.0-20260127062706-c6b205f0d770
github.com/mattermost/mattermost-serverGo
< 5.3.2-0.20260127062706-c6b205f0d7705.3.2-0.20260127062706-c6b205f0d770
github.com/mattermost/mattermost-serverGo
>= 10.11.0-rc1, < 10.11.1110.11.11
github.com/mattermost/mattermost-serverGo
>= 11.2.0-rc1, < 11.2.311.2.3
github.com/mattermost/mattermost-serverGo
>= 11.3.0-rc1, < 11.3.111.3.1

Affected products

1

Patches

1
c6b205f0d770

Fixed WS payload for post burn event (#34936)

https://github.com/mattermost/mattermostHarshil SharmaJan 27, 2026via ghsa
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

News mentions

0

No linked articles in our index yet.