VYPR
Critical severityNVD Advisory· Published Jan 3, 2024· Updated Dec 24, 2025

CVE-2023-46308

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.

PackageAffected versionsPatched versions
plotly/plotly.jsPackagist
< 2.25.22.25.2
plotly.jsnpm
< 2.25.22.25.2

Affected products

3

Patches

2
02498404c8ad

Merge pull request #6704 from plotly/nestedProperty-proto

https://github.com/plotly/plotly.jsMojtaba SamimiAug 11, 2023via ghsa
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) {
    
5efd2a1f07a4

Merge pull request #6703 from plotly/expandObjectPaths-proto

https://github.com/plotly/plotly.jsMojtaba SamimiAug 11, 2023via ghsa
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

News mentions

0

No linked articles in our index yet.