Skip to content

Commit

Permalink
feat(secure): support Content-Security-Policy-Report-Only header
Browse files Browse the repository at this point in the history
Closes #1283
  • Loading branch information
Kumar Harsh committed Feb 27, 2019
1 parent be919e8 commit 1881ca4
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 7 deletions.
13 changes: 7 additions & 6 deletions echo.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,13 @@ const (
HeaderAccessControlMaxAge = "Access-Control-Max-Age"

// Security
HeaderStrictTransportSecurity = "Strict-Transport-Security"
HeaderXContentTypeOptions = "X-Content-Type-Options"
HeaderXXSSProtection = "X-XSS-Protection"
HeaderXFrameOptions = "X-Frame-Options"
HeaderContentSecurityPolicy = "Content-Security-Policy"
HeaderXCSRFToken = "X-CSRF-Token"
HeaderStrictTransportSecurity = "Strict-Transport-Security"
HeaderXContentTypeOptions = "X-Content-Type-Options"
HeaderXXSSProtection = "X-XSS-Protection"
HeaderXFrameOptions = "X-Frame-Options"
HeaderContentSecurityPolicy = "Content-Security-Policy"
HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
HeaderXCSRFToken = "X-CSRF-Token"
)

const (
Expand Down
13 changes: 12 additions & 1 deletion middleware/secure.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ type (
// trusted web page context.
// Optional. Default value "".
ContentSecurityPolicy string `yaml:"content_security_policy"`

// CSPReportOnly would use the `Content-Security-Policy-Report-Only` header instead
// of the `Content-Security-Policy` header. This allows iterative updates of the
// content security policy by only reporting the violations that would
// have occurred instead of blocking the resource.
// Optional. Default value false.
CSPReportOnly bool `yaml:"csp_report_only"`
}
)

Expand Down Expand Up @@ -108,7 +115,11 @@ func SecureWithConfig(config SecureConfig) echo.MiddlewareFunc {
res.Header().Set(echo.HeaderStrictTransportSecurity, fmt.Sprintf("max-age=%d%s", config.HSTSMaxAge, subdomains))
}
if config.ContentSecurityPolicy != "" {
res.Header().Set(echo.HeaderContentSecurityPolicy, config.ContentSecurityPolicy)
if config.CSPReportOnly {
res.Header().Set(echo.HeaderContentSecurityPolicyReportOnly, config.ContentSecurityPolicy)
} else {
res.Header().Set(echo.HeaderContentSecurityPolicy, config.ContentSecurityPolicy)
}
}
return next(c)
}
Expand Down
20 changes: 20 additions & 0 deletions middleware/secure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,24 @@ func TestSecure(t *testing.T) {
assert.Equal(t, "", rec.Header().Get(echo.HeaderXFrameOptions))
assert.Equal(t, "max-age=3600; includeSubdomains", rec.Header().Get(echo.HeaderStrictTransportSecurity))
assert.Equal(t, "default-src 'self'", rec.Header().Get(echo.HeaderContentSecurityPolicy))
assert.Equal(t, "", rec.Header().Get(echo.HeaderContentSecurityPolicyReportOnly))

// Custom with CSPReportOnly flag
req.Header.Set(echo.HeaderXForwardedProto, "https")
rec = httptest.NewRecorder()
c = e.NewContext(req, rec)
SecureWithConfig(SecureConfig{
XSSProtection: "",
ContentTypeNosniff: "",
XFrameOptions: "",
HSTSMaxAge: 3600,
ContentSecurityPolicy: "default-src 'self'",
CSPReportOnly: true,
})(h)(c)
assert.Equal(t, "", rec.Header().Get(echo.HeaderXXSSProtection))
assert.Equal(t, "", rec.Header().Get(echo.HeaderXContentTypeOptions))
assert.Equal(t, "", rec.Header().Get(echo.HeaderXFrameOptions))
assert.Equal(t, "max-age=3600; includeSubdomains", rec.Header().Get(echo.HeaderStrictTransportSecurity))
assert.Equal(t, "default-src 'self'", rec.Header().Get(echo.HeaderContentSecurityPolicyReportOnly))
assert.Equal(t, "", rec.Header().Get(echo.HeaderContentSecurityPolicy))
}

0 comments on commit 1881ca4

Please sign in to comment.