@@ -377,3 +377,43 @@ test('required key not in properties + additionalProperties produces valid JSON'
377377 t . assert . equal ( out , '{"str":"x"}' )
378378 t . assert . deepStrictEqual ( JSON . parse ( out ) , { str : 'x' } )
379379} )
380+
381+ test ( 'additionalProperties set to false ignores properties not matched by patternProperties' , ( t ) => {
382+ // Regression: `additionalProperties: false` is documented to drop every
383+ // property that is not listed in `properties` or matched by
384+ // `patternProperties`. Combined with `patternProperties`, the generated
385+ // code used to fall through to the additionalProperties branch with a
386+ // boolean `false` schema, which serialized unmatched properties with
387+ // `JSON.stringify(value)` and leaked them into the output:
388+ // {"nickname":"nick","matchnum":3,"leaked":"secret"}
389+ t . plan ( 2 )
390+ const stringify = build ( {
391+ type : 'object' ,
392+ properties : {
393+ nickname : { type : 'string' }
394+ } ,
395+ patternProperties : {
396+ num : { type : 'number' }
397+ } ,
398+ additionalProperties : false
399+ } )
400+
401+ const out = stringify ( { nickname : 'nick' , matchnum : 3 , leaked : 'secret' } )
402+ t . assert . equal ( out , '{"nickname":"nick","matchnum":3}' )
403+ t . assert . deepStrictEqual ( JSON . parse ( out ) , { nickname : 'nick' , matchnum : 3 } )
404+ } )
405+
406+ test ( 'additionalProperties set to false without declared properties ignores unmatched properties' , ( t ) => {
407+ t . plan ( 2 )
408+ const stringify = build ( {
409+ type : 'object' ,
410+ patternProperties : {
411+ '^str' : { type : 'string' }
412+ } ,
413+ additionalProperties : false
414+ } )
415+
416+ const out = stringify ( { str1 : 'a' , leaked : 'secret' } )
417+ t . assert . equal ( out , '{"str1":"a"}' )
418+ t . assert . deepStrictEqual ( JSON . parse ( out ) , { str1 : 'a' } )
419+ } )
0 commit comments