@@ -251,6 +251,71 @@ describe('Vulnerabilities', () => {
251251 } ) ;
252252 } ) ;
253253
254+ describe ( '(GHSA-4263-jgmp-7pf4) Cloud function prototype chain dispatch via registered function' , ( ) => {
255+ const headers = {
256+ 'Content-Type' : 'application/json' ,
257+ 'X-Parse-Application-Id' : 'test' ,
258+ 'X-Parse-REST-API-Key' : 'rest' ,
259+ } ;
260+
261+ beforeEach ( ( ) => {
262+ Parse . Cloud . define ( 'legitimateFunction' , ( ) => 'ok' ) ;
263+ } ) ;
264+
265+ it ( 'rejects prototype chain traversal from a registered function name' , async ( ) => {
266+ const response = await request ( {
267+ headers,
268+ method : 'POST' ,
269+ url : 'http://localhost:8378/1/functions/legitimateFunction.__proto__.__proto__.constructor' ,
270+ body : JSON . stringify ( { } ) ,
271+ } ) . catch ( e => e ) ;
272+ expect ( response . status ) . toBe ( 400 ) ;
273+ const text = JSON . parse ( response . text ) ;
274+ expect ( text . code ) . toBe ( Parse . Error . SCRIPT_FAILED ) ;
275+ expect ( text . error ) . toContain ( 'Invalid function' ) ;
276+ } ) ;
277+
278+ it ( 'rejects prototype chain traversal via single __proto__ from a registered function' , async ( ) => {
279+ const response = await request ( {
280+ headers,
281+ method : 'POST' ,
282+ url : 'http://localhost:8378/1/functions/legitimateFunction.__proto__.constructor' ,
283+ body : JSON . stringify ( { } ) ,
284+ } ) . catch ( e => e ) ;
285+ expect ( response . status ) . toBe ( 400 ) ;
286+ const text = JSON . parse ( response . text ) ;
287+ expect ( text . code ) . toBe ( Parse . Error . SCRIPT_FAILED ) ;
288+ expect ( text . error ) . toContain ( 'Invalid function' ) ;
289+ } ) ;
290+
291+ it ( 'does not crash the server when prototype chain traversal is attempted' , async ( ) => {
292+ const maliciousNames = [
293+ 'legitimateFunction.__proto__.__proto__.constructor' ,
294+ 'legitimateFunction.__proto__.constructor' ,
295+ 'legitimateFunction.constructor' ,
296+ 'legitimateFunction.__proto__' ,
297+ ] ;
298+ for ( const name of maliciousNames ) {
299+ const response = await request ( {
300+ headers,
301+ method : 'POST' ,
302+ url : `http://localhost:8378/1/functions/${ encodeURIComponent ( name ) } ` ,
303+ body : JSON . stringify ( { } ) ,
304+ } ) . catch ( e => e ) ;
305+ expect ( response . status ) . toBe ( 400 ) ;
306+ }
307+ // Verify server is still responsive after all attempts
308+ const healthResponse = await request ( {
309+ headers,
310+ method : 'POST' ,
311+ url : 'http://localhost:8378/1/functions/legitimateFunction' ,
312+ body : JSON . stringify ( { } ) ,
313+ } ) ;
314+ expect ( healthResponse . status ) . toBe ( 200 ) ;
315+ expect ( JSON . parse ( healthResponse . text ) . result ) . toBe ( 'ok' ) ;
316+ } ) ;
317+ } ) ;
318+
254319 describe ( '(GHSA-3v4q-4q9g-x83q) Prototype pollution via application ID in trigger store' , ( ) => {
255320 const prototypeProperties = [ 'constructor' , 'toString' , 'valueOf' , 'hasOwnProperty' , '__proto__' ] ;
256321
0 commit comments