From d5885957f72e21a25782dc28e2028ffe8debf5ef Mon Sep 17 00:00:00 2001 From: etpinard Date: Thu, 10 Dec 2015 10:56:06 -0500 Subject: [PATCH 1/4] omit anchors with XSS href via a whitelist in convertToSVG --- src/lib/svg_text_utils.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index f1b9368dc72..9764c9cfb86 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -221,6 +221,8 @@ var TAG_STYLES = { em: 'font-style:italic;font-weight:bold' }; +var PROTOCOLS = ['http:', 'https:', 'mailto']; + var STRIP_TAGS = new RegExp(']*)?/?>', 'g'); util.plainText = function(_str){ @@ -252,7 +254,14 @@ function convertToSVG(_str){ if(tag === 'a'){ if(close) return ''; else if(extra.substr(0,4).toLowerCase() !== 'href') return ''; - else return ''; + else { + var dummyAnchor = document.createElement('a'); + dummyAnchor.href = extra.split('href=')[1].replace(/["']/g, ''); + + if(PROTOCOLS.indexOf(dummyAnchor.protocol) === -1) return ''; + + return ''; + } } else if(tag === 'br') return '
'; else if(close) { From e6f773f77f80a82ae5d744d94de86f64200f44c1 Mon Sep 17 00:00:00 2001 From: etpinard Date: Thu, 10 Dec 2015 14:59:51 -0500 Subject: [PATCH 2/4] fix typo: 'mailto:' protocol not 'mailto' --- src/lib/svg_text_utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index 9764c9cfb86..d86ab0edcc1 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -221,7 +221,7 @@ var TAG_STYLES = { em: 'font-style:italic;font-weight:bold' }; -var PROTOCOLS = ['http:', 'https:', 'mailto']; +var PROTOCOLS = ['http:', 'https:', 'mailto:']; var STRIP_TAGS = new RegExp(']*)?/?>', 'g'); From 7367cb2fe20ac392b4114959cd792a18684b96ee Mon Sep 17 00:00:00 2001 From: etpinard Date: Thu, 10 Dec 2015 15:00:17 -0500 Subject: [PATCH 3/4] improve attrs to href algo --- src/lib/svg_text_utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js index d86ab0edcc1..a253c3c6a0a 100644 --- a/src/lib/svg_text_utils.js +++ b/src/lib/svg_text_utils.js @@ -256,7 +256,7 @@ function convertToSVG(_str){ else if(extra.substr(0,4).toLowerCase() !== 'href') return '
'; else { var dummyAnchor = document.createElement('a'); - dummyAnchor.href = extra.split('href=')[1].replace(/["']/g, ''); + dummyAnchor.href = extra.substr(4).replace(/["'=]/g, ''); if(PROTOCOLS.indexOf(dummyAnchor.protocol) === -1) return ''; From 68801c9d2bb4ba405f475a121104d460be1098df Mon Sep 17 00:00:00 2001 From: etpinard Date: Thu, 10 Dec 2015 16:01:27 -0500 Subject: [PATCH 4/4] add svg utils tests --- test/jasmine/tests/svg_text_utils_test.js | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 test/jasmine/tests/svg_text_utils_test.js diff --git a/test/jasmine/tests/svg_text_utils_test.js b/test/jasmine/tests/svg_text_utils_test.js new file mode 100644 index 00000000000..4abb4692fd6 --- /dev/null +++ b/test/jasmine/tests/svg_text_utils_test.js @@ -0,0 +1,69 @@ +var d3 = require('d3'); + +var util = require('@src/lib/svg_text_utils'); + + +describe('svg+text utils', function() { + 'use strict'; + + describe('convertToTspans', function() { + + function mockTextSVGElement(txt) { + return d3.select('body') + .append('svg') + .attr('id', 'text') + .append('text') + .text(txt) + .call(util.convertToTspans); + } + + afterEach(function() { + d3.select('#text').remove(); + }); + + it('checks for XSS attack in href', function() { + var node = mockTextSVGElement( + 'XSS' + ) + + expect(node.text()).toEqual('XSS'); + expect(node.select('a').attr('xlink:href')).toBe(null); + }); + + it('checks for XSS attack in href (with plenty of white spaces)', function() { + var node = mockTextSVGElement( + 'XSS' + ) + + expect(node.text()).toEqual('XSS'); + expect(node.select('a').attr('xlink:href')).toBe(null); + }); + + it('whitelists http hrefs', function() { + var node = mockTextSVGElement( + 'bl.ocks.org' + ) + + expect(node.text()).toEqual('bl.ocks.org'); + expect(node.select('a').attr('xlink:href')).toEqual('http://bl.ocks.org/'); + }); + + it('whitelists https hrefs', function() { + var node = mockTextSVGElement( + 'plot.ly' + ) + + expect(node.text()).toEqual('plot.ly'); + expect(node.select('a').attr('xlink:href')).toEqual('https://plot.ly'); + }); + + it('whitelists mailto hrefs', function() { + var node = mockTextSVGElement( + 'support' + ) + + expect(node.text()).toEqual('support'); + expect(node.select('a').attr('xlink:href')).toEqual('mailto:support@plot.ly'); + }); + }); +});