From 00454b32f5db19f9dee4fa05b5cff5e24941548d Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 8 May 2018 12:28:16 +0300 Subject: [PATCH] fix XSS vulnerabilities in dashboard links (#11813) fix XSS vulnerabilities in dashboard links --- public/app/features/dashlinks/module.ts | 17 ++++++++++++++--- public/app/features/panel/panel_ctrl.ts | 4 +++- public/test/specs/helpers.ts | 24 +++++++++++++----------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/public/app/features/dashlinks/module.ts b/public/app/features/dashlinks/module.ts index 8fa110fe6b4..148d32f4399 100644 --- a/public/app/features/dashlinks/module.ts +++ b/public/app/features/dashlinks/module.ts @@ -15,7 +15,7 @@ function dashLinksContainer() { } /** @ngInject */ -function dashLink($compile, linkSrv) { +function dashLink($compile, $sanitize, linkSrv) { return { restrict: 'E', link: function(scope, elem) { @@ -49,10 +49,21 @@ function dashLink($compile, linkSrv) { var linkInfo = linkSrv.getAnchorInfo(link); span.text(linkInfo.title); anchor.attr('href', linkInfo.href); + sanitizeAnchor(); + + // tooltip + elem.find('a').tooltip({ + title: $sanitize(scope.link.tooltip), + html: true, + container: 'body', + }); + } + + function sanitizeAnchor() { + const anchorSanitized = $sanitize(anchor.parent().html()); + anchor.parent().html(anchorSanitized); } - // tooltip - elem.find('a').tooltip({ title: scope.link.tooltip, html: true, container: 'body' }); icon.attr('class', 'fa fa-fw ' + scope.link.icon); anchor.attr('target', scope.link.target); diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 67725ec5fec..6402227164f 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -324,6 +324,7 @@ export class PanelCtrl { } var linkSrv = this.$injector.get('linkSrv'); + var sanitize = this.$injector.get('$sanitize'); var templateSrv = this.$injector.get('templateSrv'); var interpolatedMarkdown = templateSrv.replace(markdown, this.panel.scopedVars); var html = '
'; @@ -346,7 +347,8 @@ export class PanelCtrl { html += ''; } - return html + '
'; + html += ''; + return sanitize(html); } openInspector() { diff --git a/public/test/specs/helpers.ts b/public/test/specs/helpers.ts index 8e83915362f..ab7fbb5698b 100644 --- a/public/test/specs/helpers.ts +++ b/public/test/specs/helpers.ts @@ -1,14 +1,15 @@ import _ from 'lodash'; import config from 'app/core/config'; import * as dateMath from 'app/core/utils/datemath'; -import {angularMocks, sinon} from '../lib/common'; -import {PanelModel} from 'app/features/dashboard/panel_model'; +import { angularMocks, sinon } from '../lib/common'; +import { PanelModel } from 'app/features/dashboard/panel_model'; export function ControllerTestContext() { var self = this; this.datasource = {}; this.$element = {}; + this.$sanitize = {}; this.annotationsSrv = {}; this.timeSrv = new TimeSrvStub(); this.templateSrv = new TemplateSrvStub(); @@ -30,6 +31,7 @@ export function ControllerTestContext() { $provide.value('timeSrv', self.timeSrv); $provide.value('templateSrv', self.templateSrv); $provide.value('$element', self.$element); + $provide.value('$sanitize', self.$sanitize); _.each(mocks, function(value, key) { $provide.value(key, value); }); @@ -42,8 +44,8 @@ export function ControllerTestContext() { self.$location = $location; self.$browser = $browser; self.$q = $q; - self.panel = new PanelModel({type: 'test'}); - self.dashboard = {meta: {}}; + self.panel = new PanelModel({ type: 'test' }); + self.dashboard = { meta: {} }; $rootScope.appEvent = sinon.spy(); $rootScope.onAppEvent = sinon.spy(); @@ -53,14 +55,14 @@ export function ControllerTestContext() { $rootScope.colors.push('#' + i); } - config.panels['test'] = {info: {}}; + config.panels['test'] = { info: {} }; self.ctrl = $controller( Ctrl, - {$scope: self.scope}, + { $scope: self.scope }, { panel: self.panel, dashboard: self.dashboard, - }, + } ); }); }; @@ -72,7 +74,7 @@ export function ControllerTestContext() { self.$browser = $browser; self.scope.contextSrv = {}; self.scope.panel = {}; - self.scope.dashboard = {meta: {}}; + self.scope.dashboard = { meta: {} }; self.scope.dashboardMeta = {}; self.scope.dashboardViewState = new DashboardViewStateStub(); self.scope.appEvent = sinon.spy(); @@ -131,7 +133,7 @@ export function DashboardViewStateStub() { export function TimeSrvStub() { this.init = sinon.spy(); - this.time = {from: 'now-1h', to: 'now'}; + this.time = { from: 'now-1h', to: 'now' }; this.timeRange = function(parse) { if (parse === false) { return this.time; @@ -159,7 +161,7 @@ export function ContextSrvStub() { export function TemplateSrvStub() { this.variables = []; - this.templateSettings = {interpolate: /\[\[([\s\S]+?)\]\]/g}; + this.templateSettings = { interpolate: /\[\[([\s\S]+?)\]\]/g }; this.data = {}; this.replace = function(text) { return _.template(text, this.templateSettings)(this.data); @@ -188,7 +190,7 @@ var allDeps = { TimeSrvStub: TimeSrvStub, ControllerTestContext: ControllerTestContext, ServiceTestContext: ServiceTestContext, - DashboardViewStateStub: DashboardViewStateStub + DashboardViewStateStub: DashboardViewStateStub, }; // for legacy