Skip to content

Commit ebccd7f

Browse files
authored
fix: Session field immutability bypass via falsy-value guard ([GHSA-f6j3-w9v3-cq22](GHSA-f6j3-w9v3-cq22)) (#10348)
1 parent fbd138c commit ebccd7f

File tree

2 files changed

+156
-4
lines changed

2 files changed

+156
-4
lines changed

spec/ParseSession.spec.js

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,156 @@ describe('Parse.Session', () => {
393393
});
394394
expect(verifyRes.data.expiresAt.iso).toBe(farFuture);
395395
});
396+
397+
it('should reject null expiresAt when updating a session via PUT', async () => {
398+
const user = await Parse.User.signUp('sessionupdatenull1', 'password');
399+
const sessionToken = user.getSessionToken();
400+
401+
const sessionRes = await request({
402+
method: 'GET',
403+
url: 'http://localhost:8378/1/sessions/me',
404+
headers: {
405+
'X-Parse-Application-Id': 'test',
406+
'X-Parse-REST-API-Key': 'rest',
407+
'X-Parse-Session-Token': sessionToken,
408+
},
409+
});
410+
const sessionId = sessionRes.data.objectId;
411+
const originalExpiresAt = sessionRes.data.expiresAt;
412+
413+
const updateRes = await request({
414+
method: 'PUT',
415+
url: `http://localhost:8378/1/sessions/${sessionId}`,
416+
headers: {
417+
'X-Parse-Application-Id': 'test',
418+
'X-Parse-REST-API-Key': 'rest',
419+
'X-Parse-Session-Token': sessionToken,
420+
'Content-Type': 'application/json',
421+
},
422+
body: {
423+
expiresAt: null,
424+
},
425+
}).catch(e => e);
426+
427+
expect(updateRes.data.code).toBe(Parse.Error.INVALID_KEY_NAME);
428+
429+
const verifyRes = await request({
430+
method: 'GET',
431+
url: 'http://localhost:8378/1/sessions/me',
432+
headers: {
433+
'X-Parse-Application-Id': 'test',
434+
'X-Parse-REST-API-Key': 'rest',
435+
'X-Parse-Session-Token': sessionToken,
436+
},
437+
});
438+
expect(verifyRes.data.expiresAt).toEqual(originalExpiresAt);
439+
});
440+
441+
it('should reject null createdWith when updating a session via PUT', async () => {
442+
const user = await Parse.User.signUp('sessionupdatenull2', 'password');
443+
const sessionToken = user.getSessionToken();
444+
445+
const sessionRes = await request({
446+
method: 'GET',
447+
url: 'http://localhost:8378/1/sessions/me',
448+
headers: {
449+
'X-Parse-Application-Id': 'test',
450+
'X-Parse-REST-API-Key': 'rest',
451+
'X-Parse-Session-Token': sessionToken,
452+
},
453+
});
454+
const sessionId = sessionRes.data.objectId;
455+
const originalCreatedWith = sessionRes.data.createdWith;
456+
457+
const updateRes = await request({
458+
method: 'PUT',
459+
url: `http://localhost:8378/1/sessions/${sessionId}`,
460+
headers: {
461+
'X-Parse-Application-Id': 'test',
462+
'X-Parse-REST-API-Key': 'rest',
463+
'X-Parse-Session-Token': sessionToken,
464+
'Content-Type': 'application/json',
465+
},
466+
body: {
467+
createdWith: null,
468+
},
469+
}).catch(e => e);
470+
471+
expect(updateRes.data.code).toBe(Parse.Error.INVALID_KEY_NAME);
472+
473+
const verifyRes = await request({
474+
method: 'GET',
475+
url: 'http://localhost:8378/1/sessions/me',
476+
headers: {
477+
'X-Parse-Application-Id': 'test',
478+
'X-Parse-REST-API-Key': 'rest',
479+
'X-Parse-Session-Token': sessionToken,
480+
},
481+
});
482+
expect(verifyRes.data.createdWith).toEqual(originalCreatedWith);
483+
});
484+
485+
it('should reject null installationId when updating a session via PUT', async () => {
486+
const user = await Parse.User.signUp('sessionupdatenull3', 'password');
487+
const sessionToken = user.getSessionToken();
488+
489+
const sessionRes = await request({
490+
method: 'GET',
491+
url: 'http://localhost:8378/1/sessions/me',
492+
headers: {
493+
'X-Parse-Application-Id': 'test',
494+
'X-Parse-REST-API-Key': 'rest',
495+
'X-Parse-Session-Token': sessionToken,
496+
},
497+
});
498+
const sessionId = sessionRes.data.objectId;
499+
500+
const updateRes = await request({
501+
method: 'PUT',
502+
url: `http://localhost:8378/1/sessions/${sessionId}`,
503+
headers: {
504+
'X-Parse-Application-Id': 'test',
505+
'X-Parse-REST-API-Key': 'rest',
506+
'X-Parse-Session-Token': sessionToken,
507+
'Content-Type': 'application/json',
508+
},
509+
body: {
510+
installationId: null,
511+
},
512+
}).catch(e => e);
513+
514+
expect(updateRes.data.code).toBe(Parse.Error.INVALID_KEY_NAME);
515+
});
516+
517+
it('should reject null sessionToken when updating a session via PUT', async () => {
518+
const user = await Parse.User.signUp('sessionupdatenull4', 'password');
519+
const sessionToken = user.getSessionToken();
520+
521+
const sessionRes = await request({
522+
method: 'GET',
523+
url: 'http://localhost:8378/1/sessions/me',
524+
headers: {
525+
'X-Parse-Application-Id': 'test',
526+
'X-Parse-REST-API-Key': 'rest',
527+
'X-Parse-Session-Token': sessionToken,
528+
},
529+
});
530+
const sessionId = sessionRes.data.objectId;
531+
532+
const updateRes = await request({
533+
method: 'PUT',
534+
url: `http://localhost:8378/1/sessions/${sessionId}`,
535+
headers: {
536+
'X-Parse-Application-Id': 'test',
537+
'X-Parse-REST-API-Key': 'rest',
538+
'X-Parse-Session-Token': sessionToken,
539+
'Content-Type': 'application/json',
540+
},
541+
body: {
542+
sessionToken: null,
543+
},
544+
}).catch(e => e);
545+
546+
expect(updateRes.data.code).toBe(Parse.Error.INVALID_KEY_NAME);
547+
});
396548
});

src/RestWrite.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,13 +1174,13 @@ RestWrite.prototype.handleSession = function () {
11741174
if (this.query) {
11751175
if (this.data.user && !this.auth.isMaster && this.data.user.objectId != this.auth.user.id) {
11761176
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME);
1177-
} else if (this.data.installationId) {
1177+
} else if ('installationId' in this.data) {
11781178
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME);
1179-
} else if (this.data.sessionToken) {
1179+
} else if ('sessionToken' in this.data) {
11801180
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME);
1181-
} else if (this.data.expiresAt && !this.auth.isMaster && !this.auth.isMaintenance) {
1181+
} else if ('expiresAt' in this.data && !this.auth.isMaster && !this.auth.isMaintenance) {
11821182
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME);
1183-
} else if (this.data.createdWith && !this.auth.isMaster && !this.auth.isMaintenance) {
1183+
} else if ('createdWith' in this.data && !this.auth.isMaster && !this.auth.isMaintenance) {
11841184
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME);
11851185
}
11861186
if (!this.auth.isMaster) {

0 commit comments

Comments
 (0)