All checks were successful
check / check (push) Successful in 4s
Canonical v2-schema `.golangci.yml`, golangci-lint pins bumped to v2.12.2 in `Dockerfile` and `script/bootstrap`, and the tree brought to `0 issues.` under it. Three behaviour deltas: `Cache.StoreVariant` takes a context (cancelled requests skip the accounting row, recovered by reconciliation); `MetadataStorage.Store` no longer leaks `.tmp-*.json` on Write/Close/Rename failure (dead-defer bug fix); the `signing_key` too-short error text gained a `value too short:` prefix. Eviction-loop context cancellation deferred to #102.
92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
package session_test
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"sneak.berlin/go/pixa/internal/session"
|
|
)
|
|
|
|
// TestSessionCookieAttributesAlwaysSecure verifies that every cookie
|
|
// emitted by the session manager carries HttpOnly, Secure, and a
|
|
// SameSite mode of Lax or stricter. Session cookies contain the
|
|
// authentication state and must never be exposed to script (HttpOnly),
|
|
// sent over plaintext HTTP (Secure), or attached to cross-site
|
|
// requests (SameSite). Nothing may weaken these attributes.
|
|
//
|
|
// This covers both cookie-writing paths: CreateSession (the login
|
|
// set-cookie path) and ClearSession (the logout delete-cookie path).
|
|
func TestSessionCookieAttributesAlwaysSecure(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
mgr, err := session.NewManager("test-signing-key-12345")
|
|
if err != nil {
|
|
t.Fatalf("NewManager() error = %v", err)
|
|
}
|
|
|
|
writePaths := []struct {
|
|
name string
|
|
setCookie func(t *testing.T, w http.ResponseWriter)
|
|
}{
|
|
{
|
|
name: "CreateSession",
|
|
setCookie: func(t *testing.T, w http.ResponseWriter) {
|
|
t.Helper()
|
|
|
|
err := mgr.CreateSession(w)
|
|
if err != nil {
|
|
t.Fatalf("CreateSession() error = %v", err)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "ClearSession",
|
|
setCookie: func(t *testing.T, w http.ResponseWriter) {
|
|
t.Helper()
|
|
mgr.ClearSession(w)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, writePath := range writePaths {
|
|
t.Run(writePath.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
w := httptest.NewRecorder()
|
|
writePath.setCookie(t, w)
|
|
|
|
var sessionCookie *http.Cookie
|
|
|
|
for _, c := range w.Result().Cookies() {
|
|
if c.Name == session.CookieName {
|
|
sessionCookie = c
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if sessionCookie == nil {
|
|
t.Fatalf("no cookie named %q was set", session.CookieName)
|
|
}
|
|
|
|
t.Logf("cookie attributes: HttpOnly=%v Secure=%v SameSite=%v",
|
|
sessionCookie.HttpOnly, sessionCookie.Secure, sessionCookie.SameSite)
|
|
|
|
if !sessionCookie.HttpOnly {
|
|
t.Error("session cookie must have HttpOnly set")
|
|
}
|
|
|
|
if !sessionCookie.Secure {
|
|
t.Error("session cookie must have Secure set")
|
|
}
|
|
|
|
if sessionCookie.SameSite != http.SameSiteLaxMode &&
|
|
sessionCookie.SameSite != http.SameSiteStrictMode {
|
|
t.Errorf("session cookie SameSite = %v, want Lax (%v) or Strict (%v)",
|
|
sessionCookie.SameSite, http.SameSiteLaxMode, http.SameSiteStrictMode)
|
|
}
|
|
})
|
|
}
|
|
}
|