diff --git a/.gitignore b/.gitignore index 3df8d93..6a5f369 100644 --- a/.gitignore +++ b/.gitignore @@ -153,3 +153,9 @@ yarn.lock **/._* **/*.pem + +# CLAUDE.md +CLAUDE.md + +# Claude Code directory +.claude diff --git a/index.js b/index.js index f37ab56..a46e37c 100755 --- a/index.js +++ b/index.js @@ -76,6 +76,8 @@ function filter (obj, { protoAction = 'error', constructorAction = 'error', safe if (constructorAction !== 'ignore' && Object.prototype.hasOwnProperty.call(node, 'constructor') && + node.constructor !== null && + typeof node.constructor === 'object' && Object.prototype.hasOwnProperty.call(node.constructor, 'prototype')) { // Avoid calling node.hasOwnProperty directly if (safe === true) { return null diff --git a/test/index.test.js b/test/index.test.js index 7c8b809..ef61d97 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -257,6 +257,27 @@ test('parse', t => { t.end() }) + t.test('handles constructor null safely', t => { + // Test that constructor: null doesn't trigger prototype pollution checks + t.deepEqual( + j.parse('{"constructor": null}', { constructorAction: 'remove' }), + { constructor: null } + ) + + // Test that constructor: null doesn't throw error when using error action + t.deepEqual( + j.parse('{"constructor": null}', { constructorAction: 'error' }), + { constructor: null } + ) + + // Test that constructor: null is preserved when using ignore action + t.deepEqual( + j.parse('{"constructor": null}', { constructorAction: 'ignore' }), + { constructor: null } + ) + t.end() + }) + t.end() })