diff --git a/src/lib/index.js b/src/lib/index.js index 583eef95615..ebb1822d48c 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -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]); } } diff --git a/test/jasmine/tests/animate_test.js b/test/jasmine/tests/animate_test.js index cae251e2950..1e7e01d199a 100644 --- a/test/jasmine/tests/animate_test.js +++ b/test/jasmine/tests/animate_test.js @@ -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;