internal/middleware: simplify content security policy
Also, remove unnecessary `policy` type.
Updates b/159711607.
Change-Id: Iff8ef753b023c1c5f5778989273d9e6943356008
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/239678
Reviewed-by: Roberto Clapis <robclap8@gmail.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/middleware/secureheaders.go b/internal/middleware/secureheaders.go
index 0eed58c..959c36b 100644
--- a/internal/middleware/secureheaders.go
+++ b/internal/middleware/secureheaders.go
@@ -17,91 +17,27 @@
// It is substituted for the actual nonce value by the SecureHeaders middleware.
const NoncePlaceholder = "$$GODISCOVERYNONCE$$"
-// policy is a helper for constructing content security policies.
-type policy struct {
- directives []string
-}
-
-// add appends a new directive to the policy. Neither name nor values are
-// validated, and no checking is performed that the new directive is unique.
-func (p *policy) add(name string, values ...string) {
- d := name + " " + strings.Join(values, " ")
- p.directives = append(p.directives, d)
-}
-
-// serialize serializes the policy for use in the Content-Security-Policy
-// header.
-func (p *policy) serialize() string {
- return strings.Join(p.directives, "; ")
-}
-
// SecureHeaders adds a content-security-policy and other security-related
// headers to all responses.
func SecureHeaders() Middleware {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- useStrictDynamic := r.FormValue("csp-strict-dynamic") == "true"
- // These are special keywords within the CSP spec
- // https://www.w3.org/TR/CSP3/#framework-directive-source-list
- const (
- self = "'self'"
- none = "'none'"
- )
-
- var p policy
-
- // Set a strict fallback for content sources.
- p.add("default-src", self)
-
- // Allow known sources for fonts.
- p.add("font-src", self, "fonts.googleapis.com", "fonts.gstatic.com")
-
- // fonts.googleapis.com is used for fonts.
- // tagmanager.google.com is used for debugging Google Tag Manager.
- p.add("style-src", self, "'unsafe-inline'", "fonts.googleapis.com", "tagmanager.google.com")
-
- // Because we are rendering user-provided README's, we allow arbitrary image
- // sources. This could possibly be narrowed to known content hosts based on
- // e.g. the github.com CSP, but that seemed fragile.
- p.add("img-src", self, "data:", "*")
-
- // Disallow plugin content: the Discovery site does not use it.
- p.add("object-src", none)
-
- // Disallow <base> URIs, which prevents attackers from changing the
- // locations of scripts loaded from relative URLs. The site doesn’t have
- // a <base> tag anyway.
- p.add("base-uri", none)
-
nonce, err := generateNonce()
if err != nil {
log.Infof(r.Context(), "generateNonce(): %v", err)
}
+ csp := []string{
+ // Disallow plugin content: pkg.go.dev does not use it.
+ "object-src 'none'",
+ // Disallow <base> URIs, which prevents attackers from changing the
+ // locations of scripts loaded from relative URLs. The site doesn’t have
+ // a <base> tag anyway.
+ "base-uri 'none'",
- scriptSrcs := []string{
- fmt.Sprintf("'nonce-%s'", nonce),
- "www.gstatic.com",
- "www.googletagmanager.com",
- "support.google.com",
+ fmt.Sprintf("script-src 'nonce-%s' 'unsafe-inline' 'strict-dynamic' https: http:",
+ nonce),
}
- if useStrictDynamic {
- scriptSrcs = append(scriptSrcs, "'strict-dynamic'")
- }
-
- p.add("script-src", scriptSrcs...)
-
- // Allow GA as a valid target for XHR, WebSockets, and EventSource.
- p.add("connect-src", self, "www.google-analytics.com")
-
- // Allow iframes that have a nonce, for Google Analytics when JavaScript
- // is disabled.
- p.add("frame-src", fmt.Sprintf("'nonce-%s'", nonce))
-
- // Don't allow parent framing.
- p.add("frame-ancestors", none)
-
- csp := p.serialize()
- w.Header().Set("Content-Security-Policy", csp)
+ w.Header().Set("Content-Security-Policy", strings.Join(csp, "; "))
// Don't allow frame embedding.
w.Header().Set("X-Frame-Options", "deny")
// Prevent MIME sniffing.
diff --git a/internal/middleware/secureheaders_test.go b/internal/middleware/secureheaders_test.go
index 46eb00e..d5bc075 100644
--- a/internal/middleware/secureheaders_test.go
+++ b/internal/middleware/secureheaders_test.go
@@ -13,16 +13,6 @@
"testing"
)
-func TestPolicySerialization(t *testing.T) {
- var p policy
- p.add("default-src", "'self'", "example.com")
- p.add("img-src", "*")
- want := "default-src 'self' example.com; img-src *"
- if got := p.serialize(); got != want {
- t.Errorf("p.serialize() = %s, want %s", got, want)
- }
-}
-
func TestSecureHeaders(t *testing.T) {
const origBody = `
<link foo>