mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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
This commit is contained in:
parent
244d3f61c3
commit
8143991b94
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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, `<script nonce="[^"]+"`, html)
|
||||
})
|
||||
|
||||
|
@ -12,7 +12,7 @@ export function spectrumPicker() {
|
||||
require: 'ngModel',
|
||||
scope: true,
|
||||
replace: true,
|
||||
template: '<color-picker color="ngModel.$viewValue" onChange="onColorChange"></color-picker>',
|
||||
template: '<color-picker color="ngModel.$viewValue" on-change="onColorChange"></color-picker>',
|
||||
link: (scope: any, element: any, attrs: any, ngModel: any) => {
|
||||
scope.ngModel = ngModel;
|
||||
scope.onColorChange = (color: string) => {
|
||||
|
@ -17,7 +17,7 @@
|
||||
Unit
|
||||
<info-popover mode="right-normal">The default unit used when not defined by the datasource or in the Fields or Overrides configuration.</info-popover>
|
||||
</label>
|
||||
<unit-picker onChange="ctrl.setUnitFormat(yaxis)" value="yaxis.format" class="flex-grow-1" />
|
||||
<unit-picker on-change="ctrl.setUnitFormat(yaxis)" value="yaxis.format" class="flex-grow-1" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -9,7 +9,7 @@ const template = `
|
||||
<div ng-if="ctrl.contextMenuCtrl.isVisible">
|
||||
<graph-context-menu
|
||||
itemsGroup="ctrl.contextMenuCtrl.menuItemsSupplier()"
|
||||
onClose="ctrl.onContextMenuClose"
|
||||
on-close="ctrl.onContextMenuClose"
|
||||
getContextMenuSource="ctrl.contextMenuCtrl.getSource"
|
||||
timeZone="ctrl.getTimeZone()"
|
||||
x="ctrl.contextMenuCtrl.position.x"
|
||||
|
@ -54,7 +54,7 @@
|
||||
<div class="gf-form" ng-if="threshold.fill && threshold.colorMode === 'custom'">
|
||||
<label class="gf-form-label">Fill color</label>
|
||||
<span class="gf-form-label">
|
||||
<color-picker color="threshold.fillColor" onChange="ctrl.onFillColorChange($index)"></color-picker>
|
||||
<color-picker color="threshold.fillColor" on-change="ctrl.onFillColorChange($index)"></color-picker>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
<div class="gf-form" ng-if="threshold.line && threshold.colorMode === 'custom'">
|
||||
<label class="gf-form-label">Line color</label>
|
||||
<span class="gf-form-label">
|
||||
<color-picker color="threshold.lineColor" onChange="ctrl.onLineColorChange($index)"></color-picker>
|
||||
<color-picker color="threshold.lineColor" on-change="ctrl.onLineColorChange($index)"></color-picker>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -59,7 +59,7 @@
|
||||
<div class="gf-form" ng-if="timeRegion.fill && timeRegion.colorMode === 'custom'">
|
||||
<label class="gf-form-label">Fill color</label>
|
||||
<span class="gf-form-label">
|
||||
<color-picker color="timeRegion.fillColor" onChange="ctrl.onFillColorChange($index)"></color-picker>
|
||||
<color-picker color="timeRegion.fillColor" on-change="ctrl.onFillColorChange($index)"></color-picker>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -68,7 +68,7 @@
|
||||
<div class="gf-form" ng-if="timeRegion.line && timeRegion.colorMode === 'custom'">
|
||||
<label class="gf-form-label">Line color</label>
|
||||
<span class="gf-form-label">
|
||||
<color-picker color="timeRegion.lineColor" onChange="ctrl.onLineColorChange($index)"></color-picker>
|
||||
<color-picker color="timeRegion.lineColor" on-change="ctrl.onLineColorChange($index)"></color-picker>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
<h5 class="section-heading">Y Axis</h5>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-8">Unit</label>
|
||||
<unit-picker onChange="editor.setUnitFormat" value="ctrl.panel.yAxis.format" class="width-12"></unit-picker>
|
||||
<unit-picker on-change="editor.setUnitFormat" value="ctrl.panel.yAxis.format" class="width-12"></unit-picker>
|
||||
</div>
|
||||
<div ng-if="ctrl.panel.dataFormat == 'timeseries'">
|
||||
<div class="gf-form">
|
||||
|
@ -12,7 +12,7 @@
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-9">Color</label>
|
||||
<span class="gf-form-label">
|
||||
<color-picker color="ctrl.panel.color.cardColor" onChange="ctrl.onCardColorChange"></color-picker>
|
||||
<color-picker color="ctrl.panel.color.cardColor" on-change="ctrl.onCardColorChange"></color-picker>
|
||||
</span>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
|
@ -106,7 +106,7 @@
|
||||
<div ng-if="style.type === 'number'">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-8">Unit</label>
|
||||
<unit-picker onChange="editor.setUnitFormat(style)" value="style.unit" width="16"></unit-picker>
|
||||
<unit-picker on-change="editor.setUnitFormat(style)" value="style.unit" width="16"></unit-picker>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-8">Decimals</label>
|
||||
@ -220,13 +220,13 @@
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-8">Colors</label>
|
||||
<span class="gf-form-label">
|
||||
<color-picker color="style.colors[0]" onChange="editor.onColorChange(style, 0)"></color-picker>
|
||||
<color-picker color="style.colors[0]" on-change="editor.onColorChange(style, 0)"></color-picker>
|
||||
</span>
|
||||
<span class="gf-form-label">
|
||||
<color-picker color="style.colors[1]" onChange="editor.onColorChange(style, 1)"></color-picker>
|
||||
<color-picker color="style.colors[1]" on-change="editor.onColorChange(style, 1)"></color-picker>
|
||||
</span>
|
||||
<span class="gf-form-label">
|
||||
<color-picker color="style.colors[2]" onChange="editor.onColorChange(style, 2)"></color-picker>
|
||||
<color-picker color="style.colors[2]" on-change="editor.onColorChange(style, 2)"></color-picker>
|
||||
</span>
|
||||
<div class="gf-form-label">
|
||||
<a class="pointer" ng-click="editor.invertColorOrder($index)">Invert</a>
|
||||
|
Binary file not shown.
@ -262,8 +262,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v13/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2)
|
||||
format('woff2');
|
||||
src: url(../fonts/roboto/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
|
||||
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@ -274,8 +273,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v13/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2)
|
||||
format('woff2');
|
||||
src: url(../fonts/roboto/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0mQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
|
||||
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user