mathjs
Version:
Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with dif
133 lines (123 loc) • 4.21 kB
JavaScript
import { hasOwnProperty } from './object.js';
/**
* Get a property of a plain object or array
* 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
*/
export function getSafeProperty(object, prop) {
if (isSafeObjectProperty(object, prop) || isSafeArrayProperty(object, prop)) {
return object[prop];
}
if (isSafeMethod(object, prop)) {
throw new Error("Cannot access method \"".concat(prop, "\" as a property"));
}
if (object === null || object === undefined) {
throw new TypeError("Cannot access property \"".concat(prop, "\": object is ").concat(object));
}
throw new Error('No access to property "' + prop + '"');
}
/**
* Set a property on a plain object or array.
* 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
*/
export function setSafeProperty(object, prop, value) {
if (isSafeObjectProperty(object, prop) || isSafeArrayProperty(object, prop)) {
object[prop] = value;
return value;
}
throw new Error("No access to property \"".concat(prop, "\""));
}
/**
* Test whether a property is safe for reading and writing on an object
* For example .constructor and .__proto__ are not safe
* @param {Object} object
* @param {string} prop
* @return {boolean} Returns true when safe
*/
export function isSafeObjectProperty(object, prop) {
if (!isPlainObject(object)) {
return false;
}
return !(prop in Object.prototype);
}
/**
* Test whether a property is safe for reading and writing on an Array
* For example .__proto__ and .constructor are not safe
* @param {unknown} array
* @param {string | number} prop
* @return {boolean} Returns true when safe
*/
export function isSafeArrayProperty(array, prop) {
if (!Array.isArray(array)) {
return false;
}
return typeof prop === 'number' || typeof prop === 'string' && isInteger(prop) || prop === 'length';
}
function isInteger(prop) {
return /^\d+$/.test(prop);
}
/**
* Validate whether a method is safe.
* Throws an error when that's not the case.
* @param {Object} object
* @param {string} method
* @return {function} Returns the method when valid
*/
export function getSafeMethod(object, method) {
if (!isSafeMethod(object, method)) {
throw new Error('No access to method "' + method + '"');
}
return object[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
*/
export function isSafeMethod(object, method) {
if (object === null || object === undefined || typeof object[method] !== 'function') {
return false;
}
// UNSAFE: ghosted
// e.g overridden toString
// Note that IE10 doesn't support __proto__ and we can't do this check there.
if (hasOwnProperty(object, method) && Object.getPrototypeOf && method in Object.getPrototypeOf(object)) {
return false;
}
// SAFE: whitelisted
// e.g toString
if (safeNativeMethods.has(method)) {
return true;
}
// UNSAFE: inherited from Object prototype
// e.g constructor
if (method in Object.prototype) {
// 'in' is used instead of hasOwnProperty for nodejs v0.10
// which is inconsistent on root prototypes. It is safe
// here because Object.prototype is a root object
return false;
}
// UNSAFE: inherited from Function prototype
// e.g call, apply
if (method in Function.prototype) {
// 'in' is used instead of hasOwnProperty for nodejs v0.10
// which is inconsistent on root prototypes. It is safe
// here because Function.prototype is a root object
return false;
}
return true;
}
export function isPlainObject(object) {
return typeof object === 'object' && object && object.constructor === Object;
}
var safeNativeMethods = new Set(['toString', 'valueOf', 'toLocaleString']);