UNPKG

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