CVE-2023-46308
Description
Prototype pollution in plotly.js before 2.25.2 allows attackers to pollute Object.prototype via plot API calls.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Prototype pollution in plotly.js before 2.25.2 allows attackers to pollute Object.prototype via plot API calls.
Description
In Plotly plotly.js prior to version 2.25.2, the expandObjectPaths and nestedProperty functions do not sanitize property names for __proto__ and similar internal properties. This allows an attacker to pollute the prototype of Object by crafting plot API calls with malicious attribute paths [1].
Exploitation
An attacker can exploit this by providing user-controlled input to plot API methods such as Plotly.newPlot or Plotly.restyle, where nested object paths are parsed. For example, a path like ['__proto__', 'polluted'] would set Object.prototype.polluted to a desired value. No authentication is required if the attacker can supply the plot data or layout [1][4].
Impact
Successful prototype pollution can lead to arbitrary code execution or property injection, affecting all objects in the application and potentially enabling further attacks like cross-site scripting (XSS) or denial of service [1].
Mitigation
The vulnerability is fixed in plotly.js version 2.25.2. The patch introduces a notValid function that rejects property names starting with __, preventing pollution via __proto__ and similar paths [4]. Users are advised to upgrade immediately.
AI Insight generated on May 20, 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 |
|---|---|---|
plotly/plotly.jsPackagist | < 2.25.2 | 2.25.2 |
plotly.jsnpm | < 2.25.2 | 2.25.2 |
Affected products
3- ghsa-coords2 versions
< 2.25.2+ 1 more
- (no CPE)range: < 2.25.2
- (no CPE)range: < 2.25.2
Patches
202498404c8adMerge pull request #6704 from plotly/nestedProperty-proto
2 files changed · +12 −3
src/lib/nested_property.js+9 −2 modified@@ -24,13 +24,20 @@ module.exports = function nestedProperty(container, propStr) { throw 'bad property string'; } - var j = 0; var propParts = propStr.split('.'); var indexed; var indices; - var i; + var i, j; + + for(j = 0; j < propParts.length; j++) { + // guard against polluting __proto__ and other internals + if(String(propParts[j]).slice(0, 2) === '__') { + throw 'bad property string'; + } + } // check for parts of the nesting hierarchy that are numbers (ie array elements) + j = 0; while(j < propParts.length) { // look for non-bracket chars, then any number of [##] blocks indexed = String(propParts[j]).match(/^([^\[\]]*)((\[\-?[0-9]*\])+)$/);
test/jasmine/tests/lib_test.js+3 −1 modified@@ -468,7 +468,9 @@ describe('Test lib.js:', function() { it('should fail on a bad property string', function() { var badStr = [ - [], {}, false, undefined, null, NaN, Infinity + [], {}, false, undefined, null, NaN, Infinity, + // should guard against prototype pollution + 'x.__proto__.polluted', 'x.y.__proto__.polluted' ]; function badProp(i) {
5efd2a1f07a4Merge pull request #6703 from plotly/expandObjectPaths-proto
2 files changed · +57 −0
src/lib/index.js+11 −0 modified@@ -925,6 +925,11 @@ lib.objectFromPath = function(path, value) { var dottedPropertyRegex = /^([^\[\.]+)\.(.+)?/; var indexedPropertyRegex = /^([^\.]+)\[([0-9]+)\](\.)?(.+)?/; +function notValid(prop) { + // guard against polluting __proto__ and other internals getters and setters + return prop.slice(0, 2) === '__'; +} + lib.expandObjectPaths = function(data) { var match, key, prop, datum, idx, dest, trailingPath; if(typeof data === 'object' && !Array.isArray(data)) { @@ -933,6 +938,7 @@ lib.expandObjectPaths = function(data) { if((match = key.match(dottedPropertyRegex))) { datum = data[key]; prop = match[1]; + if(notValid(prop)) continue; delete data[key]; @@ -941,6 +947,8 @@ lib.expandObjectPaths = function(data) { datum = data[key]; prop = match[1]; + if(notValid(prop)) continue; + idx = parseInt(match[2]); delete data[key]; @@ -969,9 +977,12 @@ lib.expandObjectPaths = function(data) { } else { // This is the case where this property is the end of the line, // e.g. xaxis.range[0] + + if(notValid(prop)) continue; data[prop][idx] = lib.expandObjectPaths(datum); } } else { + if(notValid(key)) continue; data[key] = lib.expandObjectPaths(data[key]); } }
test/jasmine/tests/animate_test.js+46 −0 modified@@ -708,6 +708,52 @@ describe('Animate API details', function() { }); }); +describe('Animate expandObjectPaths do not pollute prototype', function() { + 'use strict'; + + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(function() { + Plotly.purge(gd); + destroyGraphDiv(); + }); + + it('should not pollute prototype - layout object', function(done) { + Plotly.newPlot(gd, { + data: [{y: [1, 3, 2]}] + }).then(function() { + return Plotly.animate(gd, { + transition: {duration: 10}, + data: [{y: [2, 3, 1]}], + traces: [0], + layout: {'__proto__.polluted': true, 'x.__proto__.polluted': true} + }); + }).then(delay(100)).then(function() { + var a = {}; + expect(a.polluted).toBeUndefined(); + }).then(done, done.fail); + }); + + it('should not pollute prototype - data object', function(done) { + Plotly.newPlot(gd, { + data: [{y: [1, 3, 2]}] + }).then(function() { + return Plotly.animate(gd, { + transition: {duration: 10}, + data: [{y: [2, 3, 1], '__proto__.polluted': true}], + traces: [0] + }); + }).then(delay(100)).then(function() { + var a = {}; + expect(a.polluted).toBeUndefined(); + }).then(done, done.fail); + }); +}); + describe('Animating multiple axes', function() { var gd;
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-wjc4-73q6-gv3mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-46308ghsaADVISORY
- github.com/plotly/plotly.R/issues/2463ghsaWEB
- github.com/plotly/plotly.js/commit/02498404c8ad7a3395191e65694fb142a37b0fe9ghsaWEB
- github.com/plotly/plotly.js/commit/5efd2a1f07a418b230a5626fc6c1c7929c47949dghsaWEB
- github.com/plotly/plotly.js/releases/tag/v2.25.2ghsaWEB
- plotly.com/javascriptghsaWEB
- plotly.com/javascript/mitre
News mentions
0No linked articles in our index yet.