mathjs/lib/utils/customs.js

132 lines
4.1 KiB
JavaScript

'use strict';
var hasOwnProperty = require('./object').hasOwnProperty;
/**
* Get a property of a plain object
* Throws an error in case the object is not a plain object or the
* property is not defined on the object itself
* @param {Object} object
* @param {string} prop
* @return {*} Returns the property value when safe
*/
function getSafeProperty (object, prop) {
// only allow getting properties of a plain object
if (isPlainObject(object)) {
// only allow getting properties defined on the object itself,
// not inherited from it's prototype.
if (hasOwnProperty(object, prop)) {
return object[prop];
}
if (!(prop in object)) {
// this is a not existing property on a plain object
return undefined;
}
}
if (typeof object[prop] === 'function' && isSafeMethod(object, prop)) {
throw new Error('Cannot access method "' + prop + '" as a property');
}
throw new Error('No access to property "' + prop + '"');
}
/**
* Set a property on a plain object.
* Throws an error in case the object is not a plain object or the
* property would override an inherited property like .constructor or .toString
* @param {Object} object
* @param {string} prop
* @param {*} value
* @return {*} Returns the value
*/
// TODO: merge this function into access.js?
function setSafeProperty (object, prop, value) {
// only allow setting properties of a plain object
if (isPlainObject(object)) {
// only allow setting properties defined on the object itself,
// not inherited from it's prototype.
if (prop in object) {
// property already exists
// override when the property is defined on the object itself.
// don't allow overriding inherited properties like .constructor or .toString
if (hasOwnProperty(object, prop)) {
return object[prop] = value;
}
}
else {
// this is a new property, that's just ok
return object[prop] = value;
}
}
throw new Error('No access to property "' + prop + '"');
}
/**
* Test whether a property is safe to use for an object.
* For example .toString and .constructor are not safe
* @param {string} prop
* @return {boolean} Returns true when safe
*/
function isSafeProperty (prop) {
return !(prop in {});
}
/**
* Validate whether a method is safe.
* Throws an error when that's not the case.
* @param {Object} object
* @param {string} method
*/
// TODO: merge this function into assign.js?
function validateSafeMethod (object, method) {
if (!isSafeMethod(object, method)) {
throw new Error('No access to method "' + method + '"');
}
}
/**
* Check whether a method is safe.
* Throws an error when that's not the case (for example for `constructor`).
* @param {Object} object
* @param {string} method
* @return {boolean} Returns true when safe, false otherwise
*/
function isSafeMethod (object, method) {
// test for plain functions defined on the object (instead of a method)
if (hasOwnProperty(object, method)) {
return isPlainObject(object);
}
else {
// only allow methods:
// - defined on the prototype of this object
// - not defined on the prototype of native Object
// i.e. constructor, __defineGetter__, hasOwnProperty, etc. are not allowed
// - calling methods on a function (like bind) is not allowed
// - A few safe native methods are allowed: toString, valueOf, toLocaleString
return (object && typeof object !== 'function' &&
(hasOwnProperty(object.constructor.prototype, method) ||
hasOwnProperty(object.__proto__, method)) &&
(!hasOwnProperty(Object.prototype, method) || hasOwnProperty(safeNativeMethods, method)));
}
}
function isPlainObject (object) {
return typeof object === 'object' && object && object.constructor === Object;
}
var safeNativeMethods = {
toString: true,
valueOf: true,
toLocaleString: true
};
exports.getSafeProperty = getSafeProperty;
exports.setSafeProperty = setSafeProperty;
exports.isSafeProperty = isSafeProperty;
exports.validateSafeMethod = validateSafeMethod;
exports.isSafeMethod = isSafeMethod;
exports.isPlainObject = isPlainObject;