Skip to content

Commit

Permalink
config: do not add route headers to global map (#4629)
Browse files Browse the repository at this point in the history
Currently the GetSetResponseHeadersForPolicy() method may add entries to 
the global SetResponseHeaders map, which can lead to one route's headers
being applied to other routes.

Instead, make a copy of the SetResponseHeaders map before adding any 
route-specific response header entries.

Add additional unit tests for GetSetResponseHeaders() and 
GetSetResponseHeadersForPolicy().
  • Loading branch information
kenjenkins committed Oct 18, 2023
1 parent 5f9f466 commit 5a73526
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 3 deletions.
9 changes: 6 additions & 3 deletions config/options.go
Expand Up @@ -1121,9 +1121,12 @@ func (o *Options) GetSetResponseHeaders() map[string]string {

// GetSetResponseHeadersForPolicy gets the SetResponseHeaders for a policy.
func (o *Options) GetSetResponseHeadersForPolicy(policy *Policy) map[string]string {
hdrs := o.SetResponseHeaders
if hdrs == nil {
hdrs = make(map[string]string)
hdrs := make(map[string]string)
for k, v := range o.SetResponseHeaders {
hdrs[k] = v
}

if o.SetResponseHeaders == nil {
for k, v := range defaultSetResponseHeaders {
hdrs[k] = v
}
Expand Down
56 changes: 56 additions & 0 deletions config/options_test.go
Expand Up @@ -978,6 +978,18 @@ func TestOptions_GetSetResponseHeaders(t *testing.T) {
options.SetResponseHeaders = map[string]string{DisableHeaderKey: "1", "x-other": "xyz"}
assert.Equal(t, map[string]string{}, options.GetSetResponseHeaders())
})
t.Run("empty", func(t *testing.T) {
options := NewDefaultOptions()
options.SetResponseHeaders = map[string]string{}
assert.Equal(t, map[string]string{}, options.GetSetResponseHeaders())
})
t.Run("no partial defaults", func(t *testing.T) {
options := NewDefaultOptions()
options.Cert = "CERT"
options.SetResponseHeaders = map[string]string{"X-Frame-Options": "DENY"}
assert.Equal(t, map[string]string{"X-Frame-Options": "DENY"},
options.GetSetResponseHeaders())
})
}

func TestOptions_GetSetResponseHeadersForPolicy(t *testing.T) {
Expand All @@ -989,6 +1001,50 @@ func TestOptions_GetSetResponseHeadersForPolicy(t *testing.T) {
}
assert.Equal(t, map[string]string{"x": "y"}, options.GetSetResponseHeadersForPolicy(policy))
})
t.Run("global defaults plus policy", func(t *testing.T) {
options := NewDefaultOptions()
options.Cert = "CERT"
policy := &Policy{
SetResponseHeaders: map[string]string{"Route": "xyz"},
}
assert.Equal(t, map[string]string{
"Route": "xyz",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
"X-Frame-Options": "SAMEORIGIN",
"X-XSS-Protection": "1; mode=block",
}, options.GetSetResponseHeadersForPolicy(policy))
})
t.Run("global defaults partial override", func(t *testing.T) {
options := NewDefaultOptions()
options.Cert = "CERT"
policy := &Policy{
SetResponseHeaders: map[string]string{"X-Frame-Options": "DENY"},
}
assert.Equal(t, map[string]string{
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
"X-Frame-Options": "DENY",
"X-XSS-Protection": "1; mode=block",
}, options.GetSetResponseHeadersForPolicy(policy))
})
t.Run("multiple policies", func(t *testing.T) {
options := NewDefaultOptions()
options.SetResponseHeaders = map[string]string{"global": "foo"}
p1 := &Policy{
SetResponseHeaders: map[string]string{"route-1": "bar"},
}
p2 := &Policy{
SetResponseHeaders: map[string]string{"route-2": "baz"},
}
assert.Equal(t, map[string]string{
"global": "foo",
"route-1": "bar",
}, options.GetSetResponseHeadersForPolicy(p1))
assert.Equal(t, map[string]string{
"global": "foo",
"route-2": "baz",
}, options.GetSetResponseHeadersForPolicy(p2))
assert.Equal(t, map[string]string{"global": "foo"}, options.GetSetResponseHeaders())
})
}

func TestOptions_GetSharedKey(t *testing.T) {
Expand Down

0 comments on commit 5a73526

Please sign in to comment.