compressing 2.0.0 → 2.1.0

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 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/pedding/-/pedding-1.1.0.tgz', {
172
+ urllib.request('http://registry.npmjs.org/compressing/-/compressing-2.0.0.tgz', {
173
173
  streaming: true,
174
174
  followRedirect: true,
175
175
  })
@@ -280,6 +280,7 @@ Params
280
280
  - Korean: cp949, euc-kr
281
281
  - Japanese: sjis (shift_jis), cp932, euc-jp
282
282
  - Chinese: gbk, gb18030, gb2312, cp936, hkscs, big5, cp950
283
+ - opts.strip {Number} - Strip leading path segments when extracting (tar/tgz/zip). Default is 0.
283
284
 
284
285
  ### FileStream
285
286
 
@@ -359,6 +360,7 @@ __Constructor__
359
360
  Common params:
360
361
 
361
362
  - opts.source {String|Buffer|Stream} - source to be uncompressed, could be a file path, buffer, or a readable stream.
363
+ - opts.strip {Number} - Strip leading path segments when extracting (tar/tgz/zip). Default is 0.
362
364
 
363
365
  __CAUTION for zip.UncompressStream__
364
366
 
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;
@@ -88,10 +104,17 @@ exports.makeUncompressFn = StreamClass => {
88
104
  throw error;
89
105
  }
90
106
 
107
+ const strip = opts.strip ? Number(opts.strip) : 0;
108
+ // Strip is handled here in makeUncompressFn, so remove it from opts to avoid passing to UncompressStream
109
+ delete opts.strip;
110
+
91
111
  return new Promise((resolve, reject) => {
92
112
  fs.mkdir(destDir, { recursive: true }, err => {
93
113
  if (err) return reject(err);
94
114
 
115
+ // Resolve destDir to absolute path for security validation
116
+ const resolvedDestDir = path.resolve(destDir);
117
+
95
118
  let entryCount = 0;
96
119
  let successCount = 0;
97
120
  let isFinish = false;
@@ -108,7 +131,15 @@ exports.makeUncompressFn = StreamClass => {
108
131
  .on('error', reject)
109
132
  .on('entry', (header, stream, next) => {
110
133
  stream.on('end', next);
111
- const destFilePath = path.join(destDir, header.name);
134
+ const destFilePath = path.join(resolvedDestDir, stripFileName(strip, header.name, header.type));
135
+ const resolvedDestPath = path.resolve(destFilePath);
136
+
137
+ // Security: Validate that the entry path doesn't escape the destination directory
138
+ if (!isPathWithinParent(resolvedDestPath, resolvedDestDir)) {
139
+ console.warn(`[compressing] Skipping entry with path traversal: "${header.name}" -> "${resolvedDestPath}"`);
140
+ stream.resume();
141
+ return;
142
+ }
112
143
 
113
144
  if (header.type === 'file') {
114
145
  const dir = path.dirname(destFilePath);
@@ -125,6 +156,14 @@ exports.makeUncompressFn = StreamClass => {
125
156
  } else if (header.type === 'symlink') {
126
157
  const dir = path.dirname(destFilePath);
127
158
  const target = path.resolve(dir, header.linkname);
159
+
160
+ // Security: Validate that the symlink target doesn't escape the destination directory
161
+ if (!isPathWithinParent(target, resolvedDestDir)) {
162
+ console.warn(`[compressing] Skipping symlink "${header.name}": target "${target}" escapes extraction directory`);
163
+ stream.resume();
164
+ return;
165
+ }
166
+
128
167
  entryCount++;
129
168
 
130
169
  fs.mkdir(dir, { recursive: true }, err => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compressing",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Everything you need for compressing and uncompressing",
5
5
  "main": "index.js",
6
6
  "scripts": {