diff --git a/hscontrol/oidc.go b/hscontrol/oidc.go index db89539d..140e8683 100644 --- a/hscontrol/oidc.go +++ b/hscontrol/oidc.go @@ -919,12 +919,14 @@ func renderAuthSuccessTemplate( return bytes.NewBufferString(templates.AuthSuccess(result).Render()) } -// getCookieName generates a unique cookie name based on a cookie value. -// Callers must ensure value has at least [cookieNamePrefixLen] bytes; -// [extractCodeAndStateParamFromRequest] enforces this for the state -// parameter, and [setCSRFCookie] always supplies a 64-byte random value. +// getCookieName generates a unique cookie name based on a cookie value. It +// uses at most [cookieNamePrefixLen] bytes of value, and fewer if value is +// shorter, so a short value (e.g. a malformed nonce from a misbehaving IdP) +// yields a non-matching name rather than panicking with slice-out-of-range. func getCookieName(baseName, value string) string { - return fmt.Sprintf("%s_%s", baseName, value[:cookieNamePrefixLen]) + n := min(len(value), cookieNamePrefixLen) + + return fmt.Sprintf("%s_%s", baseName, value[:n]) } func setCSRFCookie(w http.ResponseWriter, r *http.Request, name string) (string, error) { diff --git a/hscontrol/oidc_cookiename_test.go b/hscontrol/oidc_cookiename_test.go new file mode 100644 index 00000000..6adca0bf --- /dev/null +++ b/hscontrol/oidc_cookiename_test.go @@ -0,0 +1,20 @@ +package hscontrol + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestGetCookieNameShortValue ensures a value shorter than the prefix length +// (e.g. a malformed nonce from a misbehaving IdP) does not panic with +// slice-out-of-range; it uses the available bytes instead. +func TestGetCookieNameShortValue(t *testing.T) { + require.NotPanics(t, func() { + assert.Equal(t, "nonce_ab", getCookieName("nonce", "ab")) + }) + + assert.Equal(t, "nonce_abcdef", getCookieName("nonce", "abcdef")) + assert.Equal(t, "nonce_abcdef", getCookieName("nonce", "abcdefghij")) +}