diff --git a/public/app/plugins/panel/table/editor.html b/public/app/plugins/panel/table/editor.html index c9e0bad5d9a..9206795c0d8 100644 --- a/public/app/plugins/panel/table/editor.html +++ b/public/app/plugins/panel/table/editor.html @@ -103,6 +103,11 @@ +
@@ -158,6 +163,7 @@
+ diff --git a/public/app/plugins/panel/table/editor.ts b/public/app/plugins/panel/table/editor.ts index 65c8b51cab8..0a6f695d34b 100644 --- a/public/app/plugins/panel/table/editor.ts +++ b/public/app/plugins/panel/table/editor.ts @@ -112,6 +112,7 @@ export class TablePanelEditorCtrl { pattern: '/.*/', dateFormat: 'YYYY-MM-DD HH:mm:ss', thresholds: [], + escapeHtml: true }; this.panel.styles.push(angular.copy(columnStyleDefaults)); diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts index 755c7262070..42b9aeeb2d2 100644 --- a/public/app/plugins/panel/table/renderer.ts +++ b/public/app/plugins/panel/table/renderer.ts @@ -4,6 +4,8 @@ import _ from 'lodash'; import moment from 'moment'; import kbn from 'app/core/utils/kbn'; + + export class TableRenderer { formaters: any[]; colorState: any; @@ -24,22 +26,27 @@ export class TableRenderer { return _.first(style.colors); } - defaultCellFormater(v) { - if (v === null || v === void 0) { - return ''; - } + defaultCellFormater(escapeHtml = true) { + return function(v) { + if (v === null || v === void 0 || v === undefined) { + return ''; + } - if (_.isArray(v)) { - v = v.join(', '); - } + if (_.isArray(v)) { + v = v.join(', '); + } - return v; + if (_.isString(v) && escapeHtml) { + v = encodeHtml(v); + } + + return v; + }; } - createColumnFormater(style) { if (!style) { - return this.defaultCellFormater; + return this.defaultCellFormater(); } if (style.type === 'date') { @@ -62,7 +69,7 @@ export class TableRenderer { } if (_.isString(v)) { - return v; + return encodeHtml(v); } if (style.colorMode) { @@ -73,7 +80,11 @@ export class TableRenderer { }; } - return this.defaultCellFormater; + if (style.type === 'string') { + return this.defaultCellFormater(style.escapeHtml); + } + + return this.defaultCellFormater(); } formatColumnValue(colIndex, value) { @@ -91,7 +102,7 @@ export class TableRenderer { } } - this.formaters[colIndex] = this.defaultCellFormater; + this.formaters[colIndex] = this.defaultCellFormater(); return this.formaters[colIndex](value); } @@ -142,3 +153,15 @@ export class TableRenderer { return html; } } + +function encodeHtml(unsafe) { + return unsafe.replace(/[&<>"']/g, function(m) { + return ({ + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''' + })[m]; + }); +} diff --git a/public/app/plugins/panel/table/specs/renderer_specs.ts b/public/app/plugins/panel/table/specs/renderer_specs.ts index 0e7d1e921f4..70dca26a0e8 100644 --- a/public/app/plugins/panel/table/specs/renderer_specs.ts +++ b/public/app/plugins/panel/table/specs/renderer_specs.ts @@ -11,6 +11,8 @@ describe('when rendering table', () => { {text: 'Value'}, {text: 'Colored'}, {text: 'Undefined'}, + {text: 'String'}, + {text: 'UnescapedString' } ]; var panel = { @@ -35,6 +37,16 @@ describe('when rendering table', () => { colorMode: 'value', thresholds: [50, 80], colors: ['green', 'orange', 'red'] + }, + { + pattern: 'String', + type: 'string', + escapeHtml: true, + }, + { + pattern: 'UnescapedString', + type: 'string', + escapeHtml: false, } ] }; @@ -76,6 +88,21 @@ describe('when rendering table', () => { expect(html).to.be('value'); }); + it('string style with escape html should return escaped html', () => { + var html = renderer.renderCell(4, "&breaking
the
row"); + expect(html).to.be('&breaking <br /> the <br /> row'); + }); + + it('undefined formater should return escaped html', () => { + var html = renderer.renderCell(4, "&breaking
the
row"); + expect(html).to.be('&breaking <br /> the <br /> row'); + }); + + it('string style with escape html false should return html', () => { + var html = renderer.renderCell(5, "&breaking
the
row"); + expect(html).to.be('&breaking
the
row'); + }); + it('undefined value should render as -', () => { var html = renderer.renderCell(3, undefined); expect(html).to.be('');