Skip to content

Commit 3a1a62a

Browse files
committed
add tests for sanitizeHTML
1 parent eeaf204 commit 3a1a62a

File tree

2 files changed

+120
-5
lines changed

2 files changed

+120
-5
lines changed

test/jasmine/tests/mapbox_test.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,15 +1536,18 @@ describe('@noCI, mapbox plots', function() {
15361536
var mock = require('@mocks/mapbox_layers.json');
15371537
var customMock = Lib.extendDeep(mock);
15381538

1539-
var attr = 'super custom attribution';
1539+
var attr = 'custom attribution';
1540+
var XSS = '<img src=x onerror=\"alert(XSS);\">';
15401541
customMock.data.pop();
1541-
customMock.layout.mapbox.layers[0].sourceattribution = attr;
1542+
customMock.layout.mapbox.layers[0].sourceattribution = XSS + attr;
15421543

15431544
Plotly.newPlot(gd, customMock)
15441545
.then(function() {
15451546
var s = Plotly.d3.selectAll('.mapboxgl-ctrl-attrib');
15461547
expect(s.size()).toBe(1);
1547-
expect(s.text()).toEqual([attr, '© Mapbox © OpenStreetMap Improve this map'].join(' | '));
1548+
expect(s.text()).toEqual([XSS + attr, '© Mapbox © OpenStreetMap Improve this map'].join(' | '));
1549+
expect(s.html().indexOf('<img src=x onerror="alert(XSS);">')).toBe(-1);
1550+
expect(s.html().indexOf('&lt;img src=x onerror="alert(XSS);"&gt;')).not.toBe(-1);
15481551
})
15491552
.catch(failTest)
15501553
.then(done);

test/jasmine/tests/svg_text_utils_test.js

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,12 @@ describe('svg+text utils', function() {
117117

118118
it('whitelists http hrefs', function() {
119119
var node = mockTextSVGElement(
120-
'<a href="https://bl.ocks.org/">bl.ocks.org</a>'
120+
'<a href="http://bl.ocks.org/">bl.ocks.org</a>'
121121
);
122122

123123
expect(node.text()).toEqual('bl.ocks.org');
124124
assertAnchorAttrs(node);
125-
assertAnchorLink(node, 'https://bl.ocks.org/');
125+
assertAnchorLink(node, 'http://bl.ocks.org/');
126126
});
127127

128128
it('whitelists https hrefs', function() {
@@ -512,3 +512,115 @@ describe('svg+text utils', function() {
512512
});
513513
});
514514
});
515+
516+
describe('sanitizeHTML', function() {
517+
'use strict';
518+
519+
describe('convertToTspans', function() {
520+
var stringFromCodePoint;
521+
522+
beforeAll(function() {
523+
stringFromCodePoint = String.fromCodePoint;
524+
});
525+
526+
afterEach(function() {
527+
String.fromCodePoint = stringFromCodePoint;
528+
});
529+
530+
function mockHTML(txt) {
531+
return util.sanitizeHTML(txt);
532+
}
533+
534+
afterEach(function() {
535+
d3.selectAll('.text-tester').remove();
536+
});
537+
538+
it('checks for XSS attack in href', function() {
539+
var innerHTML = mockHTML(
540+
'<a href="javascript:alert(\'attack\')">XSS</a>'
541+
);
542+
543+
expect(innerHTML).toEqual('<a>XSS</a>');
544+
});
545+
546+
it('checks for XSS attack in href (with plenty of white spaces)', function() {
547+
var innerHTML = mockHTML(
548+
'<a href = " javascript:alert(\'attack\')">XSS</a>'
549+
);
550+
551+
expect(innerHTML).toEqual('<a>XSS</a>');
552+
});
553+
554+
it('whitelists relative hrefs (interpreted as http)', function() {
555+
var innerHTML = mockHTML(
556+
'<a href="/mylink">mylink</a>'
557+
);
558+
559+
expect(innerHTML).toEqual('<a href="/mylink">mylink</a>');
560+
});
561+
562+
it('whitelists http hrefs', function() {
563+
var innerHTML = mockHTML(
564+
'<a href="http://bl.ocks.org/">bl.ocks.org</a>'
565+
);
566+
567+
expect(innerHTML).toEqual('<a href="http://bl.ocks.org/">bl.ocks.org</a>');
568+
});
569+
570+
it('whitelists https hrefs', function() {
571+
var innerHTML = mockHTML(
572+
'<a href="https://chart-studio.plotly.com">plotly</a>'
573+
);
574+
575+
expect(innerHTML).toEqual('<a href="https://chart-studio.plotly.com">plotly</a>');
576+
});
577+
578+
it('whitelists mailto hrefs', function() {
579+
var innerHTML = mockHTML(
580+
'<a href="mailto:support@plotly.com">support</a>'
581+
);
582+
583+
expect(innerHTML).toEqual('<a href="mailto:support@plotly.com">support</a>');
584+
});
585+
586+
it('drops XSS attacks in href', function() {
587+
// "XSS" gets interpreted as a relative link (http)
588+
var textCases = [
589+
'<a href="XSS\" onmouseover="alert(1)\" style="font-size:300px">Subtitle</a>',
590+
'<a href="XSS" onmouseover="alert(1)" style="font-size:300px">Subtitle</a>'
591+
];
592+
593+
textCases.forEach(function(textCase) {
594+
var innerHTML = mockHTML(textCase);
595+
596+
expect(innerHTML).toEqual('<a style="font-size:300px" href="XSS">Subtitle</a>');
597+
});
598+
});
599+
600+
it('accepts href and style in <a> in any order and tosses other stuff', function() {
601+
var textCases = [
602+
'<a href="x" style="y">z</a>',
603+
'<a href=\'x\' style="y">z</a>',
604+
'<A HREF="x"StYlE=\'y\'>z</a>',
605+
'<a style=\'y\'href=\'x\'>z</A>',
606+
'<a \t\r\n href="x" \n\r\t style="y" \n \t \r>z</a>',
607+
'<a magic="true" href="x" weather="cloudy" style="y" speed="42">z</a>',
608+
'<a href="x" style="y">z</a href="nope" style="for real?">',
609+
];
610+
611+
textCases.forEach(function(textCase) {
612+
var innerHTML = mockHTML(textCase);
613+
614+
expect(innerHTML).toEqual('<a style="y" href="x">z</a>');
615+
});
616+
});
617+
618+
it('allows encoded URIs in href', function() {
619+
var innerHTML = mockHTML(
620+
'<a href="https://example.com/?q=date%20%3E=%202018-01-01">click</a>'
621+
);
622+
623+
expect(innerHTML).toEqual('<a href="https://example.com/?q=date%20%3E=%202018-01-01">click</a>');
624+
});
625+
});
626+
});

0 commit comments

Comments
 (0)