Spaces:
Sleeping
Sleeping
; | |
const internals = { | |
suspectRx: /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\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]); | |
} | |
} | |
} | |
} | |
}; | |
exports.safeParse = function (text, reviver) { | |
try { | |
return exports.parse(text, reviver); | |
} | |
catch (ignoreError) { | |
return null; | |
} | |
}; | |