VYPR
Moderate severityNVD Advisory· Published Mar 16, 2026· Updated Mar 16, 2026

Denial of Service via Unbounded Memory Allocation in Integration Actions

CVE-2026-2456

Description

Mattermost versions 11.3.x <= 11.3.0, 11.2.x <= 11.2.2, 10.11.x <= 10.11.10 Mattermost fails to limit the size of responses from integration action endpoints, which allows an authenticated attacker to cause server memory exhaustion and denial of service via a malicious integration server that returns an arbitrarily large response when a user clicks an interactive message button.. Mattermost Advisory ID: MMSA-2026-00571

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/mattermost/mattermost/server/v8Go
< 8.0.0-20260127165411-fe3052073dc68.0.0-20260127165411-fe3052073dc6
github.com/mattermost/mattermost-serverGo
< 5.3.2-0.20260127165411-fe3052073dc65.3.2-0.20260127165411-fe3052073dc6
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
fe3052073dc6

[MM-67074] Integration Action memory use fix (#34896)

https://github.com/mattermost/mattermostChristopher PoileJan 27, 2026via ghsa
2 files changed · +115 2
  • server/channels/app/integration_action.go+2 1 modified
    @@ -252,7 +252,8 @@ func (a *App) DoPostActionWithCookie(rctx request.CTX, postID, actionId, userID,
     	defer resp.Body.Close()
     
     	var response model.PostActionIntegrationResponse
    -	respBytes, err := io.ReadAll(resp.Body)
    +	limitedReader := io.LimitReader(resp.Body, MaxIntegrationResponseSize)
    +	respBytes, err := io.ReadAll(limitedReader)
     	if err != nil {
     		return "", model.NewAppError("DoPostActionWithCookie", "api.post.do_action.action_integration.app_error", nil, "", http.StatusBadRequest).Wrap(err)
     	}
    
  • server/channels/app/integration_action_test.go+113 1 modified
    @@ -173,6 +173,118 @@ func TestPostActionEmptyResponse(t *testing.T) {
     	})
     }
     
    +// infiniteReader generates unlimited data for testing response size limits
    +type infiniteReader struct{}
    +
    +func (r infiniteReader) Read(p []byte) (n int, err error) {
    +	for i := range p {
    +		p[i] = 'a'
    +	}
    +	return len(p), nil
    +}
    +
    +// MM-67074: TestPostActionResponseSizeLimit verifies that DoPostActionWithCookie
    +// properly limits response sizes to prevent OOM attacks
    +func TestPostActionResponseSizeLimit(t *testing.T) {
    +	mainHelper.Parallel(t)
    +	th := Setup(t).InitBasic(t)
    +
    +	channel := th.BasicChannel
    +	th.App.UpdateConfig(func(cfg *model.Config) {
    +		*cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost,127.0.0.1"
    +	})
    +
    +	t.Run("large valid JSON response is truncated", func(t *testing.T) {
    +		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    +			// Send response larger than MaxIntegrationResponseSize (1MB)
    +			// Response starts as valid JSON but becomes truncated
    +			_, _ = io.Copy(w, io.MultiReader(
    +				strings.NewReader(`{"update":{"message":"`),
    +				infiniteReader{},
    +				strings.NewReader(`"}}`),
    +			))
    +		}))
    +		defer server.Close()
    +
    +		interactivePost := model.Post{
    +			Message:       "Interactive post",
    +			ChannelId:     channel.Id,
    +			PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
    +			UserId:        th.BasicUser.Id,
    +			Props: model.StringInterface{
    +				model.PostPropsAttachments: []*model.SlackAttachment{
    +					{
    +						Text: "hello",
    +						Actions: []*model.PostAction{
    +							{
    +								Type: model.PostActionTypeButton,
    +								Name: "action",
    +								Integration: &model.PostActionIntegration{
    +									URL: server.URL,
    +								},
    +							},
    +						},
    +					},
    +				},
    +			},
    +		}
    +
    +		post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true)
    +		require.Nil(t, err)
    +		attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment)
    +		require.True(t, ok)
    +
    +		// Should return error due to truncated JSON, but NOT crash or OOM
    +		_, err = th.App.DoPostActionWithCookie(th.Context, post.Id,
    +			attachments[0].Actions[0].Id, th.BasicUser.Id, "", nil)
    +		require.NotNil(t, err)
    +		// Truncated JSON causes unmarshal error
    +		assert.Equal(t, "api.post.do_action.action_integration.app_error", err.Id)
    +	})
    +
    +	t.Run("large invalid response is truncated", func(t *testing.T) {
    +		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    +			// Send infinite non-JSON data
    +			_, _ = io.Copy(w, infiniteReader{})
    +		}))
    +		defer server.Close()
    +
    +		interactivePost := model.Post{
    +			Message:       "Interactive post",
    +			ChannelId:     channel.Id,
    +			PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
    +			UserId:        th.BasicUser.Id,
    +			Props: model.StringInterface{
    +				model.PostPropsAttachments: []*model.SlackAttachment{
    +					{
    +						Text: "hello",
    +						Actions: []*model.PostAction{
    +							{
    +								Type: model.PostActionTypeButton,
    +								Name: "action",
    +								Integration: &model.PostActionIntegration{
    +									URL: server.URL,
    +								},
    +							},
    +						},
    +					},
    +				},
    +			},
    +		}
    +
    +		post, err := th.App.CreatePostAsUser(th.Context, &interactivePost, "", true)
    +		require.Nil(t, err)
    +		attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.SlackAttachment)
    +		require.True(t, ok)
    +
    +		// Should return error due to invalid JSON, but NOT crash or OOM
    +		_, err = th.App.DoPostActionWithCookie(th.Context, post.Id,
    +			attachments[0].Actions[0].Id, th.BasicUser.Id, "", nil)
    +		require.NotNil(t, err)
    +		assert.Equal(t, "api.post.do_action.action_integration.app_error", err.Id)
    +	})
    +}
    +
     func TestPostAction(t *testing.T) {
     	mainHelper.Parallel(t)
     	testCases := []struct {
    @@ -1236,7 +1348,7 @@ func TestLookupInteractiveDialog(t *testing.T) {
     			func (p *MyPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) {
     				var request model.SubmitDialogRequest
     				json.NewDecoder(r.Body).Decode(&request)
    -				
    +
     				response := &model.LookupDialogResponse{
     					Items: []model.DialogSelectOption{
     						{Text: "Plugin Option 1", Value: "plugin_value1"},
    

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.