diff --git a/src/lib/svg_text_utils.js b/src/lib/svg_text_utils.js
index e9549eab98..543ae9ff00 100644
--- a/src/lib/svg_text_utils.js
+++ b/src/lib/svg_text_utils.js
@@ -604,14 +604,9 @@ function buildSVGText(containerNode, str) {
var href = getQuotedMatch(extra, HREFMATCH);
if(href) {
- // check safe protocols
- var dummyAnchor = document.createElement('a');
- dummyAnchor.href = href;
- if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) {
- // Decode href to allow both already encoded and not encoded
- // URIs. Without decoding prior encoding, an already encoded
- // URI would be encoded twice producing a semantically different URI.
- nodeSpec.href = encodeURI(decodeURI(href));
+ var safeHref = sanitizeHref(href);
+ if(safeHref) {
+ nodeSpec.href = safeHref;
nodeSpec.target = getQuotedMatch(extra, TARGETMATCH) || '_blank';
nodeSpec.popup = getQuotedMatch(extra, POPUPMATCH);
}
@@ -626,6 +621,27 @@ function buildSVGText(containerNode, str) {
return hasLink;
}
+function sanitizeHref(href) {
+ var decodedHref = encodeURI(decodeURI(href));
+ var dummyAnchor1 = document.createElement('a');
+ var dummyAnchor2 = document.createElement('a');
+ dummyAnchor1.href = href;
+ dummyAnchor2.href = decodedHref;
+
+ var p1 = dummyAnchor1.protocol;
+ var p2 = dummyAnchor2.protocol;
+
+ // check safe protocols
+ if(
+ PROTOCOLS.indexOf(p1) !== -1 &&
+ PROTOCOLS.indexOf(p2) !== -1
+ ) {
+ return decodedHref;
+ } else {
+ return '';
+ }
+}
+
/*
* sanitizeHTML: port of buildSVGText aimed at providing a clean subset of HTML
* @param {string} str: the html string to clean
@@ -660,10 +676,9 @@ exports.sanitizeHTML = function sanitizeHTML(str) {
var href = getQuotedMatch(extra, HREFMATCH);
if(href) {
- var dummyAnchor = document.createElement('a');
- dummyAnchor.href = href;
- if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) {
- nodeAttrs.href = encodeURI(decodeURI(href));
+ var safeHref = sanitizeHref(href);
+ if(safeHref) {
+ nodeAttrs.href = safeHref;
var target = getQuotedMatch(extra, TARGETMATCH);
if(target) {
nodeAttrs.target = target;
diff --git a/test/jasmine/tests/svg_text_utils_test.js b/test/jasmine/tests/svg_text_utils_test.js
index 5b1ed7b026..34060aba24 100644
--- a/test/jasmine/tests/svg_text_utils_test.js
+++ b/test/jasmine/tests/svg_text_utils_test.js
@@ -85,6 +85,16 @@ describe('svg+text utils', function() {
d3.selectAll('.text-tester').remove();
});
+ it('checks for XSS attack in href protocol', function() {
+ var node = mockTextSVGElement(
+ 'XSS'
+ );
+
+ expect(node.text()).toEqual('XSS');
+ assertAnchorAttrs(node);
+ assertAnchorLink(node, null);
+ });
+
it('checks for XSS attack in href', function() {
var node = mockTextSVGElement(
'XSS'
@@ -534,6 +544,14 @@ describe('sanitizeHTML', function() {
d3.selectAll('.text-tester').remove();
});
+ it('checks for XSS attack in href protocol', function() {
+ var innerHTML = mockHTML(
+ 'XSS'
+ );
+
+ expect(innerHTML).toEqual('XSS');
+ });
+
it('checks for XSS attack in href', function() {
var innerHTML = mockHTML(
'XSS'