Commit cfdef66f by Torkel Ödegaard Committed by GitHub

Merge pull request #14984 from grafana/4117-xss-sanitize-text-panels

XSS sanitize text panels
parents bc7842e3 08dc5a4f
...@@ -570,6 +570,7 @@ callback_url = ...@@ -570,6 +570,7 @@ callback_url =
[panels] [panels]
enable_alpha = false enable_alpha = false
disable_sanitize_input = false
[enterprise] [enterprise]
license_path = license_path =
...@@ -188,7 +188,8 @@ ...@@ -188,7 +188,8 @@
"slate-react": "^0.12.4", "slate-react": "^0.12.4",
"tether": "^1.4.0", "tether": "^1.4.0",
"tether-drop": "https://github.com/torkelo/drop/tarball/master", "tether-drop": "https://github.com/torkelo/drop/tarball/master",
"tinycolor2": "^1.4.1" "tinycolor2": "^1.4.1",
"xss": "^1.0.3"
}, },
"resolutions": { "resolutions": {
"caniuse-db": "1.0.30000772", "caniuse-db": "1.0.30000772",
......
...@@ -166,6 +166,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf ...@@ -166,6 +166,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf
"externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl, "externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl,
"externalUserMngLinkName": setting.ExternalUserMngLinkName, "externalUserMngLinkName": setting.ExternalUserMngLinkName,
"viewersCanEdit": setting.ViewersCanEdit, "viewersCanEdit": setting.ViewersCanEdit,
"disableSanitizeInput": hs.Cfg.DisableSanitizeInput,
"buildInfo": map[string]interface{}{ "buildInfo": map[string]interface{}{
"version": setting.BuildVersion, "version": setting.BuildVersion,
"commit": setting.BuildCommit, "commit": setting.BuildCommit,
......
...@@ -18,7 +18,7 @@ import ( ...@@ -18,7 +18,7 @@ import (
"github.com/go-macaron/session" "github.com/go-macaron/session"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
"gopkg.in/ini.v1" ini "gopkg.in/ini.v1"
) )
type Scheme string type Scheme string
...@@ -90,6 +90,7 @@ var ( ...@@ -90,6 +90,7 @@ var (
EmailCodeValidMinutes int EmailCodeValidMinutes int
DataProxyWhiteList map[string]bool DataProxyWhiteList map[string]bool
DisableBruteForceLoginProtection bool DisableBruteForceLoginProtection bool
DisableSanitizeInput bool
// Snapshots // Snapshots
ExternalSnapshotUrl string ExternalSnapshotUrl string
...@@ -222,6 +223,7 @@ type Cfg struct { ...@@ -222,6 +223,7 @@ type Cfg struct {
MetricsEndpointBasicAuthUsername string MetricsEndpointBasicAuthUsername string
MetricsEndpointBasicAuthPassword string MetricsEndpointBasicAuthPassword string
EnableAlphaPanels bool EnableAlphaPanels bool
DisableSanitizeInput bool
EnterpriseLicensePath string EnterpriseLicensePath string
} }
...@@ -709,6 +711,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { ...@@ -709,6 +711,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
panels := iniFile.Section("panels") panels := iniFile.Section("panels")
cfg.EnableAlphaPanels = panels.Key("enable_alpha").MustBool(false) cfg.EnableAlphaPanels = panels.Key("enable_alpha").MustBool(false)
cfg.DisableSanitizeInput = panels.Key("sanitize_input_disabled").MustBool(false)
cfg.readSessionConfig() cfg.readSessionConfig()
cfg.readSmtpSettings() cfg.readSmtpSettings()
......
...@@ -35,8 +35,9 @@ export class Settings { ...@@ -35,8 +35,9 @@ export class Settings {
loginHint: any; loginHint: any;
loginError: any; loginError: any;
viewersCanEdit: boolean; viewersCanEdit: boolean;
disableSanitizeInput: boolean;
constructor(options) { constructor(options: Settings) {
const defaults = { const defaults = {
datasources: {}, datasources: {},
windowTitlePrefix: 'Grafana - ', windowTitlePrefix: 'Grafana - ',
...@@ -52,6 +53,7 @@ export class Settings { ...@@ -52,6 +53,7 @@ export class Settings {
isEnterprise: false, isEnterprise: false,
}, },
viewersCanEdit: false, viewersCanEdit: false,
disableSanitizeInput: false
}; };
_.extend(this, defaults, options); _.extend(this, defaults, options);
......
import { TextMatch } from 'app/types/explore'; import { TextMatch } from 'app/types/explore';
import xss from 'xss';
/** /**
* Adapt findMatchesInText for react-highlight-words findChunks handler. * Adapt findMatchesInText for react-highlight-words findChunks handler.
...@@ -22,7 +23,7 @@ export function findMatchesInText(haystack: string, needle: string): TextMatch[] ...@@ -22,7 +23,7 @@ export function findMatchesInText(haystack: string, needle: string): TextMatch[]
} }
const matches = []; const matches = [];
const cleaned = cleanNeedle(needle); const cleaned = cleanNeedle(needle);
let regexp; let regexp: RegExp;
try { try {
regexp = new RegExp(`(?:${cleaned})`, 'g'); regexp = new RegExp(`(?:${cleaned})`, 'g');
} catch (error) { } catch (error) {
...@@ -42,3 +43,12 @@ export function findMatchesInText(haystack: string, needle: string): TextMatch[] ...@@ -42,3 +43,12 @@ export function findMatchesInText(haystack: string, needle: string): TextMatch[]
}); });
return matches; return matches;
} }
export function sanitize (unsanitizedString: string): string {
try {
return xss(unsanitizedString);
} catch (error) {
console.log('String could not be sanitized', unsanitizedString);
return unsanitizedString;
}
}
import _ from 'lodash'; import _ from 'lodash';
import { PanelCtrl } from 'app/plugins/sdk'; import { PanelCtrl } from 'app/plugins/sdk';
import Remarkable from 'remarkable'; import Remarkable from 'remarkable';
import { sanitize } from 'app/core/utils/text';
import config from 'app/core/config';
const defaultContent = ` const defaultContent = `
# Title # Title
...@@ -33,11 +35,19 @@ export class TextPanelCtrl extends PanelCtrl { ...@@ -33,11 +35,19 @@ export class TextPanelCtrl extends PanelCtrl {
this.events.on('refresh', this.onRefresh.bind(this)); this.events.on('refresh', this.onRefresh.bind(this));
this.events.on('render', this.onRender.bind(this)); this.events.on('render', this.onRender.bind(this));
const renderWhenChanged = (scope: any) => {
const { panel } = scope.ctrl;
return [
panel.content,
panel.mode
].join();
};
$scope.$watch( $scope.$watch(
'ctrl.panel.content', renderWhenChanged,
_.throttle(() => { _.throttle(() => {
this.render(); this.render();
}, 1000) }, 100)
); );
} }
...@@ -62,7 +72,7 @@ export class TextPanelCtrl extends PanelCtrl { ...@@ -62,7 +72,7 @@ export class TextPanelCtrl extends PanelCtrl {
this.renderingCompleted(); this.renderingCompleted();
} }
renderText(content) { renderText(content: string) {
content = content content = content
.replace(/&/g, '&') .replace(/&/g, '&')
.replace(/>/g, '>') .replace(/>/g, '>')
...@@ -71,7 +81,7 @@ export class TextPanelCtrl extends PanelCtrl { ...@@ -71,7 +81,7 @@ export class TextPanelCtrl extends PanelCtrl {
this.updateContent(content); this.updateContent(content);
} }
renderMarkdown(content) { renderMarkdown(content: string) {
if (!this.remarkable) { if (!this.remarkable) {
this.remarkable = new Remarkable(); this.remarkable = new Remarkable();
} }
...@@ -81,7 +91,9 @@ export class TextPanelCtrl extends PanelCtrl { ...@@ -81,7 +91,9 @@ export class TextPanelCtrl extends PanelCtrl {
}); });
} }
updateContent(html) { updateContent(html: string) {
const { disableSanitizeInput } = config;
html = disableSanitizeInput ? html : sanitize(html);
try { try {
this.content = this.$sce.trustAsHtml(this.templateSrv.replace(html, this.panel.scopedVars)); this.content = this.$sce.trustAsHtml(this.templateSrv.replace(html, this.panel.scopedVars));
} catch (e) { } catch (e) {
......
...@@ -3560,6 +3560,11 @@ cssesc@^0.1.0: ...@@ -3560,6 +3560,11 @@ cssesc@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
cssfilter@0.0.10:
version "0.0.10"
resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae"
integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=
cssnano@^3.10.0: cssnano@^3.10.0:
version "3.10.0" version "3.10.0"
resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
...@@ -13344,6 +13349,14 @@ xregexp@4.0.0: ...@@ -13344,6 +13349,14 @@ xregexp@4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
xss@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.3.tgz#d04bd2558fd6c29c46113824d5e8b2a910054e23"
integrity sha512-LTpz3jXPLUphMMmyufoZRSKnqMj41OVypZ8uYGzvjkMV9C1EdACrhQl/EM8Qfh5htSAuMIQFOejmKAZGkJfaCg==
dependencies:
commander "^2.9.0"
cssfilter "0.0.10"
xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment