Skip to content

Commit 7164744

Browse files
dzbarskyunrolled
authored andcommitted
Allow using nonce with report-only policy (#54)
* Allow using nonce with report-only policy * Add tests
1 parent 4e32686 commit 7164744

File tree

3 files changed

+39
-24
lines changed

3 files changed

+39
-24
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ _testmain.go
2424

2525
*.pem
2626
.DS_Store
27+
*.swp

csp_test.go

+36-23
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,42 @@ var cspHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1515
})
1616

1717
func TestCSPNonce(t *testing.T) {
18-
s := New(Options{
19-
ContentSecurityPolicy: "default-src 'self' $NONCE; script-src 'strict-dynamic' $NONCE",
20-
})
21-
22-
res := httptest.NewRecorder()
23-
req, _ := http.NewRequest("GET", "/foo", nil)
24-
25-
s.Handler(cspHandler).ServeHTTP(res, req)
26-
27-
expect(t, res.Code, http.StatusOK)
28-
29-
csp := res.Header().Get("Content-Security-Policy")
30-
expect(t, strings.Count(csp, "'nonce-"), 2)
31-
32-
nonce := strings.Split(strings.Split(csp, "'")[3], "-")[1]
33-
// Test that the context has the CSP nonce, but only during the request.
34-
expect(t, res.Body.String(), nonce)
35-
expect(t, CSPNonce(req.Context()), "")
36-
37-
_, err := base64.RawStdEncoding.DecodeString(nonce)
38-
expect(t, err, nil)
39-
40-
expect(t, csp, fmt.Sprintf("default-src 'self' 'nonce-%[1]s'; script-src 'strict-dynamic' 'nonce-%[1]s'", nonce))
18+
csp := "default-src 'self' $NONCE; script-src 'strict-dynamic' $NONCE"
19+
cases := []struct {
20+
options Options
21+
headers []string
22+
}{
23+
{Options{ContentSecurityPolicy: csp}, []string{"Content-Security-Policy"}},
24+
{Options{ContentSecurityPolicyReportOnly: csp}, []string{"Content-Security-Policy-Report-Only"}},
25+
{Options{ContentSecurityPolicy: csp, ContentSecurityPolicyReportOnly: csp},
26+
[]string{"Content-Security-Policy", "Content-Security-Policy-Report-Only"}},
27+
}
28+
29+
for _, c := range cases {
30+
s := New(c.options)
31+
32+
res := httptest.NewRecorder()
33+
req, _ := http.NewRequest("GET", "/foo", nil)
34+
35+
s.Handler(cspHandler).ServeHTTP(res, req)
36+
37+
expect(t, res.Code, http.StatusOK)
38+
39+
for _, header := range c.headers {
40+
csp := res.Header().Get(header)
41+
expect(t, strings.Count(csp, "'nonce-"), 2)
42+
43+
nonce := strings.Split(strings.Split(csp, "'")[3], "-")[1]
44+
// Test that the context has the CSP nonce, but only during the request.
45+
expect(t, res.Body.String(), nonce)
46+
expect(t, CSPNonce(req.Context()), "")
47+
48+
_, err := base64.RawStdEncoding.DecodeString(nonce)
49+
expect(t, err, nil)
50+
51+
expect(t, csp, fmt.Sprintf("default-src 'self' 'nonce-%[1]s'; script-src 'strict-dynamic' 'nonce-%[1]s'", nonce))
52+
}
53+
}
4154
}
4255

4356
func TestWithCSPNonce(t *testing.T) {

secure.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,9 @@ func New(options ...Options) *Secure {
123123
}
124124

125125
o.ContentSecurityPolicy = strings.Replace(o.ContentSecurityPolicy, "$NONCE", "'nonce-%[1]s'", -1)
126+
o.ContentSecurityPolicyReportOnly = strings.Replace(o.ContentSecurityPolicyReportOnly, "$NONCE", "'nonce-%[1]s'", -1)
126127

127-
o.nonceEnabled = strings.Contains(o.ContentSecurityPolicy, "%[1]s")
128+
o.nonceEnabled = strings.Contains(o.ContentSecurityPolicy, "%[1]s") || strings.Contains(o.ContentSecurityPolicyReportOnly, "%[1]s")
128129

129130
s := &Secure{
130131
opt: o,

0 commit comments

Comments
 (0)