@@ -3092,3 +3092,133 @@ describe('(GHSA-fjxm-vhvc-gcmj) LiveQuery Operator Type Confusion', () => {
30923092 } ) ;
30933093 } ) ;
30943094} ) ;
3095+
3096+ describe ( '(GHSA-5hmj-jcgp-6hff) Protected fields leak via LiveQuery afterEvent trigger' , ( ) => {
3097+ let obj ;
3098+
3099+ beforeEach ( async ( ) => {
3100+ Parse . CoreManager . getLiveQueryController ( ) . setDefaultLiveQueryClient ( null ) ;
3101+ await reconfigureServer ( {
3102+ liveQuery : { classNames : [ 'SecretClass' ] } ,
3103+ startLiveQueryServer : true ,
3104+ verbose : false ,
3105+ silent : true ,
3106+ } ) ;
3107+ Parse . Cloud . afterLiveQueryEvent ( 'SecretClass' , ( ) => { } ) ;
3108+ const config = Config . get ( Parse . applicationId ) ;
3109+ const schemaController = await config . database . loadSchema ( ) ;
3110+ await schemaController . addClassIfNotExists ( 'SecretClass' , {
3111+ secretField : { type : 'String' } ,
3112+ publicField : { type : 'String' } ,
3113+ } ) ;
3114+ await schemaController . updateClass (
3115+ 'SecretClass' ,
3116+ { } ,
3117+ {
3118+ find : { '*' : true } ,
3119+ get : { '*' : true } ,
3120+ create : { '*' : true } ,
3121+ update : { '*' : true } ,
3122+ delete : { '*' : true } ,
3123+ addField : { } ,
3124+ protectedFields : { '*' : [ 'secretField' ] } ,
3125+ }
3126+ ) ;
3127+ obj = new Parse . Object ( 'SecretClass' ) ;
3128+ obj . set ( 'secretField' , 'SENSITIVE_DATA' ) ;
3129+ obj . set ( 'publicField' , 'visible' ) ;
3130+ await obj . save ( null , { useMasterKey : true } ) ;
3131+ } ) ;
3132+
3133+ afterEach ( async ( ) => {
3134+ const client = await Parse . CoreManager . getLiveQueryController ( ) . getDefaultLiveQueryClient ( ) ;
3135+ if ( client ) {
3136+ await client . close ( ) ;
3137+ }
3138+ } ) ;
3139+
3140+ it ( 'should not leak protected fields on update event when afterEvent trigger is registered' , async ( ) => {
3141+ const query = new Parse . Query ( 'SecretClass' ) ;
3142+ const subscription = await query . subscribe ( ) ;
3143+ await Promise . all ( [
3144+ new Promise ( resolve => {
3145+ subscription . on ( 'update' , ( object , original ) => {
3146+ expect ( object . get ( 'secretField' ) ) . toBeUndefined ( ) ;
3147+ expect ( object . get ( 'publicField' ) ) . toBe ( 'updated' ) ;
3148+ expect ( original . get ( 'secretField' ) ) . toBeUndefined ( ) ;
3149+ expect ( original . get ( 'publicField' ) ) . toBe ( 'visible' ) ;
3150+ resolve ( ) ;
3151+ } ) ;
3152+ } ) ,
3153+ obj . save ( { publicField : 'updated' } , { useMasterKey : true } ) ,
3154+ ] ) ;
3155+ } ) ;
3156+
3157+ it ( 'should not leak protected fields on create event when afterEvent trigger is registered' , async ( ) => {
3158+ const query = new Parse . Query ( 'SecretClass' ) ;
3159+ const subscription = await query . subscribe ( ) ;
3160+ await Promise . all ( [
3161+ new Promise ( resolve => {
3162+ subscription . on ( 'create' , object => {
3163+ expect ( object . get ( 'secretField' ) ) . toBeUndefined ( ) ;
3164+ expect ( object . get ( 'publicField' ) ) . toBe ( 'new' ) ;
3165+ resolve ( ) ;
3166+ } ) ;
3167+ } ) ,
3168+ new Parse . Object ( 'SecretClass' ) . save (
3169+ { secretField : 'SECRET' , publicField : 'new' } ,
3170+ { useMasterKey : true }
3171+ ) ,
3172+ ] ) ;
3173+ } ) ;
3174+
3175+ it ( 'should not leak protected fields on delete event when afterEvent trigger is registered' , async ( ) => {
3176+ const query = new Parse . Query ( 'SecretClass' ) ;
3177+ const subscription = await query . subscribe ( ) ;
3178+ await Promise . all ( [
3179+ new Promise ( resolve => {
3180+ subscription . on ( 'delete' , object => {
3181+ expect ( object . get ( 'secretField' ) ) . toBeUndefined ( ) ;
3182+ expect ( object . get ( 'publicField' ) ) . toBe ( 'visible' ) ;
3183+ resolve ( ) ;
3184+ } ) ;
3185+ } ) ,
3186+ obj . destroy ( { useMasterKey : true } ) ,
3187+ ] ) ;
3188+ } ) ;
3189+
3190+ it ( 'should not leak protected fields on enter event when afterEvent trigger is registered' , async ( ) => {
3191+ const query = new Parse . Query ( 'SecretClass' ) ;
3192+ query . equalTo ( 'publicField' , 'match' ) ;
3193+ const subscription = await query . subscribe ( ) ;
3194+ await Promise . all ( [
3195+ new Promise ( resolve => {
3196+ subscription . on ( 'enter' , ( object , original ) => {
3197+ expect ( object . get ( 'secretField' ) ) . toBeUndefined ( ) ;
3198+ expect ( object . get ( 'publicField' ) ) . toBe ( 'match' ) ;
3199+ expect ( original . get ( 'secretField' ) ) . toBeUndefined ( ) ;
3200+ resolve ( ) ;
3201+ } ) ;
3202+ } ) ,
3203+ obj . save ( { publicField : 'match' } , { useMasterKey : true } ) ,
3204+ ] ) ;
3205+ } ) ;
3206+
3207+ it ( 'should not leak protected fields on leave event when afterEvent trigger is registered' , async ( ) => {
3208+ const query = new Parse . Query ( 'SecretClass' ) ;
3209+ query . equalTo ( 'publicField' , 'visible' ) ;
3210+ const subscription = await query . subscribe ( ) ;
3211+ await Promise . all ( [
3212+ new Promise ( resolve => {
3213+ subscription . on ( 'leave' , ( object , original ) => {
3214+ expect ( object . get ( 'secretField' ) ) . toBeUndefined ( ) ;
3215+ expect ( object . get ( 'publicField' ) ) . toBe ( 'changed' ) ;
3216+ expect ( original . get ( 'secretField' ) ) . toBeUndefined ( ) ;
3217+ expect ( original . get ( 'publicField' ) ) . toBe ( 'visible' ) ;
3218+ resolve ( ) ;
3219+ } ) ;
3220+ } ) ,
3221+ obj . save ( { publicField : 'changed' } , { useMasterKey : true } ) ,
3222+ ] ) ;
3223+ } ) ;
3224+ } ) ;
0 commit comments