Skip to content

Commit 200ba21

Browse files
committed
Add failing tests
1 parent 916e44a commit 200ba21

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

library/sources/Express.tests.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff 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+
/Error: Zen has blocked a path traversal attack: fs.readFile\(\.\.\.\) originating from query/
768+
);
769+
});
744770
}

library/sources/HTTPServer.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff 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+
});

0 commit comments

Comments
 (0)