customs.js (4766B)
1 import { hasOwnProperty } from './object.js'; 2 /** 3 * Get a property of a plain object 4 * Throws an error in case the object is not a plain object or the 5 * property is not defined on the object itself 6 * @param {Object} object 7 * @param {string} prop 8 * @return {*} Returns the property value when safe 9 */ 10 11 function getSafeProperty(object, prop) { 12 // only allow getting safe properties of a plain object 13 if (isPlainObject(object) && isSafeProperty(object, prop)) { 14 return object[prop]; 15 } 16 17 if (typeof object[prop] === 'function' && isSafeMethod(object, prop)) { 18 throw new Error('Cannot access method "' + prop + '" as a property'); 19 } 20 21 throw new Error('No access to property "' + prop + '"'); 22 } 23 /** 24 * Set a property on a plain object. 25 * Throws an error in case the object is not a plain object or the 26 * property would override an inherited property like .constructor or .toString 27 * @param {Object} object 28 * @param {string} prop 29 * @param {*} value 30 * @return {*} Returns the value 31 */ 32 // TODO: merge this function into access.js? 33 34 35 function setSafeProperty(object, prop, value) { 36 // only allow setting safe properties of a plain object 37 if (isPlainObject(object) && isSafeProperty(object, prop)) { 38 object[prop] = value; 39 return value; 40 } 41 42 throw new Error('No access to property "' + prop + '"'); 43 } 44 45 function getSafeProperties(object) { 46 return Object.keys(object).filter(prop => hasOwnProperty(object, prop)); 47 } 48 49 function hasSafeProperty(object, prop) { 50 return prop in object; 51 } 52 /** 53 * Test whether a property is safe to use for an object. 54 * For example .toString and .constructor are not safe 55 * @param {string} prop 56 * @return {boolean} Returns true when safe 57 */ 58 59 60 function isSafeProperty(object, prop) { 61 if (!object || typeof object !== 'object') { 62 return false; 63 } // SAFE: whitelisted 64 // e.g length 65 66 67 if (hasOwnProperty(safeNativeProperties, prop)) { 68 return true; 69 } // UNSAFE: inherited from Object prototype 70 // e.g constructor 71 72 73 if (prop in Object.prototype) { 74 // 'in' is used instead of hasOwnProperty for nodejs v0.10 75 // which is inconsistent on root prototypes. It is safe 76 // here because Object.prototype is a root object 77 return false; 78 } // UNSAFE: inherited from Function prototype 79 // e.g call, apply 80 81 82 if (prop in Function.prototype) { 83 // 'in' is used instead of hasOwnProperty for nodejs v0.10 84 // which is inconsistent on root prototypes. It is safe 85 // here because Function.prototype is a root object 86 return false; 87 } 88 89 return true; 90 } 91 /** 92 * Validate whether a method is safe. 93 * Throws an error when that's not the case. 94 * @param {Object} object 95 * @param {string} method 96 */ 97 // TODO: merge this function into assign.js? 98 99 100 function validateSafeMethod(object, method) { 101 if (!isSafeMethod(object, method)) { 102 throw new Error('No access to method "' + method + '"'); 103 } 104 } 105 /** 106 * Check whether a method is safe. 107 * Throws an error when that's not the case (for example for `constructor`). 108 * @param {Object} object 109 * @param {string} method 110 * @return {boolean} Returns true when safe, false otherwise 111 */ 112 113 114 function isSafeMethod(object, method) { 115 if (object === null || object === undefined || typeof object[method] !== 'function') { 116 return false; 117 } // UNSAFE: ghosted 118 // e.g overridden toString 119 // Note that IE10 doesn't support __proto__ and we can't do this check there. 120 121 122 if (hasOwnProperty(object, method) && Object.getPrototypeOf && method in Object.getPrototypeOf(object)) { 123 return false; 124 } // SAFE: whitelisted 125 // e.g toString 126 127 128 if (hasOwnProperty(safeNativeMethods, method)) { 129 return true; 130 } // UNSAFE: inherited from Object prototype 131 // e.g constructor 132 133 134 if (method in Object.prototype) { 135 // 'in' is used instead of hasOwnProperty for nodejs v0.10 136 // which is inconsistent on root prototypes. It is safe 137 // here because Object.prototype is a root object 138 return false; 139 } // UNSAFE: inherited from Function prototype 140 // e.g call, apply 141 142 143 if (method in Function.prototype) { 144 // 'in' is used instead of hasOwnProperty for nodejs v0.10 145 // which is inconsistent on root prototypes. It is safe 146 // here because Function.prototype is a root object 147 return false; 148 } 149 150 return true; 151 } 152 153 function isPlainObject(object) { 154 return typeof object === 'object' && object && object.constructor === Object; 155 } 156 157 var safeNativeProperties = { 158 length: true, 159 name: true 160 }; 161 var safeNativeMethods = { 162 toString: true, 163 valueOf: true, 164 toLocaleString: true 165 }; 166 export { getSafeProperty }; 167 export { setSafeProperty }; 168 export { isSafeProperty }; 169 export { hasSafeProperty }; 170 export { getSafeProperties }; 171 export { validateSafeMethod }; 172 export { isSafeMethod }; 173 export { isPlainObject };