Skip to content

Commit 7aa23d8

Browse files
handle case of path traversal attack
1 parent 63b11f4 commit 7aa23d8

2 files changed

Lines changed: 37 additions & 4 deletions

File tree

src/handlers/file_handler.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,14 @@ export class FileHandler {
3939

4040
}
4141

42-
private getFileInfoFromUrl_(urlPath: string) {
43-
const splittedValue = urlPath.split("/");
42+
private getFileInfoFromUrl_(url: string) {
43+
// normalize early
44+
const normalizedPath = path.posix.normalize(url); // use posix to handle forward
45+
const splittedValue = normalizedPath.split("/");
4446
const fileInfo = {
4547
file: ""
4648
} as IFileInfo;
47-
if (splittedValue.length > 2 || !isNullOrEmpty(path.parse(urlPath).ext)) {
49+
if (splittedValue.length > 2 || !isNullOrEmpty(path.parse(normalizedPath).ext)) {
4850
fileInfo.folder = splittedValue[1];
4951
fileInfo.file = splittedValue.splice(2).join("/");
5052
return fileInfo;
@@ -71,7 +73,14 @@ export class FileHandler {
7173
const getAbsPath = () => {
7274
const folder = this.option.global['folders_'].find(qry => qry.alias === fileInfo.folder);
7375
if (folder != null) {
74-
return path.join(folder.path, fileInfo.file);
76+
const absPath = path.join(folder.path, fileInfo.file);
77+
const normalized = path.normalize(absPath);
78+
79+
// Security check: ensure path is inside allowed folder
80+
if (!normalized.startsWith(folder.path)) {
81+
return null; // prevent path traversal
82+
}
83+
return normalized;
7584
}
7685
return null;
7786
};

tests/filetest/test/static.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,30 @@ describe("/static", () => {
5353
});
5454
})
5555

56+
it("should prevent path traversal attack", (done) => {
57+
request
58+
.get("/static/../../package.json")
59+
.accept(browserAccept)
60+
.end((err, res) => {
61+
expect(err).to.be.null;
62+
expect(res).to.have.status(404); // should not expose package.json
63+
expect(res.text).to.not.include("name"); // package.json contents
64+
done();
65+
});
66+
});
67+
68+
it("should prevent path traversal attack", (done) => {
69+
request
70+
.get("/static/../package.json")
71+
.accept(browserAccept)
72+
.end((err, res) => {
73+
expect(err).to.be.null;
74+
expect(res).to.have.status(404); // should not expose package.json
75+
expect(res.text).to.not.include("name"); // package.json contents
76+
done();
77+
});
78+
});
79+
5680
let etagVal;
5781

5882
it('big file test', async () => {

0 commit comments

Comments
 (0)