High severityOSV Advisory· Published Mar 14, 2019· Updated Aug 5, 2024
CVE-2018-20801
CVE-2018-20801
Description
In js/parts/SvgRenderer.js in Highcharts JS before 6.1.0, the use of backtracking regular expressions permitted an attacker to conduct a denial of service attack against the SVGRenderer component, aka ReDoS.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
highchartsnpm | < 6.1.0 | 6.1.0 |
Affected products
1- Range: highmaps-v1.0.2, highmaps-v1.1.0, highmaps-v2.1.3, …
Patches
17c547e1e0f5eRefactored SVGRenderer text to not use backtracking regexes to replace attributes.
3 files changed · +75 −20
js/parts/SvgRenderer.js+38 −20 modified@@ -2348,9 +2348,6 @@ extend(SVGRenderer.prototype, /** @lends Highcharts.SVGRenderer.prototype */ { hasMarkup = textStr.indexOf('<') !== -1, lines, childNodes = textNode.childNodes, - clsRegex, - styleRegex, - hrefRegex, wasTooLong, parentX = attr(textNode, 'x'), textStyles = wrapper.styles, @@ -2390,6 +2387,23 @@ extend(SVGRenderer.prototype, /** @lends Highcharts.SVGRenderer.prototype */ { } }); return inputStr; + }, + parseAttribute = function (s, attr) { + var start, + delimiter; + + start = s.indexOf('<'); + s = s.substring(start, s.indexOf('>') - start); + + start = s.indexOf(attr + '='); + if (start !== -1) { + start = start + attr.length + 1; + delimiter = s.charAt(start); + if (delimiter === '"' || delimiter === "'") { // eslint-disable-line quotes + s = s.substring(start + 1); + return s.substring(0, s.indexOf(delimiter)); + } + } }; // The buildText code is quite heavy, so if we're not changing something @@ -2427,10 +2441,6 @@ extend(SVGRenderer.prototype, /** @lends Highcharts.SVGRenderer.prototype */ { // Complex strings, add more logic } else { - clsRegex = /<.*class="([^"]+)".*>/; - styleRegex = /<.*style="([^"]+)".*>/; - hrefRegex = /<.*href="([^"]+)".*>/; - if (tempParent) { // attach it to the DOM to read offset width tempParent.appendChild(textNode); @@ -2485,27 +2495,31 @@ extend(SVGRenderer.prototype, /** @lends Highcharts.SVGRenderer.prototype */ { renderer.SVG_NS, 'tspan' ), - spanCls, - spanStyle; // #390 - if (clsRegex.test(span)) { - spanCls = span.match(clsRegex)[1]; - attr(tspan, 'class', spanCls); + classAttribute, + styleAttribute, // #390 + hrefAttribute; + + classAttribute = parseAttribute(span, 'class'); + if (classAttribute) { + attr(tspan, 'class', classAttribute); } - if (styleRegex.test(span)) { - spanStyle = span.match(styleRegex)[1].replace( + + styleAttribute = parseAttribute(span, 'style'); + if (styleAttribute) { + styleAttribute = styleAttribute.replace( /(;| |^)color([ :])/, '$1fill$2' ); - attr(tspan, 'style', spanStyle); + attr(tspan, 'style', styleAttribute); } // Not for export - #1529 - if (hrefRegex.test(span) && !forExport) { + hrefAttribute = parseAttribute(span, 'href'); + if (hrefAttribute && !forExport) { attr( tspan, 'onclick', - 'location.href=\"' + - span.match(hrefRegex)[1] + '\"' + 'location.href=\"' + hrefAttribute + '\"' ); attr(tspan, 'class', 'highcharts-anchor'); /*= if (build.classic) { =*/ @@ -2649,8 +2663,12 @@ extend(SVGRenderer.prototype, /** @lends Highcharts.SVGRenderer.prototype */ { dy: dy, x: parentX }); - if (spanStyle) { // #390 - attr(tspan, 'style', spanStyle); + if (styleAttribute) { // #390 + attr( + tspan, + 'style', + styleAttribute + ); } textNode.appendChild(tspan); }
package.json+1 −0 modified@@ -82,6 +82,7 @@ "aws-sdk": "^2.94.0", "babel-runtime": "^6.20.0", "glob": "^7.1.2", + "safe-regex": "^1.1.0", "taffydb": "^2.7.3" } }
samples/unit-tests/svgrenderer/text/demo.js+36 −0 modified@@ -204,3 +204,39 @@ QUnit.test('Dir rtl (#3482)', function (assert) { document.getElementById('container').removeAttribute('dir'); }); + +QUnit.test('Attributes', function (assert) { + var ren = new Highcharts.Renderer( + document.getElementById('container'), + 600, + 400 + ); + + var text = ren + .text( + 'The quick brown fox jumps <span class="red">over</span> the lazy dog', + 20, + 20 + ) + .add(); + + assert.strictEqual( + text.element.childNodes[1].getAttribute('class'), + 'red', + 'Double quotes, red span should be picked up' + ); + + text = ren + .text( + "The quick brown fox jumps <span class='red'>over</span> the lazy dog", + 20, + 20 + ) + .add(); + + assert.strictEqual( + text.element.childNodes[1].getAttribute('class'), + 'red', + 'Single quotes, red span should be picked up' + ); +});
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-xmc8-cjfr-phx3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-20801ghsaADVISORY
- github.com/highcharts/highcharts/commit/7c547e1e0f5e4379f94396efd559a566668c0dfaghsax_refsource_MISCWEB
- security.netapp.com/advisory/ntap-20190715-0001ghsaWEB
- security.netapp.com/advisory/ntap-20190715-0001/mitrex_refsource_CONFIRM
- snyk.io/vuln/npm:highcharts:20180225ghsax_refsource_MISCWEB
- www.npmjs.com/advisories/793ghsaWEB
News mentions
0No linked articles in our index yet.