'use strict'; const internals = { suspectRx: /"(?:_|\\u005f)(?:_|\\u005f)(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006f)(?:t|\\u0074)(?:o|\\u006f)(?:_|\\u005f)(?:_|\\u005f)"\s*\:/ }; exports.parse = function (text, reviver, options) { // Normalize arguments if (!options) { if (reviver && typeof reviver === 'object') { options = reviver; reviver = undefined; } else { options = {}; } } // Parse normally, allowing exceptions const obj = JSON.parse(text, reviver); // options.protoAction: 'error' (default) / 'remove' / 'ignore' if (options.protoAction === 'ignore') { return obj; } // Ignore null and non-objects if (!obj || typeof obj !== 'object') { return obj; } // Check original string for potential exploit if (!text.match(internals.suspectRx)) { return obj; } // Scan result for proto keys exports.scan(obj, options); return obj; }; exports.scan = function (obj, options) { options = options || {}; let next = [obj]; while (next.length) { const nodes = next; next = []; for (const node of nodes) { if (Object.prototype.hasOwnProperty.call(node, '__proto__')) { // Avoid calling node.hasOwnProperty directly if (options.protoAction !== 'remove') { throw new SyntaxError('Object contains forbidden prototype property'); } delete node.__proto__; } for (const key in node) { const value = node[key]; if (value && typeof value === 'object') { next.push(node[key]); } } } } };