@@ -524,3 +524,126 @@ test('scan handles optional options', t => {
524524 t . doesNotThrow ( ( ) => j . scan ( { a : 'b' } ) )
525525 t . end ( )
526526} )
527+
528+ test ( 'safe option' , t => {
529+ t . test ( 'parse with safe=true returns null on __proto__' , t => {
530+ const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 } }'
531+ t . strictEqual ( j . parse ( text , { safe : true } ) , null )
532+ t . end ( )
533+ } )
534+
535+ t . test ( 'parse with safe=true returns null on constructor' , t => {
536+ const text = '{ "a": 5, "b": 6, "constructor": {"prototype": {"bar": "baz"}} }'
537+ t . strictEqual ( j . parse ( text , { safe : true } ) , null )
538+ t . end ( )
539+ } )
540+
541+ t . test ( 'parse with safe=true returns object when valid' , t => {
542+ const text = '{ "a": 5, "b": 6 }'
543+ t . deepEqual ( j . parse ( text , { safe : true } ) , { a : 5 , b : 6 } )
544+ t . end ( )
545+ } )
546+
547+ t . test ( 'parse with safe=true and reviver' , t => {
548+ const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 } }'
549+ const reviver = ( _key , value ) => {
550+ return typeof value === 'number' ? value + 1 : value
551+ }
552+ t . strictEqual ( j . parse ( text , reviver , { safe : true } ) , null )
553+ t . end ( )
554+ } )
555+
556+ t . test ( 'parse with safe=true and protoAction=remove returns null' , t => {
557+ const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 } }'
558+ t . strictEqual ( j . parse ( text , { safe : true , protoAction : 'remove' } ) , null )
559+ t . end ( )
560+ } )
561+
562+ t . test ( 'parse with safe=true and constructorAction=remove returns null' , t => {
563+ const text = '{ "a": 5, "b": 6, "constructor": {"prototype": {"bar": "baz"}} }'
564+ t . strictEqual ( j . parse ( text , { safe : true , constructorAction : 'remove' } ) , null )
565+ t . end ( )
566+ } )
567+
568+ t . test ( 'parse with safe=false throws on __proto__' , t => {
569+ const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 } }'
570+ t . throws ( ( ) => j . parse ( text , { safe : false } ) , SyntaxError )
571+ t . end ( )
572+ } )
573+
574+ t . test ( 'parse with safe=false throws on constructor' , t => {
575+ const text = '{ "a": 5, "b": 6, "constructor": {"prototype": {"bar": "baz"}} }'
576+ t . throws ( ( ) => j . parse ( text , { safe : false } ) , SyntaxError )
577+ t . end ( )
578+ } )
579+
580+ t . test ( 'scan with safe=true returns null on __proto__' , t => {
581+ const obj = JSON . parse ( '{ "a": 5, "b": 6, "__proto__": { "x": 7 } }' )
582+ t . strictEqual ( j . scan ( obj , { safe : true } ) , null )
583+ t . end ( )
584+ } )
585+
586+ t . test ( 'scan with safe=true returns null on constructor' , t => {
587+ const obj = JSON . parse ( '{ "a": 5, "b": 6, "constructor": {"prototype": {"bar": "baz"}} }' )
588+ t . strictEqual ( j . scan ( obj , { safe : true } ) , null )
589+ t . end ( )
590+ } )
591+
592+ t . test ( 'scan with safe=true returns object when valid' , t => {
593+ const obj = { a : 5 , b : 6 }
594+ t . deepEqual ( j . scan ( obj , { safe : true } ) , { a : 5 , b : 6 } )
595+ t . end ( )
596+ } )
597+
598+ t . test ( 'scan with safe=false throws on __proto__' , t => {
599+ const obj = JSON . parse ( '{ "a": 5, "b": 6, "__proto__": { "x": 7 } }' )
600+ t . throws ( ( ) => j . scan ( obj , { safe : false } ) , SyntaxError )
601+ t . end ( )
602+ } )
603+
604+ t . test ( 'scan with safe=false throws on constructor' , t => {
605+ const obj = JSON . parse ( '{ "a": 5, "b": 6, "constructor": {"prototype": {"bar": "baz"}} }' )
606+ t . throws ( ( ) => j . scan ( obj , { safe : false } ) , SyntaxError )
607+ t . end ( )
608+ } )
609+
610+ t . test ( 'parse with safe=true returns null on nested __proto__' , t => {
611+ const text = '{ "a": 5, "c": { "d": 0, "__proto__": { "y": 8 } } }'
612+ t . strictEqual ( j . parse ( text , { safe : true } ) , null )
613+ t . end ( )
614+ } )
615+
616+ t . test ( 'parse with safe=true returns null on nested constructor' , t => {
617+ const text = '{ "a": 5, "c": { "d": 0, "constructor": {"prototype": {"bar": "baz"}} } }'
618+ t . strictEqual ( j . parse ( text , { safe : true } ) , null )
619+ t . end ( )
620+ } )
621+
622+ t . test ( 'parse with safe=true and protoAction=ignore returns object' , t => {
623+ const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 } }'
624+ t . deepEqual (
625+ j . parse ( text , { safe : true , protoAction : 'ignore' } ) ,
626+ JSON . parse ( text )
627+ )
628+ t . end ( )
629+ } )
630+
631+ t . test ( 'parse with safe=true and constructorAction=ignore returns object' , t => {
632+ const text = '{ "a": 5, "b": 6, "constructor": {"prototype": {"bar": "baz"}} }'
633+ t . deepEqual (
634+ j . parse ( text , { safe : true , constructorAction : 'ignore' } ) ,
635+ JSON . parse ( text )
636+ )
637+ t . end ( )
638+ } )
639+
640+ t . test ( 'should reset stackTraceLimit with safe option' , t => {
641+ const text = '{ "a": 5, "b": 6, "__proto__": { "x": 7 } }'
642+ Error . stackTraceLimit = 42
643+ t . strictEqual ( j . parse ( text , { safe : true } ) , null )
644+ t . same ( Error . stackTraceLimit , 42 )
645+ t . end ( )
646+ } )
647+
648+ t . end ( )
649+ } )
0 commit comments