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) } }) } }