UNPKG

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