File tree Expand file tree Collapse file tree 2 files changed +68
-0
lines changed
Expand file tree Collapse file tree 2 files changed +68
-0
lines changed Original file line number Diff line number Diff line change @@ -74,6 +74,7 @@ export function createExpressTests(expressPackageName: string) {
7474 } ) ;
7575
7676 const express = require ( expressPackageName ) as typeof import ( "express" ) ;
77+ const { readFile } = require ( "fs" ) as typeof import ( "fs" ) ;
7778
7879 function getApp ( userMiddleware = true ) {
7980 const app = express ( ) ;
@@ -741,4 +742,29 @@ export function createExpressTests(expressPackageName: string) {
741742 t . same ( blockedResponse . statusCode , 403 ) ;
742743 t . same ( blockedResponse . text , "You are blocked by Zen." ) ;
743744 } ) ;
745+
746+ t . test ( "it detects path traversal with double encoding" , async ( t ) => {
747+ const app = express ( ) ;
748+
749+ app . get ( "/search" , ( req , res ) => {
750+ const searchTerm = req . query . q ;
751+ const fileUrl = new URL ( `file:///public/${ searchTerm } ` ) ;
752+
753+ readFile ( fileUrl , "utf-8" , ( err , data ) => {
754+ if ( err ) {
755+ return res . status ( 500 ) . send ( "Error reading file" ) ;
756+ }
757+ res . send ( `File content of /public/${ searchTerm } : ${ data } ` ) ;
758+ } ) ;
759+ } ) ;
760+
761+ const blockedResponse = await request ( app ) . get (
762+ "/search?q=.%252E/etc/passwd"
763+ ) ;
764+ t . same ( blockedResponse . statusCode , 500 ) ;
765+ t . match (
766+ blockedResponse . text ,
767+ / E r r o r : Z e n h a s b l o c k e d a p a t h t r a v e r s a l a t t a c k : f s .r e a d F i l e \( \. \. \. \) o r i g i n a t i n g f r o m q u e r y /
768+ ) ;
769+ } ) ;
744770}
Original file line number Diff line number Diff line change @@ -788,3 +788,45 @@ t.test("it blocks path traversal in path", async (t) => {
788788 } ) ;
789789 } ) ;
790790} ) ;
791+
792+ t . test ( "it blocks double encoded path traversal" , async ( t ) => {
793+ const server = http . createServer ( ( req , res ) => {
794+ try {
795+ const url = new URL ( req . url ! , `http://${ req . headers . host } ` ) ;
796+ const filePath = url . searchParams . get ( "path" ) ;
797+ const fileUrl = new URL ( `file:///public/${ filePath } ` ) ;
798+ const file = readFileSync ( fileUrl ) ;
799+
800+ res . statusCode = 200 ;
801+ res . end ( file ) ;
802+ } catch ( error ) {
803+ res . statusCode = 500 ;
804+ if ( error instanceof Error ) {
805+ res . end ( error . message ) ;
806+ return ;
807+ }
808+ res . end ( "Internal server error" ) ;
809+ }
810+ } ) ;
811+
812+ await new Promise < void > ( ( resolve ) => {
813+ server . listen ( 3327 , async ( ) => {
814+ fetch ( {
815+ url : new URL ( "http://localhost:3327/?path=.%252E/etc/passwd" ) ,
816+ method : "GET" ,
817+ headers : {
818+ "x-forwarded-for" : "1.2.3.4" ,
819+ } ,
820+ timeoutInMS : 500 ,
821+ } ) . then ( ( { statusCode, body } ) => {
822+ t . equal ( statusCode , 500 ) ;
823+ t . equal (
824+ body ,
825+ "Zen has blocked a path traversal attack: fs.readFileSync(...) originating from query"
826+ ) ;
827+ server . close ( ) ;
828+ resolve ( ) ;
829+ } ) ;
830+ } ) ;
831+ } ) ;
832+ } ) ;
You can’t perform that action at this time.
0 commit comments