Commit d750908e by bergquist

feat(table): escape html by default

closes #3673
parent b1a64860
...@@ -103,6 +103,11 @@ ...@@ -103,6 +103,11 @@
<metric-segment-model property="style.dateFormat" options="editor.dateFormats" on-change="editor.render()" custom="true"></metric-segment-model> <metric-segment-model property="style.dateFormat" options="editor.dateFormats" on-change="editor.render()" custom="true"></metric-segment-model>
</li> </li>
</ul> </ul>
<ul class="tight-form-list" ng-if="style.type === 'string'">
<li class="tight-form-item">
<editor-checkbox text="escape html" model="style.escapeHtml" change="editor.render()"></editor-checkbox>
</li>
</ul>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div class="tight-form" ng-if="style.type === 'number'"> <div class="tight-form" ng-if="style.type === 'number'">
...@@ -158,6 +163,7 @@ ...@@ -158,6 +163,7 @@
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -112,6 +112,7 @@ export class TablePanelEditorCtrl { ...@@ -112,6 +112,7 @@ export class TablePanelEditorCtrl {
pattern: '/.*/', pattern: '/.*/',
dateFormat: 'YYYY-MM-DD HH:mm:ss', dateFormat: 'YYYY-MM-DD HH:mm:ss',
thresholds: [], thresholds: [],
escapeHtml: true
}; };
this.panel.styles.push(angular.copy(columnStyleDefaults)); this.panel.styles.push(angular.copy(columnStyleDefaults));
......
...@@ -4,6 +4,8 @@ import _ from 'lodash'; ...@@ -4,6 +4,8 @@ import _ from 'lodash';
import moment from 'moment'; import moment from 'moment';
import kbn from 'app/core/utils/kbn'; import kbn from 'app/core/utils/kbn';
export class TableRenderer { export class TableRenderer {
formaters: any[]; formaters: any[];
colorState: any; colorState: any;
...@@ -24,22 +26,27 @@ export class TableRenderer { ...@@ -24,22 +26,27 @@ export class TableRenderer {
return _.first(style.colors); return _.first(style.colors);
} }
defaultCellFormater(v) { defaultCellFormater(escapeHtml = true) {
if (v === null || v === void 0) { return function(v) {
return ''; if (v === null || v === void 0 || v === undefined) {
} return '';
}
if (_.isArray(v)) { if (_.isArray(v)) {
v = v.join(',&nbsp;'); v = v.join(',&nbsp;');
} }
return v; if (_.isString(v) && escapeHtml) {
} v = encodeHtml(v);
}
return v;
};
}
createColumnFormater(style) { createColumnFormater(style) {
if (!style) { if (!style) {
return this.defaultCellFormater; return this.defaultCellFormater();
} }
if (style.type === 'date') { if (style.type === 'date') {
...@@ -62,7 +69,7 @@ export class TableRenderer { ...@@ -62,7 +69,7 @@ export class TableRenderer {
} }
if (_.isString(v)) { if (_.isString(v)) {
return v; return encodeHtml(v);
} }
if (style.colorMode) { if (style.colorMode) {
...@@ -73,7 +80,11 @@ export class TableRenderer { ...@@ -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) { formatColumnValue(colIndex, value) {
...@@ -91,7 +102,7 @@ export class TableRenderer { ...@@ -91,7 +102,7 @@ export class TableRenderer {
} }
} }
this.formaters[colIndex] = this.defaultCellFormater; this.formaters[colIndex] = this.defaultCellFormater();
return this.formaters[colIndex](value); return this.formaters[colIndex](value);
} }
...@@ -142,3 +153,15 @@ export class TableRenderer { ...@@ -142,3 +153,15 @@ export class TableRenderer {
return html; return html;
} }
} }
function encodeHtml(unsafe) {
return unsafe.replace(/[&<>"']/g, function(m) {
return ({
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'\'': '&#039;'
})[m];
});
}
...@@ -11,6 +11,8 @@ describe('when rendering table', () => { ...@@ -11,6 +11,8 @@ describe('when rendering table', () => {
{text: 'Value'}, {text: 'Value'},
{text: 'Colored'}, {text: 'Colored'},
{text: 'Undefined'}, {text: 'Undefined'},
{text: 'String'},
{text: 'UnescapedString' }
]; ];
var panel = { var panel = {
...@@ -35,6 +37,16 @@ describe('when rendering table', () => { ...@@ -35,6 +37,16 @@ describe('when rendering table', () => {
colorMode: 'value', colorMode: 'value',
thresholds: [50, 80], thresholds: [50, 80],
colors: ['green', 'orange', 'red'] colors: ['green', 'orange', 'red']
},
{
pattern: 'String',
type: 'string',
escapeHtml: true,
},
{
pattern: 'UnescapedString',
type: 'string',
escapeHtml: false,
} }
] ]
}; };
...@@ -76,6 +88,21 @@ describe('when rendering table', () => { ...@@ -76,6 +88,21 @@ describe('when rendering table', () => {
expect(html).to.be('<td>value</td>'); expect(html).to.be('<td>value</td>');
}); });
it('string style with escape html should return escaped html', () => {
var html = renderer.renderCell(4, "&breaking <br /> the <br /> row");
expect(html).to.be('<td>&amp;breaking &lt;br /&gt; the &lt;br /&gt; row</td>');
});
it('undefined formater should return escaped html', () => {
var html = renderer.renderCell(4, "&breaking <br /> the <br /> row");
expect(html).to.be('<td>&amp;breaking &lt;br /&gt; the &lt;br /&gt; row</td>');
});
it('string style with escape html false should return html', () => {
var html = renderer.renderCell(5, "&breaking <br /> the <br /> row");
expect(html).to.be('<td>&breaking <br /> the <br /> row</td>');
});
it('undefined value should render as -', () => { it('undefined value should render as -', () => {
var html = renderer.renderCell(3, undefined); var html = renderer.renderCell(3, undefined);
expect(html).to.be('<td></td>'); expect(html).to.be('<td></td>');
......
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