index.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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. };