Skip to content

Commit 119dcad

Browse files
committed
Fixed path traversal issue GHSL-2020-198
1 parent 1d22ff6 commit 119dcad

File tree

2 files changed

+50
-27
lines changed

2 files changed

+50
-27
lines changed

adm-zip.js

+10-8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ var ZipEntry = require("./zipEntry"),
99

1010
var isWin = /^win/.test(process.platform);
1111

12+
function canonical(p) {
13+
var safeSuffix = pth.normalize(p).replace(/^(\.\.(\/|\\|$))+/, '');
14+
return pth.join('./', safeSuffix);
15+
}
1216

1317
module.exports = function (/**String*/input) {
1418
var _zip = undefined,
@@ -469,12 +473,9 @@ module.exports = function (/**String*/input) {
469473
throw new Error(Utils.Errors.NO_ENTRY);
470474
}
471475

472-
var entryName = item.entryName;
476+
var entryName = canonical(item.entryName);
473477

474-
var target = sanitize(targetPath,
475-
outFileName && !item.isDirectory ? outFileName :
476-
(maintainEntryPath ? entryName : pth.basename(entryName))
477-
);
478+
var target = sanitize(targetPath,outFileName && !item.isDirectory ? outFileName : (maintainEntryPath ? entryName : pth.basename(entryName)));
478479

479480
if (item.isDirectory) {
480481
target = pth.resolve(target, "..");
@@ -485,7 +486,8 @@ module.exports = function (/**String*/input) {
485486
if (!content) {
486487
throw new Error(Utils.Errors.CANT_EXTRACT_FILE);
487488
}
488-
var childName = sanitize(targetPath, maintainEntryPath ? child.entryName : pth.basename(child.entryName));
489+
var name = canonical(child.entryName)
490+
var childName = sanitize(targetPath, maintainEntryPath ? name : pth.basename(name));
489491

490492
Utils.writeFileTo(childName, content, overwrite);
491493
});
@@ -541,7 +543,7 @@ module.exports = function (/**String*/input) {
541543
throw new Error(Utils.Errors.NO_ZIP);
542544
}
543545
_zip.entries.forEach(function (entry) {
544-
var entryName = sanitize(targetPath, entry.entryName.toString());
546+
var entryName = sanitize(targetPath, canonical(entry.entryName.toString()));
545547
if (entry.isDirectory) {
546548
Utils.makeDir(entryName);
547549
return;
@@ -582,7 +584,7 @@ module.exports = function (/**String*/input) {
582584
entries.forEach(function (entry) {
583585
if (i <= 0) return; // Had an error already
584586

585-
var entryName = pth.normalize(entry.entryName.toString());
587+
var entryName = pth.normalize(canonical(entry.entryName.toString()));
586588

587589
if (entry.isDirectory) {
588590
Utils.makeDir(sanitize(targetPath, entryName));

test/mocha.js

+40-19
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,36 @@ describe('adm-zip', () => {
2323
const files = walk(destination)
2424

2525
expect(files.sort()).to.deep.equal([
26-
"./test/xxx/attributes_test/asd/New Text Document.txt",
27-
"./test/xxx/attributes_test/blank file.txt",
28-
"./test/xxx/attributes_test/New folder/hidden.txt",
29-
"./test/xxx/attributes_test/New folder/hidden_readonly.txt",
30-
"./test/xxx/attributes_test/New folder/readonly.txt",
31-
"./test/xxx/utes_test/New folder/somefile.txt"
26+
pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"),
27+
pth.normalize("./test/xxx/attributes_test/blank file.txt"),
28+
pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"),
29+
pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"),
30+
pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"),
31+
pth.normalize("./test/xxx/utes_test/New folder/somefile.txt")
3232
].sort());
3333
})
3434

35+
it('zip pathTraversal', () => {
36+
const target = pth.join(destination, "test")
37+
const zip = new Zip();
38+
zip.addFile("../../../test1.ext", "content")
39+
zip.addFile("folder/../../test2.ext", "content")
40+
zip.addFile("test3.ext", "content")
41+
const buf = zip.toBuffer()
42+
43+
const extract = new Zip(buf)
44+
var zipEntries = zip.getEntries();
45+
zipEntries.forEach(e => zip.extractEntryTo(e, destination, false, true));
46+
47+
extract.extractAllTo(target)
48+
const files = walk(target)
49+
expect(files.sort()).to.deep.equal([
50+
pth.normalize('./test/xxx/test/test1.ext'),
51+
pth.normalize('./test/xxx/test/test2.ext'),
52+
pth.normalize('./test/xxx/test/test3.ext'),
53+
])
54+
})
55+
3556
it('zip.extractEntryTo(entry, destination, false, true)', () => {
3657
const destination = './test/xxx'
3758
const zip = new Zip('./test/assets/ultra.zip');
@@ -40,12 +61,12 @@ describe('adm-zip', () => {
4061

4162
const files = walk(destination)
4263
expect(files.sort()).to.deep.equal([
43-
"./test/xxx/blank file.txt",
44-
"./test/xxx/hidden.txt",
45-
"./test/xxx/hidden_readonly.txt",
46-
"./test/xxx/New Text Document.txt",
47-
"./test/xxx/readonly.txt",
48-
"./test/xxx/somefile.txt"
64+
pth.normalize("./test/xxx/blank file.txt"),
65+
pth.normalize("./test/xxx/hidden.txt"),
66+
pth.normalize("./test/xxx/hidden_readonly.txt"),
67+
pth.normalize("./test/xxx/New Text Document.txt"),
68+
pth.normalize("./test/xxx/readonly.txt"),
69+
pth.normalize("./test/xxx/somefile.txt")
4970
].sort());
5071
})
5172

@@ -57,12 +78,12 @@ describe('adm-zip', () => {
5778

5879
const files = walk(destination)
5980
expect(files.sort()).to.deep.equal([
60-
"./test/xxx/attributes_test/asd/New Text Document.txt",
61-
"./test/xxx/attributes_test/blank file.txt",
62-
"./test/xxx/attributes_test/New folder/hidden.txt",
63-
"./test/xxx/attributes_test/New folder/hidden_readonly.txt",
64-
"./test/xxx/attributes_test/New folder/readonly.txt",
65-
"./test/xxx/utes_test/New folder/somefile.txt"
81+
pth.normalize("./test/xxx/attributes_test/asd/New Text Document.txt"),
82+
pth.normalize("./test/xxx/attributes_test/blank file.txt"),
83+
pth.normalize("./test/xxx/attributes_test/New folder/hidden.txt"),
84+
pth.normalize("./test/xxx/attributes_test/New folder/hidden_readonly.txt"),
85+
pth.normalize("./test/xxx/attributes_test/New folder/readonly.txt"),
86+
pth.normalize("./test/xxx/utes_test/New folder/somefile.txt")
6687
].sort());
6788
})
6889

@@ -93,7 +114,7 @@ function walk(dir) {
93114
results = results.concat(walk(file));
94115
} else {
95116
/* Is a file */
96-
results.push(file);
117+
results.push(pth.normalize(file));
97118
}
98119
});
99120
return results;

0 commit comments

Comments
 (0)