@@ -1178,6 +1178,79 @@ describe('Pages Router', () => {
11781178 expect ( nonExistingResponse . status ) . toBe ( 303 ) ;
11791179 expect ( nonExistingResponse . headers . location ) . toContain ( 'email_verification_send_fail' ) ;
11801180 } ) ;
1181+
1182+ it ( 'does not create file existence oracle via path traversal in locale query parameter' , async ( ) => {
1183+ // Create a canary file at a traversable path to test the oracle
1184+ const canaryDir = path . join ( __dirname , 'tmp-locale-oracle-test' ) ;
1185+ try {
1186+ await fs . mkdir ( canaryDir , { recursive : true } ) ;
1187+ await fs . writeFile ( path . join ( canaryDir , 'password_reset.html' ) , 'canary' ) ;
1188+
1189+ config . pages . enableLocalization = true ;
1190+ await reconfigureServer ( config ) ;
1191+
1192+ // Calculate traversal from pages directory to canary directory
1193+ const pagesPath = path . resolve ( __dirname , '../public' ) ;
1194+ const relativePath = path . relative ( pagesPath , canaryDir ) ;
1195+
1196+ // Request with path traversal locale pointing to existing canary file
1197+ const existsResponse = await request ( {
1198+ url : `${ config . publicServerURL } /apps/${ config . appId } /request_password_reset?token=test&username=test&locale=${ encodeURIComponent ( relativePath ) } ` ,
1199+ followRedirects : false ,
1200+ } ) . catch ( e => e ) ;
1201+
1202+ // Request with path traversal locale pointing to non-existing directory
1203+ const notExistsResponse = await request ( {
1204+ url : `${ config . publicServerURL } /apps/${ config . appId } /request_password_reset?token=test&username=test&locale=${ encodeURIComponent ( '../../../../../../tmp/nonexistent-dir' ) } ` ,
1205+ followRedirects : false ,
1206+ } ) . catch ( e => e ) ;
1207+
1208+ // Both responses must have the same status — no differential oracle
1209+ expect ( existsResponse . status ) . toBe ( notExistsResponse . status ) ;
1210+ // Canary content must never be served
1211+ expect ( existsResponse . text ) . not . toContain ( 'canary' ) ;
1212+ expect ( notExistsResponse . text ) . not . toContain ( 'canary' ) ;
1213+ } finally {
1214+ await fs . rm ( canaryDir , { recursive : true , force : true } ) ;
1215+ }
1216+ } ) ;
1217+
1218+ it ( 'does not create file existence oracle via path traversal in locale header' , async ( ) => {
1219+ // Create a canary file at a traversable path
1220+ const canaryDir = path . join ( __dirname , 'tmp-locale-header-test' ) ;
1221+ try {
1222+ await fs . mkdir ( canaryDir , { recursive : true } ) ;
1223+ await fs . writeFile ( path . join ( canaryDir , 'password_reset.html' ) , 'canary' ) ;
1224+
1225+ config . pages . enableLocalization = true ;
1226+ await reconfigureServer ( config ) ;
1227+
1228+ const pagesPath = path . resolve ( __dirname , '../public' ) ;
1229+ const relativePath = path . relative ( pagesPath , canaryDir ) ;
1230+
1231+ // Request with path traversal locale via header pointing to existing file
1232+ const existsResponse = await request ( {
1233+ url : `${ config . publicServerURL } /apps/${ config . appId } /request_password_reset?token=test&username=test` ,
1234+ headers : { 'x-parse-page-param-locale' : relativePath } ,
1235+ followRedirects : false ,
1236+ } ) . catch ( e => e ) ;
1237+
1238+ // Request with path traversal locale via header pointing to non-existing directory
1239+ const notExistsResponse = await request ( {
1240+ url : `${ config . publicServerURL } /apps/${ config . appId } /request_password_reset?token=test&username=test` ,
1241+ headers : { 'x-parse-page-param-locale' : '../../../../../../tmp/nonexistent-dir' } ,
1242+ followRedirects : false ,
1243+ } ) . catch ( e => e ) ;
1244+
1245+ // Both responses must have the same status — no differential oracle
1246+ expect ( existsResponse . status ) . toBe ( notExistsResponse . status ) ;
1247+ // Canary content must never be served
1248+ expect ( existsResponse . text ) . not . toContain ( 'canary' ) ;
1249+ expect ( notExistsResponse . text ) . not . toContain ( 'canary' ) ;
1250+ } finally {
1251+ await fs . rm ( canaryDir , { recursive : true , force : true } ) ;
1252+ }
1253+ } ) ;
11811254 } ) ;
11821255
11831256 describe ( 'custom route' , ( ) => {
0 commit comments