CVE-2026-7184
Description
Mattermost fails to sanitize Remote Cluster API responses, allowing authenticated attackers with specific permissions to obtain remote cluster tokens.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Mattermost fails to sanitize Remote Cluster API responses, allowing authenticated attackers with specific permissions to obtain remote cluster tokens.
Vulnerability
Mattermost versions 11.6.x up to 11.6.1, 11.5.x up to 11.5.4, and 10.11.x up to 10.11.15 do not properly sanitize the Remote Cluster API response during PATCH operations. This vulnerability allows an authenticated user with the manage_secure_connections permission to obtain remote cluster authentication tokens by sending a crafted PATCH request to the remote cluster endpoint [1].
Exploitation
An attacker must have an authenticated account on the Mattermost server and possess the manage_secure_connections permission. The attacker sends a PATCH request to the remote cluster endpoint; due to insufficient sanitization, the response includes authentication tokens for remote clusters, which the attacker can capture.
Impact
Successful exploitation results in disclosure of remote cluster authentication tokens. This could allow the attacker to access or manipulate the remote cluster, potentially leading to further compromise.
Mitigation
Mattermost has released security updates for the affected versions. Users should upgrade to a fixed version as indicated in the Mattermost security advisory [1]. As of the publication date, no workaround is disclosed.
AI Insight generated on Jun 12, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2>=11.6.0,<=11.6.1 OR >=11.5.0,<=11.5.4 OR >=10.11.0,<=10.11.15+ 1 more
- (no CPE)range: >=11.6.0,<=11.6.1 OR >=11.5.0,<=11.5.4 OR >=10.11.0,<=10.11.15
- (no CPE)range: 11.6.x <= 11.6.1, 11.5.x <= 11.5.4, 10.11.x <= 10.11.15
Patches
31a0643df00c1MM-68526: Harden remote cluster patch response (#36288) (#36311)
2 files changed · +10 −3
server/channels/api4/remote_cluster.go+2 −0 modified@@ -660,6 +660,8 @@ func patchRemoteCluster(c *Context, w http.ResponseWriter, r *http.Request) { return } + updatedRC.Sanitize() + auditRec.Success() auditRec.AddEventResultState(updatedRC)
server/channels/api4/remote_cluster_test.go+8 −3 modified@@ -479,9 +479,10 @@ func TestGenerateRemoteClusterInvite(t *testing.T) { func TestGetRemoteCluster(t *testing.T) { mainHelper.Parallel(t) newRC := &model.RemoteCluster{ - Name: "remotecluster", - SiteURL: "http://example.com", - Token: model.NewId(), + Name: "remotecluster", + SiteURL: "http://example.com", + Token: model.NewId(), + RemoteToken: model.NewId(), } t.Run("Should not work if the remote cluster service is not enabled", func(t *testing.T) { @@ -530,6 +531,7 @@ func TestGetRemoteCluster(t *testing.T) { require.Equal(t, rc.RemoteId, fetchedRC.RemoteId) require.Equal(t, th.BasicTeam.Id, fetchedRC.DefaultTeamId) require.Empty(t, fetchedRC.Token) + require.Empty(t, fetchedRC.RemoteToken) }) } @@ -540,6 +542,7 @@ func TestPatchRemoteCluster(t *testing.T) { DisplayName: "initialvalue", SiteURL: "http://example.com", Token: model.NewId(), + RemoteToken: model.NewId(), } rcp := &model.RemoteClusterPatch{DisplayName: model.NewPointer("different value")} @@ -593,6 +596,8 @@ func TestPatchRemoteCluster(t *testing.T) { require.NoError(t, err) require.Equal(t, "patched!", patchedRC.DisplayName) require.Equal(t, newTeamId, patchedRC.DefaultTeamId) + require.Empty(t, patchedRC.Token) + require.Empty(t, patchedRC.RemoteToken) }) }
6fd49f56b592MM-68526: Harden remote cluster patch response (#36288) (#36313)
2 files changed · +10 −3
server/channels/api4/remote_cluster.go+2 −0 modified@@ -655,6 +655,8 @@ func patchRemoteCluster(c *Context, w http.ResponseWriter, r *http.Request) { return } + updatedRC.Sanitize() + auditRec.Success() auditRec.AddEventResultState(updatedRC)
server/channels/api4/remote_cluster_test.go+8 −3 modified@@ -488,9 +488,10 @@ func TestGenerateRemoteClusterInvite(t *testing.T) { func TestGetRemoteCluster(t *testing.T) { mainHelper.Parallel(t) newRC := &model.RemoteCluster{ - Name: "remotecluster", - SiteURL: "http://example.com", - Token: model.NewId(), + Name: "remotecluster", + SiteURL: "http://example.com", + Token: model.NewId(), + RemoteToken: model.NewId(), } t.Run("Should not work if the remote cluster service is not enabled", func(t *testing.T) { @@ -541,6 +542,7 @@ func TestGetRemoteCluster(t *testing.T) { require.Equal(t, rc.RemoteId, fetchedRC.RemoteId) require.Equal(t, th.BasicTeam.Id, fetchedRC.DefaultTeamId) require.Empty(t, fetchedRC.Token) + require.Empty(t, fetchedRC.RemoteToken) }) } @@ -551,6 +553,7 @@ func TestPatchRemoteCluster(t *testing.T) { DisplayName: "initialvalue", SiteURL: "http://example.com", Token: model.NewId(), + RemoteToken: model.NewId(), } rcp := &model.RemoteClusterPatch{DisplayName: model.NewPointer("different value")} @@ -606,6 +609,8 @@ func TestPatchRemoteCluster(t *testing.T) { require.NoError(t, err) require.Equal(t, "patched!", patchedRC.DisplayName) require.Equal(t, newTeamId, patchedRC.DefaultTeamId) + require.Empty(t, patchedRC.Token) + require.Empty(t, patchedRC.RemoteToken) }) }
c5e67e28271cMM-68526: Harden remote cluster patch response (#36288) (#36310)
2 files changed · +10 −3
server/channels/api4/remote_cluster.go+2 −0 modified@@ -660,6 +660,8 @@ func patchRemoteCluster(c *Context, w http.ResponseWriter, r *http.Request) { return } + updatedRC.Sanitize() + auditRec.Success() auditRec.AddEventResultState(updatedRC)
server/channels/api4/remote_cluster_test.go+8 −3 modified@@ -619,9 +619,10 @@ func TestGenerateRemoteClusterInvite(t *testing.T) { func TestGetRemoteCluster(t *testing.T) { mainHelper.Parallel(t) newRC := &model.RemoteCluster{ - Name: "remotecluster", - SiteURL: "http://example.com", - Token: model.NewId(), + Name: "remotecluster", + SiteURL: "http://example.com", + Token: model.NewId(), + RemoteToken: model.NewId(), } t.Run("Should not work if the remote cluster service is not enabled", func(t *testing.T) { @@ -670,6 +671,7 @@ func TestGetRemoteCluster(t *testing.T) { require.Equal(t, rc.RemoteId, fetchedRC.RemoteId) require.Equal(t, th.BasicTeam.Id, fetchedRC.DefaultTeamId) require.Empty(t, fetchedRC.Token) + require.Empty(t, fetchedRC.RemoteToken) }) } @@ -737,6 +739,7 @@ func TestPatchRemoteCluster(t *testing.T) { DisplayName: "initialvalue", SiteURL: "http://example.com", Token: model.NewId(), + RemoteToken: model.NewId(), } rcp := &model.RemoteClusterPatch{DisplayName: model.NewPointer("different value")} @@ -790,6 +793,8 @@ func TestPatchRemoteCluster(t *testing.T) { require.NoError(t, err) require.Equal(t, "patched!", patchedRC.DisplayName) require.Equal(t, newTeamId, patchedRC.DefaultTeamId) + require.Empty(t, patchedRC.Token) + require.Empty(t, patchedRC.RemoteToken) }) }
Vulnerability mechanics
Root cause
"Missing sanitization of the remote cluster object before returning it in the PATCH response leaks authentication tokens."
Attack vector
An authenticated attacker who holds the `manage_secure_connections` permission sends a PATCH request to the remote cluster endpoint. Because the server failed to sanitize the response, the attacker receives the remote cluster's authentication tokens (`Token` and `RemoteToken`) in the API reply. The attacker can then use those tokens to impersonate the remote cluster or gain unauthorized access to connected clusters. The vulnerability is triggered over the network with low complexity and requires no user interaction.
Affected code
The `patchRemoteCluster` handler in `server/channels/api4/remote_cluster.go` did not call `Sanitize()` on the updated remote cluster object before returning it in the HTTP response. This allowed sensitive fields such as `Token` and `RemoteToken` to be leaked to the caller. The three patches (patch_id=5724796, patch_id=5724797, patch_id=5724798) each add the missing `updatedRC.Sanitize()` call and update the corresponding test assertions in `remote_cluster_test.go` to verify that both `Token` and `RemoteToken` are empty in the response.
What the fix does
The fix adds a single line `updatedRC.Sanitize()` immediately after the remote cluster is patched and before the response is written. The `Sanitize()` method clears sensitive fields (including `Token` and `RemoteToken`) from the struct, ensuring they are never serialized into the HTTP response. The test changes confirm that after the fix, both `Token` and `RemoteToken` are empty in the returned object, whereas previously only `Token` was checked.
Preconditions
- authAttacker must be authenticated to the Mattermost server
- authAttacker must hold the manage_secure_connections permission
- networkAttacker must be able to send a PATCH request to the remote cluster API endpoint
Generated on Jun 12, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
1News mentions
0No linked articles in our index yet.