Moderate severityNVD Advisory· Published Mar 16, 2026· Updated Mar 16, 2026
WebSocket Message Spoofing via Permalink Embed Manipulation
CVE-2026-2457
Description
Mattermost versions 11.3.x <= 11.3.0, 11.2.x <= 11.2.2, 10.11.x <= 10.11.10 fail to sanitize client-supplied post metadata which allows an authenticated attacker to spoof permalink embeds impersonating other users via crafted PUT requests to the post update API endpoint.. Mattermost Advisory ID: MMSA-2025-00569
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/mattermost/mattermost/server/v8Go | < 8.0.0-20260123211116-9efe617be8b8 | 8.0.0-20260123211116-9efe617be8b8 |
github.com/mattermost/mattermost-serverGo | < 5.3.2-0.20260123211116-9efe617be8b8 | 5.3.2-0.20260123211116-9efe617be8b8 |
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
19efe617be8b8MM-67055: Fix permalink embeds in WebSocket messages (#34893)
4 files changed · +107 −0
server/channels/api4/post.go+4 −0 modified@@ -1010,6 +1010,10 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { return } + // MM-67055: Strip client-supplied metadata.embeds to prevent spoofing. + // This matches createPost behavior. + post.SanitizeInput() + auditRec := c.MakeAuditRecord(model.AuditEventUpdatePost, model.AuditStatusFail) model.AddEventParameterAuditableToAuditRec(auditRec, "post", &post) defer c.LogAuditRecWithLevel(auditRec, app.LevelContent)
server/channels/api4/post_test.go+47 −0 modified@@ -1552,6 +1552,53 @@ func TestUpdatePost(t *testing.T) { assert.NotEqual(t, rpost3.Attachments(), rrupost3.Attachments()) }) + t.Run("should strip spoofed metadata embeds", func(t *testing.T) { + // MM-67055: Verify that client-supplied metadata.embeds are stripped + post := &model.Post{ + ChannelId: channel.Id, + Message: "test message " + model.NewId(), + } + createdPost, _, err := client.CreatePost(context.Background(), post) + require.NoError(t, err) + + // Try to update with spoofed embed + updatePost := &model.Post{ + Id: createdPost.Id, + ChannelId: channel.Id, + Message: "updated message " + model.NewId(), + Metadata: &model.PostMetadata{ + Embeds: []*model.PostEmbed{ + { + Type: model.PostEmbedPermalink, + Data: &model.PreviewPost{ + PostID: "spoofed-post-id", + Post: &model.Post{ + Id: "spoofed-post-id", + UserId: th.BasicUser2.Id, + Message: "This is a spoofed message!", + }, + }, + }, + }, + }, + } + + updatedPost, _, err := client.UpdatePost(context.Background(), createdPost.Id, updatePost) + require.NoError(t, err) + + // Verify spoofed embed was stripped + if updatedPost.Metadata != nil { + assert.Empty(t, updatedPost.Metadata.Embeds, "spoofed embeds should be stripped") + } + + // Double-check by fetching the post + fetchedPost, _, err := client.GetPost(context.Background(), createdPost.Id, "") + require.NoError(t, err) + if fetchedPost.Metadata != nil { + assert.Empty(t, fetchedPost.Metadata.Embeds, "spoofed embeds should not be persisted") + } + }) + t.Run("change message, but post too old", func(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.PostEditTimeLimit = 1
server/channels/app/post.go+2 −0 modified@@ -849,6 +849,8 @@ func (a *App) UpdatePost(rctx request.CTX, receivedUpdatedPost *model.Post, upda // Always use incoming metadata when provided, otherwise retain existing if receivedUpdatedPost.Metadata != nil { newPost.Metadata = receivedUpdatedPost.Metadata.Copy() + // MM-67055: Strip embeds - always server-generated. Preserves Priority/Acks for Shared Channels sync. + newPost.Metadata.Embeds = nil } else { // Restore the post metadata that was stripped by the plugin. Set it to // the last known good.
server/channels/app/post_test.go+54 −0 modified@@ -1952,6 +1952,60 @@ func TestUpdatePost(t *testing.T) { } }) + t.Run("should strip client-supplied embeds", func(t *testing.T) { + // MM-67055: Verify that client-supplied metadata.embeds are stripped. + // This prevents WebSocket message spoofing via permalink embeds. + // + // Note: Priority and Acknowledgements are stored in separate database tables, + // not in post metadata. Shared Channels handles them separately via + // syncRemotePriorityMetadata and syncRemoteAcknowledgementsMetadata after + // calling UpdatePost. See sync_recv.go::upsertSyncPost + mainHelper.Parallel(t) + th := Setup(t).InitBasic(t) + + th.AddUserToChannel(t, th.BasicUser, th.BasicChannel) + th.Context.Session().UserId = th.BasicUser.Id + + // Create a basic post + post := &model.Post{ + ChannelId: th.BasicChannel.Id, + Message: "original message", + UserId: th.BasicUser.Id, + } + createdPost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{}) + require.Nil(t, err) + + // Try to update with spoofed embeds (the attack vector) + updatePost := &model.Post{ + Id: createdPost.Id, + ChannelId: th.BasicChannel.Id, + Message: "updated message", + UserId: th.BasicUser.Id, + Metadata: &model.PostMetadata{ + Embeds: []*model.PostEmbed{ + { + Type: model.PostEmbedPermalink, + Data: &model.PreviewPost{ + PostID: "spoofed-post-id", + Post: &model.Post{ + Id: "spoofed-post-id", + UserId: th.BasicUser2.Id, + Message: "Spoofed message from another user!", + }, + }, + }, + }, + }, + } + + updatedPost, err := th.App.UpdatePost(th.Context, updatePost, nil) + require.Nil(t, err) + require.NotNil(t, updatedPost.Metadata) + + // Verify embeds were stripped + assert.Empty(t, updatedPost.Metadata.Embeds, "spoofed embeds should be stripped") + }) + t.Run("cannot update post in restricted DM", func(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic(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-ph22-fw5m-w2q9ghsaADVISORY
- mattermost.com/security-updatesghsavendor-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-2457ghsaADVISORY
- github.com/mattermost/mattermost/commit/9efe617be8b8f1d036e12721e8e73b69a543ed34ghsaWEB
News mentions
0No linked articles in our index yet.