test: require Secure/HttpOnly/SameSite on all session cookies
Add a failing test asserting that every cookie written by the session manager (both the CreateSession set-cookie path and the ClearSession delete-cookie path) carries HttpOnly, Secure, and a SameSite mode of Lax or stricter, regardless of constructor arguments. Session cookies carry authentication state and must never be sent over plaintext HTTP. Currently fails for the constructor secure=false case, which produces cookies without the Secure attribute (gosec G124 at internal/session/session.go:84 and :128).
This commit is contained in:
92
internal/session/session_cookie_attributes_test.go
Normal file
92
internal/session/session_cookie_attributes_test.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSessionCookieAttributesAlwaysSecure verifies that every cookie
|
||||||
|
// emitted by the session manager carries HttpOnly, Secure, and a
|
||||||
|
// SameSite mode of Lax or stricter, regardless of how the manager was
|
||||||
|
// constructed. 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). No
|
||||||
|
// constructor argument 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) {
|
||||||
|
for _, constructorBoolArg := range []bool{false, true} {
|
||||||
|
mgr, err := NewManager("test-signing-key-12345", constructorBoolArg)
|
||||||
|
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()
|
||||||
|
if err := mgr.CreateSession(w); 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 {
|
||||||
|
testName := writePath.name
|
||||||
|
if constructorBoolArg {
|
||||||
|
testName += "/constructorBoolArg=true"
|
||||||
|
} else {
|
||||||
|
testName += "/constructorBoolArg=false"
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run(testName, func(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
writePath.setCookie(t, w)
|
||||||
|
|
||||||
|
var sessionCookie *http.Cookie
|
||||||
|
for _, c := range w.Result().Cookies() {
|
||||||
|
if c.Name == CookieName {
|
||||||
|
sessionCookie = c
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sessionCookie == nil {
|
||||||
|
t.Fatalf("no cookie named %q was set", 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user