customs.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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. function getSafeProperty(object, prop) {
  11. // only allow getting safe properties of a plain object
  12. if (isPlainObject(object) && isSafeProperty(object, prop)) {
  13. return object[prop];
  14. }
  15. if (typeof object[prop] === 'function' && isSafeMethod(object, prop)) {
  16. throw new Error('Cannot access method "' + prop + '" as a property');
  17. }
  18. throw new Error('No access to property "' + prop + '"');
  19. }
  20. /**
  21. * Set a property on a plain object.
  22. * Throws an error in case the object is not a plain object or the
  23. * property would override an inherited property like .constructor or .toString
  24. * @param {Object} object
  25. * @param {string} prop
  26. * @param {*} value
  27. * @return {*} Returns the value
  28. */
  29. // TODO: merge this function into access.js?
  30. function setSafeProperty(object, prop, value) {
  31. // only allow setting safe properties of a plain object
  32. if (isPlainObject(object) && isSafeProperty(object, prop)) {
  33. object[prop] = value;
  34. return value;
  35. }
  36. throw new Error('No access to property "' + prop + '"');
  37. }
  38. function getSafeProperties(object) {
  39. return Object.keys(object).filter(prop => hasOwnProperty(object, prop));
  40. }
  41. function hasSafeProperty(object, prop) {
  42. return prop in object;
  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. */
  50. function 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. * Validate whether a method is safe.
  79. * Throws an error when that's not the case.
  80. * @param {Object} object
  81. * @param {string} method
  82. */
  83. // TODO: merge this function into assign.js?
  84. function validateSafeMethod(object, method) {
  85. if (!isSafeMethod(object, method)) {
  86. throw new Error('No access to method "' + method + '"');
  87. }
  88. }
  89. /**
  90. * Check whether a method is safe.
  91. * Throws an error when that's not the case (for example for `constructor`).
  92. * @param {Object} object
  93. * @param {string} method
  94. * @return {boolean} Returns true when safe, false otherwise
  95. */
  96. function isSafeMethod(object, method) {
  97. if (object === null || object === undefined || typeof object[method] !== 'function') {
  98. return false;
  99. }
  100. // UNSAFE: ghosted
  101. // e.g overridden toString
  102. // Note that IE10 doesn't support __proto__ and we can't do this check there.
  103. if (hasOwnProperty(object, method) && Object.getPrototypeOf && method in Object.getPrototypeOf(object)) {
  104. return false;
  105. }
  106. // SAFE: whitelisted
  107. // e.g toString
  108. if (hasOwnProperty(safeNativeMethods, method)) {
  109. return true;
  110. }
  111. // UNSAFE: inherited from Object prototype
  112. // e.g constructor
  113. if (method in Object.prototype) {
  114. // 'in' is used instead of hasOwnProperty for nodejs v0.10
  115. // which is inconsistent on root prototypes. It is safe
  116. // here because Object.prototype is a root object
  117. return false;
  118. }
  119. // UNSAFE: inherited from Function prototype
  120. // e.g call, apply
  121. if (method in Function.prototype) {
  122. // 'in' is used instead of hasOwnProperty for nodejs v0.10
  123. // which is inconsistent on root prototypes. It is safe
  124. // here because Function.prototype is a root object
  125. return false;
  126. }
  127. return true;
  128. }
  129. function isPlainObject(object) {
  130. return typeof object === 'object' && object && object.constructor === Object;
  131. }
  132. var safeNativeProperties = {
  133. length: true,
  134. name: true
  135. };
  136. var safeNativeMethods = {
  137. toString: true,
  138. valueOf: true,
  139. toLocaleString: true
  140. };
  141. export { getSafeProperty };
  142. export { setSafeProperty };
  143. export { isSafeProperty };
  144. export { hasSafeProperty };
  145. export { getSafeProperties };
  146. export { validateSafeMethod };
  147. export { isSafeMethod };
  148. export { isPlainObject };