Skip to content

Commit 775165d

Browse files
authored
Merge pull request #381 from 5saviahv/sanitize
Move sanitize functions to Utils
2 parents 62559b3 + e2813a4 commit 775165d

File tree

5 files changed

+234
-164
lines changed

5 files changed

+234
-164
lines changed

adm-zip.js

+39-51
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@ const defaultOptions = {
1515
method: Utils.Constants.NONE
1616
};
1717

18-
function canonical(p) {
19-
// trick normalize think path is absolute
20-
var safeSuffix = pth.posix.normalize("/" + p.split("\\").join("/"));
21-
return pth.join(".", safeSuffix);
22-
}
23-
2418
module.exports = function (/**String*/ input, /** object */ options) {
2519
let inBuffer = null;
2620

@@ -37,7 +31,7 @@ module.exports = function (/**String*/ input, /** object */ options) {
3731
}
3832

3933
// if input is buffer
40-
if (input instanceof Uint8Array) {
34+
if (Buffer.isBuffer(input)) {
4135
inBuffer = input;
4236
opts.method = Utils.Constants.BUFFER;
4337
input = undefined;
@@ -62,17 +56,7 @@ module.exports = function (/**String*/ input, /** object */ options) {
6256
// create variable
6357
const _zip = new ZipFile(inBuffer, opts);
6458

65-
function sanitize(prefix, name) {
66-
prefix = pth.resolve(pth.normalize(prefix));
67-
var parts = name.split("/");
68-
for (var i = 0, l = parts.length; i < l; i++) {
69-
var path = pth.normalize(pth.join(prefix, parts.slice(i, l).join(pth.sep)));
70-
if (path.indexOf(prefix) === 0) {
71-
return path;
72-
}
73-
}
74-
return pth.normalize(pth.join(prefix, pth.basename(name)));
75-
}
59+
const { canonical, sanitize } = Utils;
7660

7761
function getEntry(/**Object*/ entry) {
7862
if (entry && _zip) {
@@ -636,48 +620,52 @@ module.exports = function (/**String*/ input, /** object */ options) {
636620
return;
637621
}
638622

639-
var entries = _zip.entries;
640-
var i = entries.length;
641-
entries.forEach(function (entry) {
642-
if (i <= 0) return; // Had an error already
623+
targetPath = pth.resolve(targetPath);
624+
// convert entryname to
625+
const getPath = (entry) => sanitize(targetPath, pth.normalize(canonical(entry.entryName.toString())));
626+
const getError = (msg, file) => new Error(msg + ': "' + file + '"');
643627

644-
var entryName = pth.normalize(canonical(entry.entryName.toString()));
628+
const entries = _zip.entries;
645629

646-
if (entry.isDirectory) {
647-
Utils.makeDir(sanitize(targetPath, entryName));
648-
if (--i === 0) callback(undefined);
649-
return;
630+
// Create directory entries first
631+
for (const entry of entries.filter((e) => e.isDirectory)) {
632+
const filePath = getPath(entry);
633+
// The reverse operation for attr depend on method addFile()
634+
const fileAttr = entry.header.fileAttr;
635+
try {
636+
Utils.makeDir(filePath);
637+
if (fileAttr) fs.chmodSync(filePath, fileAttr);
638+
// in unix timestamp will change if files are later added to folder, but still
639+
fs.utimesSync(filePath, entry.header.time, entry.header.time);
640+
} catch (er) {
641+
callback(getError("Unable to create folder", filePath));
650642
}
651-
entry.getDataAsync(function (content, err) {
652-
if (i <= 0) return;
653-
if (err) {
654-
callback(new Error(err));
643+
}
644+
645+
// File entries
646+
for (const entry of entries.filter((e) => !e.isDirectory)) {
647+
const entryName = pth.normalize(canonical(entry.entryName.toString()));
648+
const filePath = sanitize(targetPath, entryName);
649+
entry.getDataAsync(function (content, err_1) {
650+
if (err_1) {
651+
callback(new Error(err_1));
655652
return;
656653
}
657654
if (!content) {
658-
i = 0;
659655
callback(new Error(Utils.Errors.CANT_EXTRACT_FILE));
660-
return;
656+
} else {
657+
// The reverse operation for attr depend on method addFile()
658+
Utils.writeFileToAsync(filePath, content, overwrite, entry.header.fileAttr, function (succ) {
659+
if (!succ) callback(getError("Unable to write file", filePath));
660+
fs.utimes(filePath, entry.header.time, entry.header.time, function (err_2) {
661+
if (err_2) callback(getError("Unable to set times", filePath));
662+
});
663+
});
661664
}
662-
663-
// The reverse operation for attr depend on method addFile()
664-
var fileAttr = entry.attr ? (((entry.attr >>> 0) | 0) >> 16) & 0xfff : 0;
665-
Utils.writeFileToAsync(sanitize(targetPath, entryName), content, overwrite, fileAttr, function (succ) {
666-
try {
667-
fs.utimesSync(pth.resolve(targetPath, entryName), entry.header.time, entry.header.time);
668-
} catch (er) {
669-
callback(new Error("Unable to set utimes"));
670-
}
671-
if (i <= 0) return;
672-
if (!succ) {
673-
i = 0;
674-
callback(new Error("Unable to write"));
675-
return;
676-
}
677-
if (--i === 0) callback(undefined);
678-
});
679665
});
680-
});
666+
}
667+
668+
callback();
681669
},
682670

683671
/**

headers/entryHeader.js

+12-7
Original file line numberDiff line numberDiff line change
@@ -84,21 +84,21 @@ module.exports = function () {
8484
return _crc;
8585
},
8686
set crc(val) {
87-
_crc = val;
87+
_crc = Math.max(0, val) >>> 0;
8888
},
8989

9090
get compressedSize() {
9191
return _compressedSize;
9292
},
9393
set compressedSize(val) {
94-
_compressedSize = val;
94+
_compressedSize = Math.max(0, val) >>> 0;
9595
},
9696

9797
get size() {
9898
return _size;
9999
},
100100
set size(val) {
101-
_size = val;
101+
_size = Math.max(0, val) >>> 0;
102102
},
103103

104104
get fileNameLength() {
@@ -126,28 +126,33 @@ module.exports = function () {
126126
return _diskStart;
127127
},
128128
set diskNumStart(val) {
129-
_diskStart = val;
129+
_diskStart = Math.max(0, val) >>> 0;
130130
},
131131

132132
get inAttr() {
133133
return _inattr;
134134
},
135135
set inAttr(val) {
136-
_inattr = val;
136+
_inattr = Math.max(0, val) >>> 0;
137137
},
138138

139139
get attr() {
140140
return _attr;
141141
},
142142
set attr(val) {
143-
_attr = val;
143+
_attr = Math.max(0, val) >>> 0;
144+
},
145+
146+
// get Unix file permissions
147+
get fileAttr() {
148+
return (((_attr >>> 0) | 0) >> 16) & 0xfff;
144149
},
145150

146151
get offset() {
147152
return _offset;
148153
},
149154
set offset(val) {
150-
_offset = val;
155+
_offset = Math.max(0, val) >>> 0;
151156
},
152157

153158
get encripted() {

test/utils.test.js

+58-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
"use strict";
22
const { expect } = require("chai");
3-
const { crc32 } = require("../util/utils");
3+
const { crc32, canonical, sanitize } = require("../util/utils");
4+
const pth = require("path");
45

5-
describe("crc32 function", () => {
6+
describe("utils - crc32 function", () => {
67
// tests how crc32 function handles strings as input
78
it("handle strings", () => {
89
const tests = [
@@ -34,3 +35,58 @@ describe("crc32 function", () => {
3435
}
3536
});
3637
});
38+
39+
describe("utils - sanitizing functions :", () => {
40+
// tests how sanitize works
41+
it("function sanitize()", () => {
42+
const tests = [
43+
// basic latin
44+
{ prefix: "", file: "", result: "" },
45+
{ prefix: "folder", file: "file", result: "folder/file" },
46+
{ prefix: "folder", file: "../file", result: "folder/file" },
47+
{ prefix: "folder", file: "../../../file", result: "folder/file" },
48+
{ prefix: "folder", file: "./../file", result: "folder/file" },
49+
{ prefix: "test/folder/subfolder", file: "../../file", result: "test/folder/subfolder/file" },
50+
{ prefix: "test/folder/subfolder", file: "../../file1/../file2", result: "test/folder/subfolder/file2" },
51+
// no prefixed (currently allows change folder)
52+
{ prefix: "", file: "../../file1/../file2", result: "file2" },
53+
{ prefix: "", file: "../subfolder/file2", result: "subfolder/file2" },
54+
{ prefix: "", file: "../subfolder2/file2", result: "subfolder2/file2" },
55+
{ prefix: "", file: "../subfolder/file2", result: "subfolder/file2" },
56+
{ prefix: "", file: "../../subfolder2/file2", result: "subfolder2/file2" }
57+
];
58+
59+
const curfolder = pth.resolve(".");
60+
// console.log("\n");
61+
for (let test of tests) {
62+
// path.normalize in win32 will convert "/" to native "\" format
63+
64+
const out = sanitize(pth.normalize(test.prefix || ""), test.file);
65+
const res = pth.join(curfolder, pth.normalize(test.result));
66+
67+
expect(out).to.equal(res);
68+
}
69+
});
70+
71+
it("function canonical()", () => {
72+
const tests = [
73+
// no name
74+
{ file: "", result: "" },
75+
// file has name
76+
{ file: "file", result: "file" },
77+
{ file: "../file", result: "file" },
78+
{ file: "../../../file", result: "file" },
79+
{ file: "./../file", result: "file" },
80+
{ file: "../../file", result: "file" },
81+
{ file: "../../file1/../file2", result: "file2" },
82+
{ file: "../subfolder/file2", result: "subfolder/file2" },
83+
{ file: "../subfolder2/file2", result: "subfolder2/file2" },
84+
{ file: "../subfolder/file2", result: "subfolder/file2" },
85+
{ file: "../../subfolder2/file2", result: "subfolder2/file2" }
86+
];
87+
88+
for (let test of tests) {
89+
expect(canonical(test.file)).to.equal(test.result);
90+
}
91+
});
92+
});

util/utils.js

+19
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,25 @@ module.exports = (function () {
123123
return true;
124124
},
125125

126+
canonical: function (p) {
127+
if (!p) return "";
128+
// trick normalize think path is absolute
129+
var safeSuffix = pth.posix.normalize("/" + p.split("\\").join("/"));
130+
return pth.join(".", safeSuffix);
131+
},
132+
133+
sanitize: function (prefix, name) {
134+
prefix = pth.resolve(pth.normalize(prefix));
135+
var parts = name.split("/");
136+
for (var i = 0, l = parts.length; i < l; i++) {
137+
var path = pth.normalize(pth.join(prefix, parts.slice(i, l).join(pth.sep)));
138+
if (path.indexOf(prefix) === 0) {
139+
return path;
140+
}
141+
}
142+
return pth.normalize(pth.join(prefix, pth.basename(name)));
143+
},
144+
126145
writeFileToAsync: function (/*String*/ path, /*Buffer*/ content, /*Boolean*/ overwrite, /*Number*/ attr, /*Function*/ callback) {
127146
if (typeof attr === "function") {
128147
callback = attr;

0 commit comments

Comments
 (0)