From 8143991b9476bbc3a1e92636f01a9ba9c4a80a65 Mon Sep 17 00:00:00 2001 From: kay delaney <45561153+kaydelaney@users.noreply.github.com> Date: Fri, 28 May 2021 16:01:10 +0100 Subject: [PATCH] Security: Update default CSP template and fix firefox CSP issues (#34836) * Security: Update default content_security_policy_template - Add 'strict-dynamic' back to script-src - Add ws(s)://$ROOT_PATH to connect-src - Change onEvent to on-event in angular templates to fix CSP issues in firefox. - Add blob: to style-src --- conf/defaults.ini | 3 ++- conf/sample.ini | 3 ++- pkg/middleware/csp.go | 5 +++++ pkg/setting/setting_test.go | 2 +- pkg/tests/web/index_view_test.go | 3 +-- .../components/colorpicker/spectrum_picker.ts | 2 +- public/app/plugins/panel/graph/axes_editor.html | 2 +- public/app/plugins/panel/graph/template.ts | 2 +- .../plugins/panel/graph/thresholds_form.html | 4 ++-- .../plugins/panel/graph/time_regions_form.html | 4 ++-- .../panel/heatmap/partials/axes_editor.html | 2 +- .../panel/heatmap/partials/display_editor.html | 2 +- .../plugins/panel/table-old/column_options.html | 8 ++++---- ...g45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2 | Bin 0 -> 22196 bytes public/sass/base/_fonts.scss | 6 ++---- 15 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 public/fonts/roboto/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2 diff --git a/conf/defaults.ini b/conf/defaults.ini index 6e0a7a6c41a..8699ea11e90 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -255,7 +255,8 @@ content_security_policy = false # Set Content Security Policy template used when adding the Content-Security-Policy header to your requests. # $NONCE in the template includes a random nonce. -content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline';object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline';img-src * data:;base-uri 'self';connect-src 'self' grafana.com;manifest-src 'self';media-src 'none';form-action 'self';""" +# $ROOT_PATH is server.root_url without the protocol. +content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';""" #################################### Snapshots ########################### [snapshots] diff --git a/conf/sample.ini b/conf/sample.ini index 78e4db378de..7b66c7f141e 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -261,7 +261,8 @@ # Set Content Security Policy template used when adding the Content-Security-Policy header to your requests. # $NONCE in the template includes a random nonce. -;content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline';object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline';img-src * data:;base-uri 'self';connect-src 'self' grafana.com;manifest-src 'self';media-src 'none';form-action 'self';""" +# $ROOT_PATH is server.root_url without the protocol. +;content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';""" #################################### Snapshots ########################### [snapshots] diff --git a/pkg/middleware/csp.go b/pkg/middleware/csp.go index 46acb5a8ce5..3fff0cbe04f 100644 --- a/pkg/middleware/csp.go +++ b/pkg/middleware/csp.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "regexp" "strings" "github.com/grafana/grafana/pkg/infra/log" @@ -42,6 +43,10 @@ func AddCSPHeader(cfg *setting.Cfg, logger log.Logger) macaron.Handler { nonce := base64.RawStdEncoding.EncodeToString(buf[:]) val := strings.ReplaceAll(cfg.CSPTemplate, "$NONCE", fmt.Sprintf("'nonce-%s'", nonce)) + + re := regexp.MustCompile(`^\w+:(//)?`) + rootPath := re.ReplaceAllString(cfg.AppURL, "") + val = strings.ReplaceAll(val, "$ROOT_PATH", rootPath) w.Header().Set("Content-Security-Policy", val) ctx.RequestNonce = nonce logger.Debug("Successfully generated CSP nonce", "nonce", nonce) diff --git a/pkg/setting/setting_test.go b/pkg/setting/setting_test.go index aa52cb60a14..9a3d5ae6d35 100644 --- a/pkg/setting/setting_test.go +++ b/pkg/setting/setting_test.go @@ -28,7 +28,7 @@ func TestLoadingSettings(t *testing.T) { Convey("Given the default ini files", func() { cfg := NewCfg() - err := cfg.Load(&CommandLineArgs{HomePath: "../../"}) + err := cfg.Load(&CommandLineArgs{HomePath: "../../", Config: "../../conf/defaults.ini"}) So(err, ShouldBeNil) So(cfg.AdminUser, ShouldEqual, "admin") diff --git a/pkg/tests/web/index_view_test.go b/pkg/tests/web/index_view_test.go index 0f6086016aa..8c4af6aa641 100644 --- a/pkg/tests/web/index_view_test.go +++ b/pkg/tests/web/index_view_test.go @@ -23,8 +23,7 @@ func TestIndexView(t *testing.T) { // nolint:bodyclose resp, html := makeRequest(t, addr) - - assert.Regexp(t, `script-src 'self' 'unsafe-eval' 'unsafe-inline';object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline';img-src \* data:;base-uri 'self';connect-src 'self' grafana.com;manifest-src 'self';media-src 'none';form-action 'self';`, resp.Header.Get("Content-Security-Policy")) + assert.Regexp(t, `script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' 'nonce-[^']+';object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src \* data:;base-uri 'self';connect-src 'self' grafana.com ws://localhost:3000/ wss://localhost:3000/;manifest-src 'self';media-src 'none';form-action 'self';`, resp.Header.Get("Content-Security-Policy")) assert.Regexp(t, `