@@ -169,4 +169,91 @@ describe('Parse.Session', () => {
169169 expect ( first . get ( 'user' ) . id ) . toBe ( firstUser ) ;
170170 expect ( second . get ( 'user' ) . id ) . toBe ( secondUser ) ;
171171 } ) ;
172+
173+ it ( 'should ignore sessionToken when creating a session via POST /classes/_Session' , async ( ) => {
174+ const user = await Parse . User . signUp ( 'sessionuser' , 'password' ) ;
175+ const sessionToken = user . getSessionToken ( ) ;
176+
177+ const response = await request ( {
178+ method : 'POST' ,
179+ url : 'http://localhost:8378/1/classes/_Session' ,
180+ headers : {
181+ 'X-Parse-Application-Id' : 'test' ,
182+ 'X-Parse-REST-API-Key' : 'rest' ,
183+ 'X-Parse-Session-Token' : sessionToken ,
184+ 'Content-Type' : 'application/json' ,
185+ } ,
186+ body : {
187+ sessionToken : 'r:ATTACKER_CONTROLLED_TOKEN' ,
188+ } ,
189+ } ) ;
190+
191+ // The returned session should have a server-generated token, not the attacker's
192+ expect ( response . data . sessionToken ) . not . toBe ( 'r:ATTACKER_CONTROLLED_TOKEN' ) ;
193+ expect ( response . data . sessionToken ) . toMatch ( / ^ r : / ) ;
194+ } ) ;
195+
196+ it ( 'should ignore expiresAt when creating a session via POST /classes/_Session' , async ( ) => {
197+ const user = await Parse . User . signUp ( 'sessionuser2' , 'password' ) ;
198+ const sessionToken = user . getSessionToken ( ) ;
199+ const farFuture = new Date ( '2099-12-31T23:59:59.000Z' ) ;
200+
201+ await request ( {
202+ method : 'POST' ,
203+ url : 'http://localhost:8378/1/classes/_Session' ,
204+ headers : {
205+ 'X-Parse-Application-Id' : 'test' ,
206+ 'X-Parse-REST-API-Key' : 'rest' ,
207+ 'X-Parse-Session-Token' : sessionToken ,
208+ 'Content-Type' : 'application/json' ,
209+ } ,
210+ body : {
211+ expiresAt : { __type : 'Date' , iso : farFuture . toISOString ( ) } ,
212+ } ,
213+ } ) ;
214+
215+ // Fetch the newly created session and verify expiresAt is server-generated, not 2099
216+ const sessions = await request ( {
217+ method : 'GET' ,
218+ url : 'http://localhost:8378/1/classes/_Session' ,
219+ headers : {
220+ 'X-Parse-Application-Id' : 'test' ,
221+ 'X-Parse-Master-Key' : 'test' ,
222+ } ,
223+ } ) ;
224+ const newSession = sessions . data . results . find ( s => s . sessionToken !== sessionToken ) ;
225+ const expiresAt = new Date ( newSession . expiresAt . iso ) ;
226+ expect ( expiresAt . getFullYear ( ) ) . not . toBe ( 2099 ) ;
227+ } ) ;
228+
229+ it ( 'should ignore createdWith when creating a session via POST /classes/_Session' , async ( ) => {
230+ const user = await Parse . User . signUp ( 'sessionuser3' , 'password' ) ;
231+ const sessionToken = user . getSessionToken ( ) ;
232+
233+ await request ( {
234+ method : 'POST' ,
235+ url : 'http://localhost:8378/1/classes/_Session' ,
236+ headers : {
237+ 'X-Parse-Application-Id' : 'test' ,
238+ 'X-Parse-REST-API-Key' : 'rest' ,
239+ 'X-Parse-Session-Token' : sessionToken ,
240+ 'Content-Type' : 'application/json' ,
241+ } ,
242+ body : {
243+ createdWith : { action : 'attacker' , authProvider : 'evil' } ,
244+ } ,
245+ } ) ;
246+
247+ const sessions = await request ( {
248+ method : 'GET' ,
249+ url : 'http://localhost:8378/1/classes/_Session' ,
250+ headers : {
251+ 'X-Parse-Application-Id' : 'test' ,
252+ 'X-Parse-Master-Key' : 'test' ,
253+ } ,
254+ } ) ;
255+ const newSession = sessions . data . results . find ( s => s . sessionToken !== sessionToken ) ;
256+ expect ( newSession . createdWith . action ) . toBe ( 'create' ) ;
257+ expect ( newSession . createdWith . authProvider ) . toBeUndefined ( ) ;
258+ } ) ;
172259} ) ;
0 commit comments