From 33a2f16e7c6cae9a732abdc30f8aab29d61797d3 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 10 Aug 2023 16:43:14 -0400 Subject: [PATCH 1/6] add tests --- test/jasmine/tests/animate_test.js | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/jasmine/tests/animate_test.js b/test/jasmine/tests/animate_test.js index cae251e2950..2f1dd58bcaa 100644 --- a/test/jasmine/tests/animate_test.js +++ b/test/jasmine/tests/animate_test.js @@ -708,6 +708,56 @@ 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} + }); + }).then(function() { + setTimeout(function() { + var a = {}; + expect(a.polluted).toBeUndefined(); + }, 100); + }).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(function() { + setTimeout(function() { + var a = {}; + expect(a.polluted).toBeUndefined(); + }, 100); + }).then(done, done.fail); + }); +}); + describe('Animating multiple axes', function() { var gd; From ec7ff522c09c96320352acb30992c101eb8dac6f Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 10 Aug 2023 17:13:25 -0400 Subject: [PATCH 2/6] ensure __proto__ is not polluted in expandObjectPaths --- src/lib/index.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/index.js b/src/lib/index.js index 583eef95615..564b9f3cc41 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -933,6 +933,7 @@ lib.expandObjectPaths = function(data) { if((match = key.match(dottedPropertyRegex))) { datum = data[key]; prop = match[1]; + if(prop === '__proto__') continue; delete data[key]; @@ -941,6 +942,8 @@ lib.expandObjectPaths = function(data) { datum = data[key]; prop = match[1]; + if(prop === '__proto__') continue; + idx = parseInt(match[2]); delete data[key]; @@ -969,9 +972,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(prop === '__proto__') continue; data[prop][idx] = lib.expandObjectPaths(datum); } } else { + if(key === '__proto__') continue; data[key] = lib.expandObjectPaths(data[key]); } } From 4474ca8056ed6bbb8bf27b140215d10ecd4f6d24 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi <33888540+archmoj@users.noreply.github.com> Date: Fri, 11 Aug 2023 08:45:17 -0400 Subject: [PATCH 3/6] Update test/jasmine/tests/animate_test.js Co-authored-by: Alex Johnson --- test/jasmine/tests/animate_test.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/jasmine/tests/animate_test.js b/test/jasmine/tests/animate_test.js index 2f1dd58bcaa..31800b77260 100644 --- a/test/jasmine/tests/animate_test.js +++ b/test/jasmine/tests/animate_test.js @@ -732,11 +732,9 @@ describe('Animate expandObjectPaths do not pollute prototype', function() { traces: [0], layout: {'__proto__.polluted': true} }); - }).then(function() { - setTimeout(function() { - var a = {}; - expect(a.polluted).toBeUndefined(); - }, 100); + }).then(delay(100)).then(function() { + var a = {}; + expect(a.polluted).toBeUndefined(); }).then(done, done.fail); }); From 0dcb1f73390f92af277d18c76b0c3cf4092bcfc6 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi <33888540+archmoj@users.noreply.github.com> Date: Fri, 11 Aug 2023 08:45:26 -0400 Subject: [PATCH 4/6] Update test/jasmine/tests/animate_test.js Co-authored-by: Alex Johnson --- test/jasmine/tests/animate_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jasmine/tests/animate_test.js b/test/jasmine/tests/animate_test.js index 31800b77260..f32da2df108 100644 --- a/test/jasmine/tests/animate_test.js +++ b/test/jasmine/tests/animate_test.js @@ -730,7 +730,7 @@ describe('Animate expandObjectPaths do not pollute prototype', function() { transition: {duration: 10}, data: [{y: [2, 3, 1]}], traces: [0], - layout: {'__proto__.polluted': true} + layout: {'__proto__.polluted': true, 'x.__proto__.polluted': true} }); }).then(delay(100)).then(function() { var a = {}; From cf5c6231043a110bd58224f44a899b0ae50ae931 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 11 Aug 2023 08:54:36 -0400 Subject: [PATCH 5/6] skip all __ keys instead of only __proto__ --- src/lib/index.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/lib/index.js b/src/lib/index.js index 564b9f3cc41..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,7 +938,7 @@ lib.expandObjectPaths = function(data) { if((match = key.match(dottedPropertyRegex))) { datum = data[key]; prop = match[1]; - if(prop === '__proto__') continue; + if(notValid(prop)) continue; delete data[key]; @@ -942,7 +947,7 @@ lib.expandObjectPaths = function(data) { datum = data[key]; prop = match[1]; - if(prop === '__proto__') continue; + if(notValid(prop)) continue; idx = parseInt(match[2]); @@ -973,11 +978,11 @@ lib.expandObjectPaths = function(data) { // This is the case where this property is the end of the line, // e.g. xaxis.range[0] - if(prop === '__proto__') continue; + if(notValid(prop)) continue; data[prop][idx] = lib.expandObjectPaths(datum); } } else { - if(key === '__proto__') continue; + if(notValid(key)) continue; data[key] = lib.expandObjectPaths(data[key]); } } From e1e31752fdbd8beca6d7bf2d69d2096728ba7bbd Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 11 Aug 2023 08:57:39 -0400 Subject: [PATCH 6/6] fix delay in test --- test/jasmine/tests/animate_test.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/jasmine/tests/animate_test.js b/test/jasmine/tests/animate_test.js index f32da2df108..1e7e01d199a 100644 --- a/test/jasmine/tests/animate_test.js +++ b/test/jasmine/tests/animate_test.js @@ -747,11 +747,9 @@ describe('Animate expandObjectPaths do not pollute prototype', function() { data: [{y: [2, 3, 1], '__proto__.polluted': true}], traces: [0] }); - }).then(function() { - setTimeout(function() { - var a = {}; - expect(a.polluted).toBeUndefined(); - }, 100); + }).then(delay(100)).then(function() { + var a = {}; + expect(a.polluted).toBeUndefined(); }).then(done, done.fail); }); });