index.js 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. 'use strict';
  2. const internals = {
  3. suspectRx: /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*\:/
  4. };
  5. exports.parse = function (text, reviver, options) {
  6. // Normalize arguments
  7. if (!options) {
  8. if (reviver &&
  9. typeof reviver === 'object') {
  10. options = reviver;
  11. reviver = undefined;
  12. }
  13. else {
  14. options = {};
  15. }
  16. }
  17. // Parse normally, allowing exceptions
  18. const obj = JSON.parse(text, reviver);
  19. // options.protoAction: 'error' (default) / 'remove' / 'ignore'
  20. if (options.protoAction === 'ignore') {
  21. return obj;
  22. }
  23. // Ignore null and non-objects
  24. if (!obj ||
  25. typeof obj !== 'object') {
  26. return obj;
  27. }
  28. // Check original string for potential exploit
  29. if (!text.match(internals.suspectRx)) {
  30. return obj;
  31. }
  32. // Scan result for proto keys
  33. exports.scan(obj, options);
  34. return obj;
  35. };
  36. exports.scan = function (obj, options) {
  37. options = options || {};
  38. let next = [obj];
  39. while (next.length) {
  40. const nodes = next;
  41. next = [];
  42. for (const node of nodes) {
  43. if (Object.prototype.hasOwnProperty.call(node, '__proto__')) { // Avoid calling node.hasOwnProperty directly
  44. if (options.protoAction !== 'remove') {
  45. throw new SyntaxError('Object contains forbidden prototype property');
  46. }
  47. delete node.__proto__;
  48. }
  49. for (const key in node) {
  50. const value = node[key];
  51. if (value &&
  52. typeof value === 'object') {
  53. next.push(node[key]);
  54. }
  55. }
  56. }
  57. }
  58. };
  59. exports.safeParse = function (text, reviver) {
  60. try {
  61. return exports.parse(text, reviver);
  62. }
  63. catch (ignoreError) {
  64. return null;
  65. }
  66. };