diff --git a/admin.go b/admin.go index 97af846ef..5a54753d1 100644 --- a/admin.go +++ b/admin.go @@ -978,7 +978,9 @@ func (h adminHandler) originAllowed(origin *url.URL) bool { if allowedOrigin.Scheme != "" && origin.Scheme != allowedOrigin.Scheme { continue } - if origin.Host == allowedOrigin.Host { + // Host comparison is case-insensitive per RFC 3986 §3.2.2; url.Parse + // does not normalize host case, so fold it here. + if strings.EqualFold(origin.Host, allowedOrigin.Host) { return true } } diff --git a/admin_security_test.go b/admin_security_test.go index 48bb5c1f3..a47fb8d93 100644 --- a/admin_security_test.go +++ b/admin_security_test.go @@ -90,12 +90,24 @@ func TestAdminHandlerOriginAllowed(t *testing.T) { want: true, }, { - // Per RFC 3986 section 3.2.2, host names are case-insensitive. - name: "case-sensitive host comparison (potential RFC 3986 deviation)", + // Per RFC 3986 §3.2.2, host names are case-insensitive. + name: "host comparison is case-insensitive (allow-list uppercase)", allowedOrigins: []*url.URL{mustParseURL(t, "http://Example.com:8080")}, origin: mustParseURL(t, "http://example.com:8080"), want: true, }, + { + name: "host comparison is case-insensitive (origin uppercase)", + allowedOrigins: []*url.URL{mustParseURL(t, "http://example.com:8080")}, + origin: mustParseURL(t, "http://EXAMPLE.com:8080"), + want: true, + }, + { + name: "host comparison is case-insensitive (mixed case)", + allowedOrigins: []*url.URL{mustParseURL(t, "http://Foo.Bar.Example.com:8080")}, + origin: mustParseURL(t, "http://foo.bar.example.COM:8080"), + want: true, + }, } for _, tc := range tests {