Cross-Site Scripting in c3
Description
Affected versions of c3 are vulnerable to cross-site scripting via improper sanitization of HTML in rendered tooltips.
Recommendation
Update to 0.4.11 or later.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
C3.js versions before 0.4.11 are vulnerable to stored XSS via unsanitized HTML in tooltip titles, values, and names.
C3.js, a D3-based chart library, contained a cross-site scripting (XSS) vulnerability in its tooltip rendering. The getTooltipContent function did not sanitize user-supplied data for the title, value, and name fields before inserting them into the HTML tooltip [1]. This allowed an attacker to inject arbitrary HTML and JavaScript into the tooltip output.
An attacker can exploit this by providing malicious data to a chart, such as a data series name containing `` tags or event handlers. When a user hovers over the corresponding chart element, the unsanitized content is rendered, executing the injected script in the user's browser. No authentication is required if the attacker can control data passed to the chart, for example through a web application that reflects user input in chart data [4].
Successful exploitation leads to cross-site scripting (XSS), enabling the attacker to steal cookies, session tokens, or perform actions on behalf of the victim. The vulnerability is rated medium severity due to the need for user interaction (hovering) and the prerequisite that the attacker can supply data to the chart.
The vulnerability was fixed in version 0.4.11 by adding a sanitise function that replaces < and > with HTML entities [1][3]. Users are strongly advised to update to version 0.4.11 or later to mitigate the risk.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
c3npm | < 0.4.11 | 0.4.11 |
Affected products
2Patches
1de3864650300Sanitise string embedded in tooltip - #1536
4 files changed · +17 −11
c3.js+6 −3 modified@@ -3913,15 +3913,15 @@ if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; } if (! text) { - title = titleFormat ? titleFormat(d[i].x) : d[i].x; + title = sanitise(titleFormat ? titleFormat(d[i].x) : d[i].x); text = "<table class='" + $$.CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : ""); } - value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d); + value = sanitise(valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d)); if (value !== undefined) { // Skip elements when their name is set to null if (d[i].name === null) { continue; } - name = nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index); + name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index)); bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id); text += "<tr class='" + $$.CLASS.tooltipName + "-" + $$.getTargetSelectorSuffix(d[i].id) + "'>"; @@ -6100,6 +6100,9 @@ }); return found; }, + sanitise = c3_chart_internal_fn.sanitise = function (str) { + return typeof str === 'string' ? str.replace(/</g, '<').replace(/>/g, '>') : str; + }, getPathBox = c3_chart_internal_fn.getPathBox = function (path) { var box = path.getBoundingClientRect(), items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)],
c3.min.js+5 −5 modifiedsrc/tooltip.js+3 −3 modified@@ -55,15 +55,15 @@ c3_chart_internal_fn.getTooltipContent = function (d, defaultTitleFormat, defaul if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; } if (! text) { - title = titleFormat ? titleFormat(d[i].x) : d[i].x; + title = sanitise(titleFormat ? titleFormat(d[i].x) : d[i].x); text = "<table class='" + $$.CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : ""); } - value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d); + value = sanitise(valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d)); if (value !== undefined) { // Skip elements when their name is set to null if (d[i].name === null) { continue; } - name = nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index); + name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index)); bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id); text += "<tr class='" + $$.CLASS.tooltipName + "-" + $$.getTargetSelectorSuffix(d[i].id) + "'>";
src/util.js+3 −0 modified@@ -38,6 +38,9 @@ var isValue = c3_chart_internal_fn.isValue = function (v) { }); return found; }, + sanitise = c3_chart_internal_fn.sanitise = function (str) { + return typeof str === 'string' ? str.replace(/</g, '<').replace(/>/g, '>') : str; + }, getPathBox = c3_chart_internal_fn.getPathBox = function (path) { var box = path.getBoundingClientRect(), items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)],
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6News mentions
0No linked articles in our index yet.