Skip to content

Commit 5efd2a1

Browse files
authored
Merge pull request #6703 from plotly/expandObjectPaths-proto
Fix potential prototype pollution in `expandObjectPaths`
2 parents bb4a1f7 + e1e3175 commit 5efd2a1

File tree

2 files changed

+57
-0
lines changed

2 files changed

+57
-0
lines changed

Diff for: src/lib/index.js

+11
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,11 @@ lib.objectFromPath = function(path, value) {
925925
var dottedPropertyRegex = /^([^\[\.]+)\.(.+)?/;
926926
var indexedPropertyRegex = /^([^\.]+)\[([0-9]+)\](\.)?(.+)?/;
927927

928+
function notValid(prop) {
929+
// guard against polluting __proto__ and other internals getters and setters
930+
return prop.slice(0, 2) === '__';
931+
}
932+
928933
lib.expandObjectPaths = function(data) {
929934
var match, key, prop, datum, idx, dest, trailingPath;
930935
if(typeof data === 'object' && !Array.isArray(data)) {
@@ -933,6 +938,7 @@ lib.expandObjectPaths = function(data) {
933938
if((match = key.match(dottedPropertyRegex))) {
934939
datum = data[key];
935940
prop = match[1];
941+
if(notValid(prop)) continue;
936942

937943
delete data[key];
938944

@@ -941,6 +947,8 @@ lib.expandObjectPaths = function(data) {
941947
datum = data[key];
942948

943949
prop = match[1];
950+
if(notValid(prop)) continue;
951+
944952
idx = parseInt(match[2]);
945953

946954
delete data[key];
@@ -969,9 +977,12 @@ lib.expandObjectPaths = function(data) {
969977
} else {
970978
// This is the case where this property is the end of the line,
971979
// e.g. xaxis.range[0]
980+
981+
if(notValid(prop)) continue;
972982
data[prop][idx] = lib.expandObjectPaths(datum);
973983
}
974984
} else {
985+
if(notValid(key)) continue;
975986
data[key] = lib.expandObjectPaths(data[key]);
976987
}
977988
}

Diff for: test/jasmine/tests/animate_test.js

+46
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,52 @@ describe('Animate API details', function() {
708708
});
709709
});
710710

711+
describe('Animate expandObjectPaths do not pollute prototype', function() {
712+
'use strict';
713+
714+
var gd;
715+
716+
beforeEach(function() {
717+
gd = createGraphDiv();
718+
});
719+
720+
afterEach(function() {
721+
Plotly.purge(gd);
722+
destroyGraphDiv();
723+
});
724+
725+
it('should not pollute prototype - layout object', function(done) {
726+
Plotly.newPlot(gd, {
727+
data: [{y: [1, 3, 2]}]
728+
}).then(function() {
729+
return Plotly.animate(gd, {
730+
transition: {duration: 10},
731+
data: [{y: [2, 3, 1]}],
732+
traces: [0],
733+
layout: {'__proto__.polluted': true, 'x.__proto__.polluted': true}
734+
});
735+
}).then(delay(100)).then(function() {
736+
var a = {};
737+
expect(a.polluted).toBeUndefined();
738+
}).then(done, done.fail);
739+
});
740+
741+
it('should not pollute prototype - data object', function(done) {
742+
Plotly.newPlot(gd, {
743+
data: [{y: [1, 3, 2]}]
744+
}).then(function() {
745+
return Plotly.animate(gd, {
746+
transition: {duration: 10},
747+
data: [{y: [2, 3, 1], '__proto__.polluted': true}],
748+
traces: [0]
749+
});
750+
}).then(delay(100)).then(function() {
751+
var a = {};
752+
expect(a.polluted).toBeUndefined();
753+
}).then(done, done.fail);
754+
});
755+
});
756+
711757
describe('Animating multiple axes', function() {
712758
var gd;
713759

0 commit comments

Comments
 (0)