VYPR
High severityNVD Advisory· Published Oct 16, 2025· Updated Feb 26, 2026

Arbitrary Mattermost Team can be joined by manipulating the OAuth state

CVE-2025-58073

Description

Mattermost versions 10.11.x <= 10.11.1, 10.10.x <= 10.10.2, 10.5.x <= 10.5.10 fail to verify a user has permission to join a Mattermost team using the original invite token which allows any attacked to join any team on a Mattermost server regardless of restrictions via manipulating the OAuth state.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/mattermost/mattermost/server/v8Go
< 8.0.0-20250807174701-e14175eb65398.0.0-20250807174701-e14175eb6539
github.com/mattermost/mattermost-serverGo
>= 10.11.0, < 10.11.210.11.2
github.com/mattermost/mattermost-serverGo
>= 10.10.0, < 10.10.310.10.3
github.com/mattermost/mattermost-serverGo
>= 10.5.0, < 10.5.1110.5.11

Affected products

1

Patches

3
39bd251fe4f6

[MM-65015] Restore Mobile redirection on oauth login (#33626) (#33637)

https://github.com/mattermost/mattermostMattermost BuildAug 8, 2025via ghsa
3 files changed · +21 6
  • server/channels/web/oauth.go+16 4 modified
    @@ -11,6 +11,7 @@ import (
     	"net/url"
     	"path"
     	"path/filepath"
    +	"slices"
     	"strings"
     	"time"
     
    @@ -23,6 +24,10 @@ import (
     	"github.com/mattermost/mattermost/server/v8/channels/utils/fileutils"
     )
     
    +const (
    +	callbackHost = "callback"
    +)
    +
     func (w *Web) InitOAuth() {
     	// API version independent OAuth 2.0 as a service provider endpoints
     	w.MainRouter.Handle("/oauth/authorize", w.APIHandlerTrustRequester(authorizeOAuthPage)).Methods(http.MethodGet)
    @@ -316,7 +321,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
     			hasRedirectURL = redirectURL != ""
     		}
     	}
    -	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL)
    +	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL, c.App.Config().NativeAppSettings.AppCustomURLSchemes)
     
     	renderError := func(err *model.AppError) {
     		if isMobile && hasRedirectURL {
    @@ -536,7 +541,7 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
     	http.Redirect(w, r, authURL, http.StatusFound)
     }
     
    -func fullyQualifiedRedirectURL(siteURLPrefix, targetURL string) string {
    +func fullyQualifiedRedirectURL(siteURLPrefix, targetURL string, otherValidSchemes []string) string {
     	parsed, err := url.Parse(targetURL)
     	if err != nil {
     		return siteURLPrefix
    @@ -545,8 +550,15 @@ func fullyQualifiedRedirectURL(siteURLPrefix, targetURL string) string {
     	if err != nil {
     		return siteURLPrefix
     	}
    -
    -	// Check if the targetURL is a valid URL and is within the siteURLPrefix
    +	// mobile access
    +	if slices.Contains(otherValidSchemes, fmt.Sprintf("%v://", parsed.Scheme)) &&
    +		parsed.Host == callbackHost &&
    +		parsed.Path == "" &&
    +		parsed.RawQuery == "" &&
    +		parsed.Fragment == "" {
    +		return targetURL
    +	}
    +	// Check if the targetURL is valid and within the siteURLPrefix, excluding native app schemes like mmauth://
     	sameScheme := parsed.Scheme == prefixParsed.Scheme
     	sameHost := parsed.Host == prefixParsed.Host
     	safePath := strings.HasPrefix(path.Clean(parsed.Path), path.Clean(prefixParsed.Path))
    
  • server/channels/web/oauth_test.go+4 1 modified
    @@ -849,6 +849,7 @@ func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
     
     func TestFullyQualifiedRedirectURL(t *testing.T) {
     	const siteURL = "https://xxx.yyy/mm"
    +
     	for target, expected := range map[string]string{
     		"":                                     siteURL,
     		"/":                                    siteURL + "/",
    @@ -869,9 +870,11 @@ func TestFullyQualifiedRedirectURL(t *testing.T) {
     		"https://xxx.yyy/mm/some-path#section": siteURL + "/some-path#section",
     		"https://xxx.yyy/mm/../malicious-path": siteURL,
     		":foo":                                 siteURL,
    +		"mmauth://callback":                    "mmauth://callback",
    +		"mmauth://xxx.yyy/mm":                  siteURL, // invalid mobile URL (wrong host)
     	} {
     		t.Run(target, func(t *testing.T) {
    -			require.Equal(t, expected, fullyQualifiedRedirectURL(siteURL, target))
    +			require.Equal(t, expected, fullyQualifiedRedirectURL(siteURL, target, []string{"mmauth://"}))
     		})
     	}
     }
    
  • server/channels/web/saml.go+1 1 modified
    @@ -117,7 +117,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
     		redirectURL = val
     		hasRedirectURL = val != ""
     	}
    -	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL)
    +	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL, c.App.Config().NativeAppSettings.AppCustomURLSchemes)
     
     	handleError := func(err *model.AppError) {
     		if isMobile && hasRedirectURL {
    
2096f975b2c0

[MM-65015] Restore Mobile redirection on oauth login (#33626) (#33634)

https://github.com/mattermost/mattermostMattermost BuildAug 8, 2025via ghsa
3 files changed · +21 6
  • server/channels/web/oauth.go+16 4 modified
    @@ -11,6 +11,7 @@ import (
     	"net/url"
     	"path"
     	"path/filepath"
    +	"slices"
     	"strings"
     	"time"
     
    @@ -23,6 +24,10 @@ import (
     	"github.com/mattermost/mattermost/server/v8/channels/utils/fileutils"
     )
     
    +const (
    +	callbackHost = "callback"
    +)
    +
     func (w *Web) InitOAuth() {
     	// API version independent OAuth 2.0 as a service provider endpoints
     	w.MainRouter.Handle("/oauth/authorize", w.APIHandlerTrustRequester(authorizeOAuthPage)).Methods(http.MethodGet)
    @@ -316,7 +321,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
     			hasRedirectURL = redirectURL != ""
     		}
     	}
    -	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL)
    +	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL, c.App.Config().NativeAppSettings.AppCustomURLSchemes)
     
     	renderError := func(err *model.AppError) {
     		if isMobile && hasRedirectURL {
    @@ -536,7 +541,7 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
     	http.Redirect(w, r, authURL, http.StatusFound)
     }
     
    -func fullyQualifiedRedirectURL(siteURLPrefix, targetURL string) string {
    +func fullyQualifiedRedirectURL(siteURLPrefix, targetURL string, otherValidSchemes []string) string {
     	parsed, err := url.Parse(targetURL)
     	if err != nil {
     		return siteURLPrefix
    @@ -545,8 +550,15 @@ func fullyQualifiedRedirectURL(siteURLPrefix, targetURL string) string {
     	if err != nil {
     		return siteURLPrefix
     	}
    -
    -	// Check if the targetURL is a valid URL and is within the siteURLPrefix
    +	// mobile access
    +	if slices.Contains(otherValidSchemes, fmt.Sprintf("%v://", parsed.Scheme)) &&
    +		parsed.Host == callbackHost &&
    +		parsed.Path == "" &&
    +		parsed.RawQuery == "" &&
    +		parsed.Fragment == "" {
    +		return targetURL
    +	}
    +	// Check if the targetURL is valid and within the siteURLPrefix, excluding native app schemes like mmauth://
     	sameScheme := parsed.Scheme == prefixParsed.Scheme
     	sameHost := parsed.Host == prefixParsed.Host
     	safePath := strings.HasPrefix(path.Clean(parsed.Path), path.Clean(prefixParsed.Path))
    
  • server/channels/web/oauth_test.go+4 1 modified
    @@ -861,6 +861,7 @@ func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
     
     func TestFullyQualifiedRedirectURL(t *testing.T) {
     	const siteURL = "https://xxx.yyy/mm"
    +
     	for target, expected := range map[string]string{
     		"":                                     siteURL,
     		"/":                                    siteURL + "/",
    @@ -881,9 +882,11 @@ func TestFullyQualifiedRedirectURL(t *testing.T) {
     		"https://xxx.yyy/mm/some-path#section": siteURL + "/some-path#section",
     		"https://xxx.yyy/mm/../malicious-path": siteURL,
     		":foo":                                 siteURL,
    +		"mmauth://callback":                    "mmauth://callback",
    +		"mmauth://xxx.yyy/mm":                  siteURL, // invalid mobile URL (wrong host)
     	} {
     		t.Run(target, func(t *testing.T) {
    -			require.Equal(t, expected, fullyQualifiedRedirectURL(siteURL, target))
    +			require.Equal(t, expected, fullyQualifiedRedirectURL(siteURL, target, []string{"mmauth://"}))
     		})
     	}
     }
    
  • server/channels/web/saml.go+1 1 modified
    @@ -118,7 +118,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
     		redirectURL = val
     		hasRedirectURL = val != ""
     	}
    -	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL)
    +	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL, c.App.Config().NativeAppSettings.AppCustomURLSchemes)
     
     	handleError := func(err *model.AppError) {
     		if isMobile && hasRedirectURL {
    
e14175eb6539

[MM-65015] Restore Mobile redirection on oauth login (#33626) (#33633)

https://github.com/mattermost/mattermostMattermost BuildAug 7, 2025via ghsa
3 files changed · +21 6
  • server/channels/web/oauth.go+16 4 modified
    @@ -11,6 +11,7 @@ import (
     	"net/url"
     	"path"
     	"path/filepath"
    +	"slices"
     	"strings"
     	"time"
     
    @@ -22,6 +23,10 @@ import (
     	"github.com/mattermost/mattermost/server/v8/channels/utils/fileutils"
     )
     
    +const (
    +	callbackHost = "callback"
    +)
    +
     func (w *Web) InitOAuth() {
     	// API version independent OAuth 2.0 as a service provider endpoints
     	w.MainRouter.Handle("/oauth/authorize", w.APIHandlerTrustRequester(authorizeOAuthPage)).Methods(http.MethodGet)
    @@ -315,7 +320,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
     			hasRedirectURL = redirectURL != ""
     		}
     	}
    -	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL)
    +	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL, c.App.Config().NativeAppSettings.AppCustomURLSchemes)
     
     	renderError := func(err *model.AppError) {
     		if isMobile && hasRedirectURL {
    @@ -535,7 +540,7 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
     	http.Redirect(w, r, authURL, http.StatusFound)
     }
     
    -func fullyQualifiedRedirectURL(siteURLPrefix, targetURL string) string {
    +func fullyQualifiedRedirectURL(siteURLPrefix, targetURL string, otherValidSchemes []string) string {
     	parsed, err := url.Parse(targetURL)
     	if err != nil {
     		return siteURLPrefix
    @@ -544,8 +549,15 @@ func fullyQualifiedRedirectURL(siteURLPrefix, targetURL string) string {
     	if err != nil {
     		return siteURLPrefix
     	}
    -
    -	// Check if the targetURL is a valid URL and is within the siteURLPrefix
    +	// mobile access
    +	if slices.Contains(otherValidSchemes, fmt.Sprintf("%v://", parsed.Scheme)) &&
    +		parsed.Host == callbackHost &&
    +		parsed.Path == "" &&
    +		parsed.RawQuery == "" &&
    +		parsed.Fragment == "" {
    +		return targetURL
    +	}
    +	// Check if the targetURL is valid and within the siteURLPrefix, excluding native app schemes like mmauth://
     	sameScheme := parsed.Scheme == prefixParsed.Scheme
     	sameHost := parsed.Host == prefixParsed.Host
     	safePath := strings.HasPrefix(path.Clean(parsed.Path), path.Clean(prefixParsed.Path))
    
  • server/channels/web/oauth_test.go+4 1 modified
    @@ -861,6 +861,7 @@ func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
     
     func TestFullyQualifiedRedirectURL(t *testing.T) {
     	const siteURL = "https://xxx.yyy/mm"
    +
     	for target, expected := range map[string]string{
     		"":                                     siteURL,
     		"/":                                    siteURL + "/",
    @@ -881,9 +882,11 @@ func TestFullyQualifiedRedirectURL(t *testing.T) {
     		"https://xxx.yyy/mm/some-path#section": siteURL + "/some-path#section",
     		"https://xxx.yyy/mm/../malicious-path": siteURL,
     		":foo":                                 siteURL,
    +		"mmauth://callback":                    "mmauth://callback",
    +		"mmauth://xxx.yyy/mm":                  siteURL, // invalid mobile URL (wrong host)
     	} {
     		t.Run(target, func(t *testing.T) {
    -			require.Equal(t, expected, fullyQualifiedRedirectURL(siteURL, target))
    +			require.Equal(t, expected, fullyQualifiedRedirectURL(siteURL, target, []string{"mmauth://"}))
     		})
     	}
     }
    
  • server/channels/web/saml.go+1 1 modified
    @@ -117,7 +117,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
     		redirectURL = val
     		hasRedirectURL = val != ""
     	}
    -	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL)
    +	redirectURL = fullyQualifiedRedirectURL(c.GetSiteURLHeader(), redirectURL, c.App.Config().NativeAppSettings.AppCustomURLSchemes)
     
     	handleError := func(err *model.AppError) {
     		if isMobile && hasRedirectURL {
    

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

6

News mentions

0

No linked articles in our index yet.