Skip to content

Commit

Permalink
Merge pull request from GHSA-pmjg-52h9-72qv
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

formatting

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

fixes from comments

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>

fix test

Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>
  • Loading branch information
crenshaw-dev authored Jul 12, 2022
1 parent 068ca89 commit 8d5119b
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 4 deletions.
16 changes: 13 additions & 3 deletions util/oidc/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"github.com/argoproj/argo-cd/v2/util/settings"
)

var InvalidRedirectURLError = fmt.Errorf("invalid return URL")

const (
GrantTypeAuthorizationCode = "authorization_code"
GrantTypeImplicit = "implicit"
Expand Down Expand Up @@ -185,10 +187,18 @@ func (a *ClientApp) verifyAppState(r *http.Request, w http.ResponseWriter, state
return "", err
}
cookieVal := string(val)
returnURL := a.baseHRef
redirectURL := a.baseHRef
parts := strings.SplitN(cookieVal, ":", 2)
if len(parts) == 2 && parts[1] != "" {
returnURL = parts[1]
if !isValidRedirectURL(parts[1], []string{a.settings.URL}) {
sanitizedUrl := parts[1]
if len(sanitizedUrl) > 100 {
sanitizedUrl = sanitizedUrl[:100]
}
log.Warnf("Failed to verify app state - got invalid redirectURL %q", sanitizedUrl)
return "", fmt.Errorf("failed to verify app state: %w", InvalidRedirectURLError)
}
redirectURL = parts[1]
}
if parts[0] != state {
return "", fmt.Errorf("invalid state in '%s' cookie", common.AuthCookieName)
Expand All @@ -201,7 +211,7 @@ func (a *ClientApp) verifyAppState(r *http.Request, w http.ResponseWriter, state
SameSite: http.SameSiteLaxMode,
Secure: a.secureCookie,
})
return returnURL, nil
return redirectURL, nil
}

// isValidRedirectURL checks whether the given redirectURL matches on of the
Expand Down
52 changes: 51 additions & 1 deletion util/oidc/oidc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ func TestGenerateAppState(t *testing.T) {
signature, err := util.MakeSignature(32)
require.NoError(t, err)
expectedReturnURL := "http://argocd.example.com/"
app, err := NewClientApp(&settings.ArgoCDSettings{ServerSignature: signature}, "", "")
app, err := NewClientApp(&settings.ArgoCDSettings{ServerSignature: signature, URL: expectedReturnURL}, "", "")
require.NoError(t, err)
generateResponse := httptest.NewRecorder()
state, err := app.generateAppState(expectedReturnURL, generateResponse)
Expand Down Expand Up @@ -219,6 +219,56 @@ func TestGenerateAppState(t *testing.T) {
})
}

func TestGenerateAppState_XSS(t *testing.T) {
signature, err := util.MakeSignature(32)
require.NoError(t, err)
app, err := NewClientApp(
&settings.ArgoCDSettings{
// Only return URLs starting with this base should be allowed.
URL: "https://argocd.example.com",
ServerSignature: signature,
},
"", "",
)
require.NoError(t, err)

t.Run("XSS fails", func(t *testing.T) {
// This attack assumes the attacker has compromised the server's secret key. We use `generateAppState` here for
// convenience, but an attacker with access to the server secret could write their own code to generate the
// malicious cookie.

expectedReturnURL := "javascript: alert('hi')"
generateResponse := httptest.NewRecorder()
state, err := app.generateAppState(expectedReturnURL, generateResponse)
require.NoError(t, err)

req := httptest.NewRequest("GET", "/", nil)
for _, cookie := range generateResponse.Result().Cookies() {
req.AddCookie(cookie)
}

returnURL, err := app.verifyAppState(req, httptest.NewRecorder(), state)
assert.ErrorIs(t, err, InvalidRedirectURLError)
assert.Empty(t, returnURL)
})

t.Run("valid return URL succeeds", func(t *testing.T) {
expectedReturnURL := "https://argocd.example.com/some/path"
generateResponse := httptest.NewRecorder()
state, err := app.generateAppState(expectedReturnURL, generateResponse)
require.NoError(t, err)

req := httptest.NewRequest("GET", "/", nil)
for _, cookie := range generateResponse.Result().Cookies() {
req.AddCookie(cookie)
}

returnURL, err := app.verifyAppState(req, httptest.NewRecorder(), state)
assert.NoError(t, err, InvalidRedirectURLError)
assert.Equal(t, expectedReturnURL, returnURL)
})
}

func TestGenerateAppState_NoReturnURL(t *testing.T) {
signature, err := util.MakeSignature(32)
require.NoError(t, err)
Expand Down

0 comments on commit 8d5119b

Please sign in to comment.