snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
189
x/nosurfx/nosurf_test.go
Normal file
189
x/nosurfx/nosurf_test.go
Normal file
@@ -0,0 +1,189 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nosurfx_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/kratos/x/nosurfx"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/ory/x/assertx"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ory/kratos/driver/config"
|
||||
"github.com/ory/kratos/pkg"
|
||||
"github.com/ory/nosurf"
|
||||
"github.com/ory/x/randx"
|
||||
)
|
||||
|
||||
func TestNosurfBaseCookieHandler(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conf, reg := pkg.NewFastRegistryWithMocks(t)
|
||||
require.NoError(t, conf.Set(ctx, config.ViperKeyPublicBaseURL, "http://foo.com/bar"))
|
||||
|
||||
cookie := nosurfx.NosurfBaseCookieHandler(reg)(httptest.NewRecorder(), httptest.NewRequest("GET", "https://foo/bar", nil))
|
||||
assert.EqualValues(t, "csrf_token_01c86631efd1537ee34a98e75884a6e21dd8e2d9e944934bca21204106bfd32f", cookie.Name, "base64 representation of http://foo.com/bar")
|
||||
assert.EqualValues(t, http.SameSiteLaxMode, cookie.SameSite, "is set to lax because https/secure is false - chrome rejects none samesite on non-https")
|
||||
assert.EqualValues(t, nosurf.MaxAge, cookie.MaxAge)
|
||||
assert.EqualValues(t, "/", cookie.Path, "cookie path is site root by default")
|
||||
assert.EqualValues(t, "", cookie.Domain, "domain for the cookie is set to empty per default")
|
||||
assert.False(t, cookie.Secure, "false because insecure dev mode")
|
||||
assert.True(t, cookie.HttpOnly)
|
||||
|
||||
alNum := regexp.MustCompile("[a-zA-Z_0-9]+")
|
||||
for i := 0; i < 10; i++ {
|
||||
require.NoError(t, conf.Set(ctx, config.ViperKeyPublicBaseURL, randx.MustString(16, randx.AlphaNum)))
|
||||
cookie := nosurfx.NosurfBaseCookieHandler(reg)(httptest.NewRecorder(), httptest.NewRequest("GET", "https://foo/bar", nil))
|
||||
|
||||
assert.NotEqual(t, "aHR0cDovL2Zvby5jb20vYmFy_csrf_token", cookie.Name, "should no longer be http://foo.com/bar")
|
||||
assert.True(t, alNum.MatchString(cookie.Name), "does not have any special chars")
|
||||
}
|
||||
|
||||
require.NoError(t, conf.Set(ctx, config.ViperKeyCookieSameSite, "None"))
|
||||
require.NoError(t, conf.Set(ctx, "dev", false))
|
||||
cookie = nosurfx.NosurfBaseCookieHandler(reg)(httptest.NewRecorder(), httptest.NewRequest("GET", "https://foo/bar", nil))
|
||||
assert.EqualValues(t, http.SameSiteNoneMode, cookie.SameSite, "can be none because https/secure is true")
|
||||
assert.True(t, cookie.Secure, "true because secure mode")
|
||||
assert.True(t, cookie.HttpOnly)
|
||||
}
|
||||
|
||||
func TestNosurfBaseCookieHandlerAliasing(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
conf, reg := pkg.NewFastRegistryWithMocks(t)
|
||||
|
||||
require.NoError(t, conf.Set(ctx, config.ViperKeyPublicBaseURL, "http://foo.com/bar"))
|
||||
|
||||
cookie := nosurfx.NosurfBaseCookieHandler(reg)(httptest.NewRecorder(), httptest.NewRequest("GET", "http://foo.com/bar", nil))
|
||||
assert.EqualValues(t, "", cookie.Domain, "remains unset")
|
||||
assert.EqualValues(t, "/", cookie.Path, "cookie path is site root by default")
|
||||
|
||||
// Check root settings
|
||||
require.NoError(t, conf.Set(ctx, config.ViperKeyCookieDomain, "bar.com"))
|
||||
require.NoError(t, conf.Set(ctx, config.ViperKeyCookiePath, "/baz"))
|
||||
cookie = nosurfx.NosurfBaseCookieHandler(reg)(httptest.NewRecorder(), httptest.NewRequest("GET", "http://foo.com/bar", nil))
|
||||
assert.EqualValues(t, "bar.com", cookie.Domain, "domain doesn't change when request not from an alias but is overwritten by ViperKeyCookieDomain")
|
||||
assert.EqualValues(t, "/baz", cookie.Path, "cookie path is site root by default but is overwritten by ViperKeyCookiePath")
|
||||
}
|
||||
|
||||
func TestNosurfBaseCookieErrorHandler(t *testing.T) {
|
||||
_, reg := pkg.NewFastRegistryWithMocks(t)
|
||||
|
||||
h := nosurfx.CSRFFailureHandler(reg)
|
||||
expectError := func(t *testing.T, err error, req *http.Request) {
|
||||
t.Helper()
|
||||
rec := httptest.NewRecorder()
|
||||
h(rec, req)
|
||||
assertx.EqualAsJSON(t, err, json.RawMessage(gjson.Get(rec.Body.String(), "error").Raw))
|
||||
}
|
||||
|
||||
newAjaxFormRequest := func() *http.Request {
|
||||
req := httptest.NewRequest("GET", "https://foo/bar", nil)
|
||||
req.Header.Set("Sec-Fetch-Mode", "cors")
|
||||
return req
|
||||
}
|
||||
|
||||
newAjaxJSONRequest := func() *http.Request {
|
||||
req := httptest.NewRequest("GET", "https://foo/bar", nil)
|
||||
req.Header.Set("Sec-Fetch-Mode", "cors")
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return req
|
||||
}
|
||||
|
||||
newBrowserRequest := func() *http.Request {
|
||||
req := httptest.NewRequest("GET", "https://foo/bar", nil)
|
||||
return req
|
||||
}
|
||||
|
||||
t.Run("case=without cookie", func(t *testing.T) {
|
||||
t.Run("source=ajax", func(t *testing.T) {
|
||||
expectError(t, nosurfx.ErrInvalidCSRFTokenAJAXNoCookies(), newAjaxFormRequest())
|
||||
})
|
||||
|
||||
t.Run("source=browser", func(t *testing.T) {
|
||||
expectError(t, nosurfx.ErrInvalidCSRFTokenServerNoCookies(), newBrowserRequest())
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("case=ajax with cookie but without csrf cookie", func(t *testing.T) {
|
||||
test := func(t *testing.T, req *http.Request, err error) {
|
||||
req.Header.Set("Cookie", "foo=bar;")
|
||||
expectError(t, err, req)
|
||||
}
|
||||
|
||||
t.Run("source=ajax", func(t *testing.T) {
|
||||
test(t, newAjaxFormRequest(), nosurfx.ErrInvalidCSRFTokenAJAXCookieMissing())
|
||||
})
|
||||
|
||||
t.Run("source=browser", func(t *testing.T) {
|
||||
test(t, newBrowserRequest(), nosurfx.ErrInvalidCSRFTokenServerCookieMissing())
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("case=with correct cookie but token was not sent in header", func(t *testing.T) {
|
||||
test := func(t *testing.T, req *http.Request, err error) {
|
||||
req.Header.Set("Cookie", nosurfx.CSRFCookieName(req.Context(), reg)+"=bar;")
|
||||
expectError(t, err, req)
|
||||
}
|
||||
|
||||
t.Run("source=ajax", func(t *testing.T) {
|
||||
test(t, newAjaxFormRequest(), nosurfx.ErrInvalidCSRFTokenAJAXTokenNotSent())
|
||||
})
|
||||
|
||||
t.Run("source=ajax json", func(t *testing.T) {
|
||||
test(t, newAjaxJSONRequest(), nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch())
|
||||
})
|
||||
|
||||
t.Run("source=browser", func(t *testing.T) {
|
||||
test(t, newBrowserRequest(), nosurfx.ErrInvalidCSRFTokenServerTokenNotSent())
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("case=ajax with correct cookie and token in header but they do not match", func(t *testing.T) {
|
||||
test := func(t *testing.T, req *http.Request, err error) {
|
||||
req.Header.Set(nosurf.HeaderName, "bar")
|
||||
req.Header.Set("Cookie", nosurfx.CSRFCookieName(req.Context(), reg)+"=bar;")
|
||||
expectError(t, err, req)
|
||||
}
|
||||
|
||||
t.Run("source=ajax", func(t *testing.T) {
|
||||
test(t, newAjaxFormRequest(), nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch())
|
||||
})
|
||||
|
||||
t.Run("source=browser", func(t *testing.T) {
|
||||
test(t, newBrowserRequest(), nosurfx.ErrInvalidCSRFTokenServerTokenMismatch())
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("case=correct cookie and token in body but they do not match", func(t *testing.T) {
|
||||
test := func(t *testing.T, req *http.Request, err error) {
|
||||
req.Header.Set("Accept", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Cookie", nosurfx.CSRFCookieName(req.Context(), reg)+"=bar;")
|
||||
require.NoError(t, req.ParseForm())
|
||||
expectError(t, err, req)
|
||||
}
|
||||
|
||||
t.Run("source=ajax", func(t *testing.T) {
|
||||
req := httptest.NewRequest("POST", "https://foo/bar", strings.NewReader(url.Values{nosurf.FormFieldName: {"bar"}}.Encode()))
|
||||
req.Header.Set("Sec-Fetch-Mode", "cors")
|
||||
test(t, req, nosurfx.ErrInvalidCSRFTokenAJAXTokenMismatch())
|
||||
})
|
||||
|
||||
t.Run("source=browser", func(t *testing.T) {
|
||||
req := httptest.NewRequest("POST", "https://foo/bar", strings.NewReader(url.Values{nosurf.FormFieldName: {"bar"}}.Encode()))
|
||||
test(t, req, nosurfx.ErrInvalidCSRFTokenServerTokenMismatch())
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user