index.js 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  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, ...args) {
  6. // Normalize arguments
  7. const firstOptions = typeof args[0] === 'object' && args[0];
  8. const reviver = args.length > 1 || !firstOptions ? args[0] : undefined;
  9. const options = (args.length > 1 && args[1]) || firstOptions || {};
  10. // Parse normally, allowing exceptions
  11. const obj = JSON.parse(text, reviver);
  12. // options.protoAction: 'error' (default) / 'remove' / 'ignore'
  13. if (options.protoAction === 'ignore') {
  14. return obj;
  15. }
  16. // Ignore null and non-objects
  17. if (!obj ||
  18. typeof obj !== 'object') {
  19. return obj;
  20. }
  21. // Check original string for potential exploit
  22. if (!text.match(internals.suspectRx)) {
  23. return obj;
  24. }
  25. // Scan result for proto keys
  26. exports.scan(obj, options);
  27. return obj;
  28. };
  29. exports.scan = function (obj, options = {}) {
  30. let next = [obj];
  31. while (next.length) {
  32. const nodes = next;
  33. next = [];
  34. for (const node of nodes) {
  35. if (Object.prototype.hasOwnProperty.call(node, '__proto__')) { // Avoid calling node.hasOwnProperty directly
  36. if (options.protoAction !== 'remove') {
  37. throw new SyntaxError('Object contains forbidden prototype property');
  38. }
  39. delete node.__proto__;
  40. }
  41. for (const key in node) {
  42. const value = node[key];
  43. if (value &&
  44. typeof value === 'object') {
  45. next.push(node[key]);
  46. }
  47. }
  48. }
  49. }
  50. };
  51. exports.safeParse = function (text, reviver) {
  52. try {
  53. return exports.parse(text, reviver);
  54. }
  55. catch (ignoreError) {
  56. return null;
  57. }
  58. };