VYPR
Low severityNVD Advisory· Published Oct 16, 2025· Updated Oct 16, 2025

Insecure string comparison enables timing attacks

CVE-2025-54499

Description

Mattermost versions 10.5.x <= 10.5.10, 10.11.x <= 10.11.2 fail to use constant-time comparison for sensitive string comparisons which allows attackers to exploit timing oracles to perform byte-by-byte brute force attacks via response time analysis on Cloud API keys and OAuth client secrets

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/mattermost/mattermost/server/v8Go
< 8.0.0-20250728063359-38208b8f065f8.0.0-20250728063359-38208b8f065f
github.com/mattermost/mattermost-serverGo
>= 10.5.0, < 10.5.1110.5.11
github.com/mattermost/mattermost-serverGo
>= 10.11.0, < 10.11.310.11.3

Affected products

1

Patches

2
97a4c7839cf5

MM-64755: Fix redirect in oauth login (#33388) (#33570)

https://github.com/mattermost/mattermostMattermost BuildJul 28, 2025via ghsa
2 files changed · +181 6
  • server/channels/utils/utils.go+9 6 modified
    @@ -191,14 +191,17 @@ func AppendQueryParamsToURL(baseURL string, params map[string]string) string {
     // Validates RedirectURL passed during OAuth or SAML
     func IsValidWebAuthRedirectURL(config *model.Config, redirectURL string) bool {
     	u, err := url.Parse(redirectURL)
    -	if err == nil && (u.Scheme == "http" || u.Scheme == "https") {
    -		if config.ServiceSettings.SiteURL != nil {
    -			siteURL := *config.ServiceSettings.SiteURL
    -			return strings.Index(strings.ToLower(redirectURL), strings.ToLower(siteURL)) == 0
    -		}
    +	if err != nil || config.ServiceSettings.SiteURL == nil {
    +		return false
    +	}
    +	siteURL, err := url.Parse(*config.ServiceSettings.SiteURL)
    +	if err != nil {
     		return false
     	}
    -	return true
    +	if u.Scheme == siteURL.Scheme && u.Host == siteURL.Host {
    +		return true
    +	}
    +	return false
     }
     
     // Validates Mobile Custom URL Scheme passed during OAuth or SAML
    
  • server/channels/utils/utils_test.go+172 0 modified
    @@ -9,6 +9,8 @@ import (
     	"testing"
     
     	"github.com/stretchr/testify/assert"
    +
    +	"github.com/mattermost/mattermost/server/public/model"
     )
     
     func TestStringArrayIntersection(t *testing.T) {
    @@ -432,3 +434,173 @@ func TestRoundOffToZeroesResolution(t *testing.T) {
     		})
     	}
     }
    +
    +func TestIsValidWebAuthRedirectURL(t *testing.T) {
    +	t.Run("Valid redirect URL with matching scheme and host", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "https://example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Valid redirect URL with matching scheme and host with port", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com:8080"),
    +			},
    +		}
    +		redirectURL := "https://example.com:8080/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL with different scheme", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "http://example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL with different host", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "https://malicious.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL with different port", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com:8080"),
    +			},
    +		}
    +		redirectURL := "https://example.com:9090/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL - malformed URL", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "not-a-valid-url"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Invalid config - nil SiteURL", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: nil,
    +			},
    +		}
    +		redirectURL := "https://example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Invalid config - malformed SiteURL", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("not-a-valid-url"),
    +			},
    +		}
    +		redirectURL := "https://example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Valid redirect URL with subdomain", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://app.example.com"),
    +			},
    +		}
    +		redirectURL := "https://app.example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL with different subdomain", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://app.example.com"),
    +			},
    +		}
    +		redirectURL := "https://api.example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Valid redirect URL with path", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com/mattermost"),
    +			},
    +		}
    +		redirectURL := "https://example.com/mattermost/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Valid redirect URL with query parameters", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "https://example.com/oauth/callback?state=abc123&code=def456"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Valid redirect URL with fragment", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "https://example.com/oauth/callback#token=abc123"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL with @ symbol in host", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://qa-release.test.mattermost.cloud"),
    +			},
    +		}
    +		redirectURL := "https://qa-release.test.mattermost.cloud@example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +}
    
38208b8f065f

MM-64755: Fix redirect in oauth login (#33388) (#33569)

https://github.com/mattermost/mattermostMattermost BuildJul 28, 2025via ghsa
2 files changed · +181 6
  • server/channels/utils/utils.go+9 6 modified
    @@ -191,14 +191,17 @@ func AppendQueryParamsToURL(baseURL string, params map[string]string) string {
     // Validates RedirectURL passed during OAuth or SAML
     func IsValidWebAuthRedirectURL(config *model.Config, redirectURL string) bool {
     	u, err := url.Parse(redirectURL)
    -	if err == nil && (u.Scheme == "http" || u.Scheme == "https") {
    -		if config.ServiceSettings.SiteURL != nil {
    -			siteURL := *config.ServiceSettings.SiteURL
    -			return strings.Index(strings.ToLower(redirectURL), strings.ToLower(siteURL)) == 0
    -		}
    +	if err != nil || config.ServiceSettings.SiteURL == nil {
    +		return false
    +	}
    +	siteURL, err := url.Parse(*config.ServiceSettings.SiteURL)
    +	if err != nil {
     		return false
     	}
    -	return true
    +	if u.Scheme == siteURL.Scheme && u.Host == siteURL.Host {
    +		return true
    +	}
    +	return false
     }
     
     // Validates Mobile Custom URL Scheme passed during OAuth or SAML
    
  • server/channels/utils/utils_test.go+172 0 modified
    @@ -9,6 +9,8 @@ import (
     	"testing"
     
     	"github.com/stretchr/testify/assert"
    +
    +	"github.com/mattermost/mattermost/server/public/model"
     )
     
     func TestStringArrayIntersection(t *testing.T) {
    @@ -432,3 +434,173 @@ func TestRoundOffToZeroesResolution(t *testing.T) {
     		})
     	}
     }
    +
    +func TestIsValidWebAuthRedirectURL(t *testing.T) {
    +	t.Run("Valid redirect URL with matching scheme and host", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "https://example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Valid redirect URL with matching scheme and host with port", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com:8080"),
    +			},
    +		}
    +		redirectURL := "https://example.com:8080/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL with different scheme", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "http://example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL with different host", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "https://malicious.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL with different port", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com:8080"),
    +			},
    +		}
    +		redirectURL := "https://example.com:9090/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL - malformed URL", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "not-a-valid-url"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Invalid config - nil SiteURL", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: nil,
    +			},
    +		}
    +		redirectURL := "https://example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Invalid config - malformed SiteURL", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("not-a-valid-url"),
    +			},
    +		}
    +		redirectURL := "https://example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Valid redirect URL with subdomain", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://app.example.com"),
    +			},
    +		}
    +		redirectURL := "https://app.example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL with different subdomain", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://app.example.com"),
    +			},
    +		}
    +		redirectURL := "https://api.example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +
    +	t.Run("Valid redirect URL with path", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com/mattermost"),
    +			},
    +		}
    +		redirectURL := "https://example.com/mattermost/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Valid redirect URL with query parameters", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "https://example.com/oauth/callback?state=abc123&code=def456"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Valid redirect URL with fragment", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://example.com"),
    +			},
    +		}
    +		redirectURL := "https://example.com/oauth/callback#token=abc123"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.True(t, result)
    +	})
    +
    +	t.Run("Invalid redirect URL with @ symbol in host", func(t *testing.T) {
    +		config := &model.Config{
    +			ServiceSettings: model.ServiceSettings{
    +				SiteURL: model.NewPointer("https://qa-release.test.mattermost.cloud"),
    +			},
    +		}
    +		redirectURL := "https://qa-release.test.mattermost.cloud@example.com/oauth/callback"
    +
    +		result := IsValidWebAuthRedirectURL(config, redirectURL)
    +		assert.False(t, result)
    +	})
    +}
    

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

5

News mentions

0

No linked articles in our index yet.