compressing 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/utils.js +36 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -169,7 +169,7 @@ const urllib = require('urllib');
|
|
|
169
169
|
const targetDir = require('os').tmpdir();
|
|
170
170
|
const compressing = require('compressing');
|
|
171
171
|
|
|
172
|
-
urllib.request('http://registry.npmjs.org/
|
|
172
|
+
urllib.request('http://registry.npmjs.org/compressing/-/compressing-2.0.0.tgz', {
|
|
173
173
|
streaming: true,
|
|
174
174
|
followRedirect: true,
|
|
175
175
|
})
|
package/lib/utils.js
CHANGED
|
@@ -4,6 +4,22 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { pipeline: pump } = require('stream');
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Check if childPath is within parentPath (prevents path traversal attacks)
|
|
9
|
+
* @param {string} childPath - The path to check
|
|
10
|
+
* @param {string} parentPath - The parent directory path
|
|
11
|
+
* @returns {boolean} - True if childPath is within parentPath
|
|
12
|
+
*/
|
|
13
|
+
function isPathWithinParent(childPath, parentPath) {
|
|
14
|
+
const normalizedChild = path.resolve(childPath);
|
|
15
|
+
const normalizedParent = path.resolve(parentPath);
|
|
16
|
+
const parentWithSep = normalizedParent.endsWith(path.sep)
|
|
17
|
+
? normalizedParent
|
|
18
|
+
: normalizedParent + path.sep;
|
|
19
|
+
return normalizedChild === normalizedParent ||
|
|
20
|
+
normalizedChild.startsWith(parentWithSep);
|
|
21
|
+
}
|
|
22
|
+
|
|
7
23
|
// file/fileBuffer/stream
|
|
8
24
|
exports.sourceType = source => {
|
|
9
25
|
if (!source) return undefined;
|
|
@@ -92,6 +108,9 @@ exports.makeUncompressFn = StreamClass => {
|
|
|
92
108
|
fs.mkdir(destDir, { recursive: true }, err => {
|
|
93
109
|
if (err) return reject(err);
|
|
94
110
|
|
|
111
|
+
// Resolve destDir to absolute path for security validation
|
|
112
|
+
const resolvedDestDir = path.resolve(destDir);
|
|
113
|
+
|
|
95
114
|
let entryCount = 0;
|
|
96
115
|
let successCount = 0;
|
|
97
116
|
let isFinish = false;
|
|
@@ -108,7 +127,15 @@ exports.makeUncompressFn = StreamClass => {
|
|
|
108
127
|
.on('error', reject)
|
|
109
128
|
.on('entry', (header, stream, next) => {
|
|
110
129
|
stream.on('end', next);
|
|
111
|
-
const destFilePath = path.join(
|
|
130
|
+
const destFilePath = path.join(resolvedDestDir, header.name);
|
|
131
|
+
const resolvedDestPath = path.resolve(destFilePath);
|
|
132
|
+
|
|
133
|
+
// Security: Validate that the entry path doesn't escape the destination directory
|
|
134
|
+
if (!isPathWithinParent(resolvedDestPath, resolvedDestDir)) {
|
|
135
|
+
console.warn(`[compressing] Skipping entry with path traversal: "${header.name}" -> "${resolvedDestPath}"`);
|
|
136
|
+
stream.resume();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
112
139
|
|
|
113
140
|
if (header.type === 'file') {
|
|
114
141
|
const dir = path.dirname(destFilePath);
|
|
@@ -125,6 +152,14 @@ exports.makeUncompressFn = StreamClass => {
|
|
|
125
152
|
} else if (header.type === 'symlink') {
|
|
126
153
|
const dir = path.dirname(destFilePath);
|
|
127
154
|
const target = path.resolve(dir, header.linkname);
|
|
155
|
+
|
|
156
|
+
// Security: Validate that the symlink target doesn't escape the destination directory
|
|
157
|
+
if (!isPathWithinParent(target, resolvedDestDir)) {
|
|
158
|
+
console.warn(`[compressing] Skipping symlink "${header.name}": target "${target}" escapes extraction directory`);
|
|
159
|
+
stream.resume();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
128
163
|
entryCount++;
|
|
129
164
|
|
|
130
165
|
fs.mkdir(dir, { recursive: true }, err => {
|