diff --git a/site/site.go b/site/site.go
index f838e2b61645e..a8d1147381243 100644
--- a/site/site.go
+++ b/site/site.go
@@ -256,48 +256,70 @@ const (
CSPFrameAncestors = "frame-ancestors"
)
+func cspHeaders(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Content-Security-Policy disables loading certain content types and can prevent XSS injections.
+ // This site helps eval your policy for syntax and other common issues: https://csp-evaluator.withgoogle.com/
+ // If we ever want to render something like a PDF, we need to adjust "object-src"
+ //
+ // The list of CSP options: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src
+ cspSrcs := CSPDirectives{
+ // All omitted fetch csp srcs default to this.
+ CSPDirectiveDefaultSrc: {"'self'"},
+ CSPDirectiveConnectSrc: {"'self'"},
+ CSPDirectiveChildSrc: {"'self'"},
+ CSPDirectiveScriptSrc: {"'self'"},
+ CSPDirectiveFontSrc: {"'self'"},
+ CSPDirectiveStyleSrc: {"'self' 'unsafe-inline'"},
+ // object-src is needed to support code-server
+ CSPDirectiveObjectSrc: {"'self'"},
+ // blob: for loading the pwa manifest for code-server
+ CSPDirectiveManifestSrc: {"'self' blob:"},
+ CSPDirectiveFrameSrc: {"'self'"},
+ // data: for loading base64 encoded icons for generic applications.
+ // https: allows loading images from external sources. This is not ideal
+ // but is required for the templates page that renders readmes.
+ // We should find a better solution in the future.
+ CSPDirectiveImgSrc: {"'self' data:"},
+ CSPDirectiveFormAction: {"'self'"},
+ CSPDirectiveMediaSrc: {"'self'"},
+ // Report all violations back to the server to log
+ CSPDirectiveReportURI: {"/api/v2/csp/reports"},
+ CSPFrameAncestors: {"'none'"},
+
+ // Only scripts can manipulate the dom. This prevents someone from
+ // naming themselves something like ''.
+ // "require-trusted-types-for" : []string{"'script'"},
+ }
+
+ // This extra connect-src addition is required to support old webkit
+ // based browsers (Safari).
+ // See issue: https://github.com/w3c/webappsec-csp/issues/7
+ // Once webkit browsers support 'self' on connect-src, we can remove this.
+ // When we remove this, the csp header can be static, as opposed to being
+ // dynamically generated for each request.
+ host := r.Host
+ // It is important r.Host is not an empty string.
+ if host != "" {
+ // We can add both ws:// and wss:// as browsers do not let https
+ // pages to connect to non-tls websocket connections. So this
+ // supports both http & https webpages.
+ cspSrcs.Append(CSPDirectiveConnectSrc, fmt.Sprintf("wss://%[1]s ws://%[1]s", host))
+ }
+
+ var csp strings.Builder
+ for src, vals := range cspSrcs {
+ _, _ = fmt.Fprintf(&csp, "%s %s; ", src, strings.Join(vals, " "))
+ }
+
+ w.Header().Set("Content-Security-Policy", csp.String())
+ next.ServeHTTP(w, r)
+ })
+}
+
// secureHeaders is only needed for statically served files. We do not need this for api endpoints.
// It adds various headers to enforce browser security features.
func secureHeaders(next http.Handler) http.Handler {
- // Content-Security-Policy disables loading certain content types and can prevent XSS injections.
- // This site helps eval your policy for syntax and other common issues: https://csp-evaluator.withgoogle.com/
- // If we ever want to render something like a PDF, we need to adjust "object-src"
- //
- // The list of CSP options: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src
- cspSrcs := CSPDirectives{
- // All omitted fetch csp srcs default to this.
- CSPDirectiveDefaultSrc: {"'self'"},
- CSPDirectiveConnectSrc: {"'self' ws: wss:"},
- CSPDirectiveChildSrc: {"'self'"},
- CSPDirectiveScriptSrc: {"'self'"},
- CSPDirectiveFontSrc: {"'self'"},
- CSPDirectiveStyleSrc: {"'self' 'unsafe-inline'"},
- // object-src is needed to support code-server
- CSPDirectiveObjectSrc: {"'self'"},
- // blob: for loading the pwa manifest for code-server
- CSPDirectiveManifestSrc: {"'self' blob:"},
- CSPDirectiveFrameSrc: {"'self'"},
- // data: for loading base64 encoded icons for generic applications.
- // https: allows loading images from external sources. This is not ideal
- // but is required for the templates page that renders readmes.
- // We should find a better solution in the future.
- CSPDirectiveImgSrc: {"'self' https: https://cdn.coder.com data:"},
- CSPDirectiveFormAction: {"'self'"},
- CSPDirectiveMediaSrc: {"'self'"},
- // Report all violations back to the server to log
- CSPDirectiveReportURI: {"/api/v2/csp/reports"},
- CSPFrameAncestors: {"'none'"},
-
- // Only scripts can manipulate the dom. This prevents someone from
- // naming themselves something like ''.
- // "require-trusted-types-for" : []string{"'script'"},
- }
-
- var csp strings.Builder
- for src, vals := range cspSrcs {
- _, _ = fmt.Fprintf(&csp, "%s %s; ", src, strings.Join(vals, " "))
- }
-
// Permissions-Policy can be used to disabled various browser features that we do not use.
// This can prevent an embedded iframe from accessing these features.
// If we support arbitrary iframes such as generic applications, we might need to add permissions
@@ -322,15 +344,11 @@ func secureHeaders(next http.Handler) http.Handler {
}, ", ")
return secure.New(secure.Options{
- // Set to ContentSecurityPolicyReportOnly for testing, as all errors are printed to the console log
- // but are not enforced.
- ContentSecurityPolicy: csp.String(),
-
PermissionsPolicy: permissions,
// Prevent the browser from sending Referer header with requests
ReferrerPolicy: "no-referrer",
- }).Handler(next)
+ }).Handler(cspHeaders(next))
}
// htmlFiles recursively walks the file system passed finding all *.html files.