mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-19 14:46:51 +01:00
Use Object.create(null) as merge target, to prevent prototype pollution (#7917)
Use Object.create(null) as merge target to prevent polluting `Object.prototype`
This commit is contained in:
@@ -44,8 +44,8 @@ function mergeScaleConfig(config, options) {
|
||||
const chartDefaults = defaults[config.type] || {scales: {}};
|
||||
const configScales = options.scales || {};
|
||||
const chartIndexAxis = getIndexAxis(config.type, options);
|
||||
const firstIDs = {};
|
||||
const scales = {};
|
||||
const firstIDs = Object.create(null);
|
||||
const scales = Object.create(null);
|
||||
|
||||
// First figure out first scale id's per axis.
|
||||
Object.keys(configScales).forEach(id => {
|
||||
@@ -53,12 +53,12 @@ function mergeScaleConfig(config, options) {
|
||||
const axis = determineAxis(id, scaleConf);
|
||||
const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis);
|
||||
firstIDs[axis] = firstIDs[axis] || id;
|
||||
scales[id] = mergeIf({axis}, [scaleConf, chartDefaults.scales[axis], chartDefaults.scales[defaultId]]);
|
||||
scales[id] = mergeIf(Object.create(null), [{axis}, scaleConf, chartDefaults.scales[axis], chartDefaults.scales[defaultId]]);
|
||||
});
|
||||
|
||||
// Backward compatibility
|
||||
if (options.scale) {
|
||||
scales[options.scale.id || 'r'] = mergeIf({axis: 'r'}, [options.scale, chartDefaults.scales.r]);
|
||||
scales[options.scale.id || 'r'] = mergeIf(Object.create(null), [{axis: 'r'}, options.scale, chartDefaults.scales.r]);
|
||||
firstIDs.r = firstIDs.r || options.scale.id || 'r';
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ function mergeScaleConfig(config, options) {
|
||||
Object.keys(defaultScaleOptions).forEach(defaultID => {
|
||||
const axis = getAxisFromDefaultScaleID(defaultID, indexAxis);
|
||||
const id = dataset[axis + 'AxisID'] || firstIDs[axis] || axis;
|
||||
scales[id] = scales[id] || {};
|
||||
scales[id] = scales[id] || Object.create(null);
|
||||
mergeIf(scales[id], [{axis}, configScales[id], defaultScaleOptions[defaultID]]);
|
||||
});
|
||||
});
|
||||
@@ -91,7 +91,7 @@ function mergeScaleConfig(config, options) {
|
||||
* a deep copy of the result, thus doesn't alter inputs.
|
||||
*/
|
||||
function mergeConfig(...args/* config objects ... */) {
|
||||
return merge({}, args, {
|
||||
return merge(Object.create(null), args, {
|
||||
merger(key, target, source, options) {
|
||||
if (key !== 'scales' && key !== 'scale') {
|
||||
_merger(key, target, source, options);
|
||||
@@ -118,8 +118,8 @@ function initConfig(config) {
|
||||
|
||||
options.scales = scaleConfig;
|
||||
|
||||
options.title = (options.title !== false) && merge({}, [defaults.plugins.title, options.title]);
|
||||
options.tooltips = (options.tooltips !== false) && merge({}, [defaults.plugins.tooltip, options.tooltips]);
|
||||
options.title = (options.title !== false) && merge(Object.create(null), [defaults.plugins.title, options.title]);
|
||||
options.tooltips = (options.tooltips !== false) && merge(Object.create(null), [defaults.plugins.tooltip, options.tooltips]);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ export default class DatasetController {
|
||||
*/
|
||||
configure() {
|
||||
const me = this;
|
||||
me._config = merge({}, [
|
||||
me._config = merge(Object.create(null), [
|
||||
me.chart.options[me._type].datasets,
|
||||
me.getDataset(),
|
||||
], {
|
||||
|
||||
@@ -12,7 +12,7 @@ function getScope(node, key) {
|
||||
const keys = key.split('.');
|
||||
for (let i = 0, n = keys.length; i < n; ++i) {
|
||||
const k = keys[i];
|
||||
node = node[k] || (node[k] = {});
|
||||
node = node[k] || (node[k] = Object.create(null));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import defaults from './core.defaults';
|
||||
import {each} from '../helpers/helpers.core';
|
||||
import {each, isObject} from '../helpers/helpers.core';
|
||||
import {toPadding} from '../helpers/helpers.options';
|
||||
|
||||
/**
|
||||
@@ -84,6 +84,10 @@ function updateDims(chartArea, params, layout) {
|
||||
const box = layout.box;
|
||||
const maxPadding = chartArea.maxPadding;
|
||||
|
||||
if (isObject(layout.pos)) {
|
||||
// dynamically placed boxes are not considered
|
||||
return;
|
||||
}
|
||||
if (layout.size) {
|
||||
// this layout was already counted for, lets first reduce old size
|
||||
chartArea[layout.pos] -= layout.size;
|
||||
|
||||
@@ -157,7 +157,7 @@ export function clone(source) {
|
||||
}
|
||||
|
||||
if (isObject(source)) {
|
||||
const target = {};
|
||||
const target = Object.create(null);
|
||||
const keys = Object.keys(source);
|
||||
const klen = keys.length;
|
||||
let k = 0;
|
||||
|
||||
@@ -55,11 +55,14 @@ function parseFillOption(line) {
|
||||
*/
|
||||
function decodeFill(line, index, count) {
|
||||
const fill = parseFillOption(line);
|
||||
let target = parseFloat(fill);
|
||||
|
||||
if (isObject(fill)) {
|
||||
return isNaN(fill.value) ? false : fill;
|
||||
} else if (isFinite(target) && Math.floor(target) === target) {
|
||||
}
|
||||
|
||||
let target = parseFloat(fill);
|
||||
|
||||
if (isFinite(target) && Math.floor(target) === target) {
|
||||
if (fill[0] === '-' || fill[0] === '+') {
|
||||
target = index + target;
|
||||
}
|
||||
|
||||
@@ -629,7 +629,7 @@ export class Legend extends Element {
|
||||
}
|
||||
|
||||
function resolveOptions(options) {
|
||||
return options !== false && merge({}, [defaults.plugins.legend, options]);
|
||||
return options !== false && merge(Object.create(null), [defaults.plugins.legend, options]);
|
||||
}
|
||||
|
||||
function createNewLegendAndAttach(chart, legendOpts) {
|
||||
|
||||
@@ -139,7 +139,7 @@ function createTooltipItem(chart, item) {
|
||||
*/
|
||||
function resolveOptions(options, fallbackFont) {
|
||||
|
||||
options = merge({}, [defaults.plugins.tooltip, options]);
|
||||
options = merge(Object.create(null), [defaults.plugins.tooltip, options]);
|
||||
|
||||
options.bodyFont = toFont(options.bodyFont, fallbackFont);
|
||||
options.titleFont = toFont(options.titleFont, fallbackFont);
|
||||
|
||||
@@ -13,4 +13,12 @@ describe('Core helper tests', function() {
|
||||
expect(helpers.uid()).toBe(uid + 2);
|
||||
expect(helpers.uid()).toBe(uid + 3);
|
||||
});
|
||||
|
||||
describe('clone', function() {
|
||||
it('should not allow prototype pollution', function() {
|
||||
const test = helpers.clone(JSON.parse('{"__proto__":{"polluted": true}}'));
|
||||
expect(test.prototype).toBeUndefined();
|
||||
expect(Object.prototype.polluted).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user