mirror of
https://github.com/umputun/reproxy.git
synced 2025-11-29 22:08:14 +02:00
fix parsing headers with colons in the value, i.e. CSP
This commit is contained in:
@@ -18,20 +18,23 @@ import (
|
||||
)
|
||||
|
||||
func headersHandler(addHeaders, dropHeaders []string) func(next http.Handler) http.Handler {
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if len(addHeaders) == 0 && len(dropHeaders) == 0 {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// add headers to response
|
||||
for _, h := range addHeaders {
|
||||
elems := strings.Split(h, ":")
|
||||
if len(elems) != 2 {
|
||||
continue
|
||||
// split on first colon only
|
||||
if i := strings.Index(h, ":"); i >= 0 {
|
||||
key := strings.TrimSpace(h[:i])
|
||||
value := strings.TrimSpace(h[i+1:])
|
||||
if key != "" {
|
||||
w.Header().Set(key, value)
|
||||
}
|
||||
}
|
||||
w.Header().Set(strings.TrimSpace(elems[0]), strings.TrimSpace(elems[1]))
|
||||
}
|
||||
|
||||
// drop headers from request
|
||||
|
||||
@@ -270,3 +270,61 @@ func TestHttp_basicAuthHandler(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaders_CSPParsing(t *testing.T) {
|
||||
tbl := []struct {
|
||||
name string
|
||||
headers []string
|
||||
expected map[string]string
|
||||
}{
|
||||
{
|
||||
name: "simple headers",
|
||||
headers: []string{"X-Frame-Options:SAMEORIGIN", "X-XSS-Protection:1; mode=block"},
|
||||
expected: map[string]string{"X-Frame-Options": "SAMEORIGIN", "X-XSS-Protection": "1; mode=block"},
|
||||
},
|
||||
{
|
||||
name: "CSP header with multiple directives",
|
||||
headers: []string{"Content-Security-Policy:default-src 'self'; style-src 'self' 'unsafe-inline': something"},
|
||||
expected: map[string]string{"Content-Security-Policy": "default-src 'self'; style-src 'self' 'unsafe-inline': something"},
|
||||
},
|
||||
{
|
||||
name: "CSP header with quotes and colons",
|
||||
headers: []string{"Content-Security-Policy:script-src 'unsafe-inline' 'unsafe-eval' 'self' https://example.com:443"},
|
||||
expected: map[string]string{"Content-Security-Policy": "script-src 'unsafe-inline' 'unsafe-eval' 'self' https://example.com:443"},
|
||||
},
|
||||
{
|
||||
name: "multiple colons in value",
|
||||
headers: []string{"Custom-Header:value:with:colons"},
|
||||
expected: map[string]string{"Custom-Header": "value:with:colons"},
|
||||
},
|
||||
{
|
||||
name: "empty value after colon",
|
||||
headers: []string{"Empty-Header:"},
|
||||
expected: map[string]string{"Empty-Header": ""},
|
||||
},
|
||||
{
|
||||
name: "malformed no colon",
|
||||
headers: []string{"Bad-Header-No-Colon"},
|
||||
expected: map[string]string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tbl {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
wr := httptest.NewRecorder()
|
||||
handler := headersHandler(tt.headers, nil)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
req := httptest.NewRequest("GET", "http://example.com", http.NoBody)
|
||||
handler.ServeHTTP(wr, req)
|
||||
|
||||
if len(tt.expected) == 0 {
|
||||
// For malformed headers, check they weren't set
|
||||
assert.Equal(t, 0, len(wr.Header()))
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range tt.expected {
|
||||
assert.Equal(t, v, wr.Header().Get(k), "Header %s value mismatch", k)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user