Skip to content

Commit f4db4db

Browse files
committed
feat: enhance JSON parsing options to include constructor poisoning handling
Signed-off-by: Sebastian Beltran <[email protected]>
1 parent 2158ee8 commit f4db4db

File tree

2 files changed

+31
-5
lines changed

2 files changed

+31
-5
lines changed

lib/types/json.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function json (options) {
5656

5757
var reviver = options?.reviver
5858
var strict = options?.strict !== false
59-
const protoPoisoning = options?.onProtoPoisoning || 'ignore'
59+
const poisoningOptions = { onProtoPoisoning: options?.onProto?.onProtoPoisoning || 'ignore', onConstructorPoisoning: options?.onProto?.onConstructorPoisoning || 'ignore' }
6060

6161
function parse (body) {
6262
if (body.length === 0) {
@@ -76,7 +76,7 @@ function json (options) {
7676

7777
try {
7878
debug('parse json')
79-
return secureJson.parse(body, reviver, { protoAction: protoPoisoning })
79+
return secureJson.parse(body, reviver, { protoAction: poisoningOptions.onProtoPoisoning, constructorAction: poisoningOptions.onConstructorPoisoning })
8080
} catch (e) {
8181
throw normalizeJsonSyntaxError(e, {
8282
message: e.message,

test/json.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -724,29 +724,55 @@ describe('bodyParser.json()', function () {
724724

725725
describe('prototype poisoning', function () {
726726
it('should parse __proto__ when protoAction is set to ignore', function (done) {
727-
request(createServer({ onProtoPoisoning: 'ignore' }))
727+
request(createServer({ onProto: { onProtoPoisoning: 'ignore' }}))
728728
.post('/')
729729
.set('Content-Type', 'application/json')
730730
.send('{"user":"tobi","__proto__":{"x":7}}')
731731
.expect(200, '{"user":"tobi","__proto__":{"x":7}}', done)
732732
})
733733

734734
it('should throw when protoAction is set to error', function (done) {
735-
request(createServer({ onProtoPoisoning: 'error' }))
735+
request(createServer({ onProto: { onProtoPoisoning: 'error' }}))
736736
.post('/')
737737
.set('Content-Type', 'application/json')
738738
.send('{"user":"tobi","__proto__":{"x":7}}')
739739
.expect(400, '[entity.parse.failed] Object contains forbidden prototype property', done)
740740
})
741741

742742
it('should remove prototype poisoning when protoAction is set to remove', function (done) {
743-
request(createServer({ onProtoPoisoning: 'remove' }))
743+
request(createServer({ onProto: { onProtoPoisoning: 'remove' }}))
744744
.post('/')
745745
.set('Content-Type', 'application/json')
746746
.send('{"user":"tobi","__proto__":{"x":7}}')
747747
.expect(200, '{"user":"tobi"}', done)
748748
})
749749
})
750+
751+
describe('constructor poisoning', function () {
752+
it('should parse constructor when protoAction is set to ignore', function (done) {
753+
request(createServer({ onProto: { onConstructorPoisoning: 'ignore' }}))
754+
.post('/')
755+
.set('Content-Type', 'application/json')
756+
.send('{"user":"tobi","constructor":{"prototype":{"bar":"baz"}}}')
757+
.expect(200, '{"user":"tobi","constructor":{"prototype":{"bar":"baz"}}}', done)
758+
})
759+
760+
it('should throw when protoAction is set to error', function (done) {
761+
request(createServer({ onProto: { onConstructorPoisoning: 'error' }}))
762+
.post('/')
763+
.set('Content-Type', 'application/json')
764+
.send('{"user":"tobi","constructor":{"prototype":{"bar":"baz"}}}')
765+
.expect(400, '[entity.parse.failed] Object contains forbidden prototype property', done)
766+
})
767+
768+
it('should remove prototype poisoning when protoAction is set to remove', function (done) {
769+
request(createServer({ onProto: { onConstructorPoisoning: 'remove' }}))
770+
.post('/')
771+
.set('Content-Type', 'application/json')
772+
.send('{"user":"tobi","constructor":{"prototype":{"bar":"baz"}}}')
773+
.expect(200, '{"user":"tobi"}', done)
774+
})
775+
})
750776
})
751777

752778
function createServer (opts) {

0 commit comments

Comments
 (0)