@pnpm/exe 11.9.0 → 11.10.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.
@@ -60,6 +60,10 @@ const SAW_VALID_ENTRY = Symbol('sawValidEntry');
60
60
  const SAW_NULL_BLOCK = Symbol('sawNullBlock');
61
61
  const SAW_EOF = Symbol('sawEOF');
62
62
  const CLOSESTREAM = Symbol('closeStream');
63
+ const MAX_DECOMPRESSION_RATIO = 1000;
64
+ const COMPRESSEDBYTESREAD = Symbol('compressedBytesRead');
65
+ const DECOMPRESSEDBYTESREAD = Symbol('decompressedBytesRead');
66
+ const CHECKDECOMPRESSIONRATIO = Symbol('checkDecompressionRatio');
63
67
  const noop = () => true;
64
68
  class Parser extends events_1.EventEmitter {
65
69
  file;
@@ -68,6 +72,7 @@ class Parser extends events_1.EventEmitter {
68
72
  filter;
69
73
  brotli;
70
74
  zstd;
75
+ maxDecompressionRatio;
71
76
  writable = true;
72
77
  readable = false;
73
78
  [QUEUE] = [];
@@ -87,6 +92,8 @@ class Parser extends events_1.EventEmitter {
87
92
  [WRITING] = false;
88
93
  [CONSUMING] = false;
89
94
  [EMITTEDEND] = false;
95
+ [COMPRESSEDBYTESREAD] = 0;
96
+ [DECOMPRESSEDBYTESREAD] = 0;
90
97
  constructor(opt = {}) {
91
98
  super();
92
99
  this.file = opt.file || '';
@@ -109,6 +116,10 @@ class Parser extends events_1.EventEmitter {
109
116
  });
110
117
  }
111
118
  this.strict = !!opt.strict;
119
+ this.maxDecompressionRatio =
120
+ typeof opt.maxDecompressionRatio === 'number' ?
121
+ opt.maxDecompressionRatio
122
+ : MAX_DECOMPRESSION_RATIO;
112
123
  this.maxMetaEntrySize = opt.maxMetaEntrySize || maxMetaEntrySize;
113
124
  this.filter = typeof opt.filter === 'function' ? opt.filter : noop;
114
125
  // Unlike gzip, brotli doesn't have any magic bytes to identify it
@@ -362,11 +373,23 @@ class Parser extends events_1.EventEmitter {
362
373
  }
363
374
  }
364
375
  abort(error) {
376
+ if (this[ABORTED]) {
377
+ return;
378
+ }
365
379
  this[ABORTED] = true;
366
380
  this.emit('abort', error);
367
381
  // always throws, even in non-strict mode
368
382
  this.warn('TAR_ABORT', error, { recoverable: false });
369
383
  }
384
+ [CHECKDECOMPRESSIONRATIO](chunk) {
385
+ this[DECOMPRESSEDBYTESREAD] += chunk.length;
386
+ const ratio = this[DECOMPRESSEDBYTESREAD] / this[COMPRESSEDBYTESREAD];
387
+ if (ratio > this.maxDecompressionRatio) {
388
+ this.abort(new Error(`max decompression ratio exceeded: ${ratio.toFixed(2)} > ${this.maxDecompressionRatio}`));
389
+ return false;
390
+ }
391
+ return true;
392
+ }
370
393
  write(chunk, encoding, cb) {
371
394
  if (typeof encoding === 'function') {
372
395
  cb = encoding;
@@ -450,13 +473,22 @@ class Parser extends events_1.EventEmitter {
450
473
  this[UNZIP] === undefined ? new minizlib_1.Unzip({})
451
474
  : isZstd ? new minizlib_1.ZstdDecompress({})
452
475
  : new minizlib_1.BrotliDecompress({});
453
- this[UNZIP].on('data', chunk => this[CONSUMECHUNK](chunk));
454
- this[UNZIP].on('error', er => this.abort(er));
476
+ this[UNZIP].on('data', chunk => {
477
+ if (this[CHECKDECOMPRESSIONRATIO](chunk)) {
478
+ this[CONSUMECHUNK](chunk);
479
+ }
480
+ });
481
+ this[UNZIP].on('error', er => {
482
+ if (!this[ABORTED]) {
483
+ this.abort(er);
484
+ }
485
+ });
455
486
  this[UNZIP].on('end', () => {
456
487
  this[ENDED] = true;
457
488
  this[CONSUMECHUNK]();
458
489
  });
459
490
  this[WRITING] = true;
491
+ this[COMPRESSEDBYTESREAD] += chunk.length;
460
492
  const ret = !!this[UNZIP][ended ? 'end' : 'write'](chunk);
461
493
  this[WRITING] = false;
462
494
  cb?.();
@@ -465,6 +497,7 @@ class Parser extends events_1.EventEmitter {
465
497
  }
466
498
  this[WRITING] = true;
467
499
  if (this[UNZIP]) {
500
+ this[COMPRESSEDBYTESREAD] += chunk.length;
468
501
  this[UNZIP].write(chunk);
469
502
  }
470
503
  else {
@@ -495,7 +528,7 @@ class Parser extends events_1.EventEmitter {
495
528
  !this[CONSUMING]) {
496
529
  this[EMITTEDEND] = true;
497
530
  const entry = this[WRITEENTRY];
498
- if (entry && entry.blockRemain) {
531
+ if (entry?.blockRemain) {
499
532
  // truncated, likely a damaged file
500
533
  const have = this[BUFFER] ? this[BUFFER].length : 0;
501
534
  this.warn('TAR_BAD_ARCHIVE', `Truncated input (needed ${entry.blockRemain} more bytes, only ${have} available)`, { entry });
@@ -589,8 +622,10 @@ class Parser extends events_1.EventEmitter {
589
622
  if (!this[ABORTED]) {
590
623
  if (this[UNZIP]) {
591
624
  /* c8 ignore start */
592
- if (chunk)
625
+ if (chunk) {
626
+ this[COMPRESSEDBYTESREAD] += chunk.length;
593
627
  this[UNZIP].write(chunk);
628
+ }
594
629
  /* c8 ignore stop */
595
630
  this[UNZIP].end();
596
631
  }
@@ -147,12 +147,36 @@ const parseKVLine = (set, line) => {
147
147
  return set;
148
148
  }
149
149
  const k = r.replace(/^SCHILY\.(dev|ino|nlink)/, '$1');
150
- const v = kv.join('=');
151
- set[k] =
152
- /^([A-Z]+\.)?([mac]|birth|creation)time$/.test(k) ?
153
- new Date(Number(v) * 1000)
154
- : /^[0-9]+$/.test(v) ? +v
155
- : v;
150
+ const v = kv.join('=').replace(/\0.*/, '');
151
+ switch (k) {
152
+ case 'path':
153
+ case 'linkpath':
154
+ case 'type':
155
+ case 'charset':
156
+ case 'comment':
157
+ case 'gname':
158
+ case 'uname':
159
+ set[k] = v;
160
+ break;
161
+ case 'ctime':
162
+ case 'atime':
163
+ case 'mtime':
164
+ set[k] = new Date(Number(v) * 1000);
165
+ break;
166
+ case 'size':
167
+ const s = +v;
168
+ if (s >= 0)
169
+ set[k] = s;
170
+ break;
171
+ case 'gid':
172
+ case 'uid':
173
+ case 'dev':
174
+ case 'ino':
175
+ case 'nlink':
176
+ case 'mode':
177
+ set[k] = +v;
178
+ break;
179
+ }
156
180
  return set;
157
181
  };
158
182
  //# sourceMappingURL=pax.js.map
@@ -185,7 +185,7 @@ class Unpack extends parse_js_1.Parser {
185
185
  // default true for root
186
186
  this.preserveOwner =
187
187
  opt.preserveOwner === undefined && typeof opt.uid !== 'number' ?
188
- !!(process.getuid && process.getuid() === 0)
188
+ !!(process.getuid?.() === 0)
189
189
  : !!opt.preserveOwner;
190
190
  this.processUid =
191
191
  (this.preserveOwner || this.setOwner) && process.getuid ?
@@ -406,7 +406,7 @@ class Unpack extends parse_js_1.Parser {
406
406
  }
407
407
  }
408
408
  [MKDIR](dir, mode, cb) {
409
- (0, mkdir_js_1.mkdir)((0, normalize_windows_path_js_1.normalizeWindowsPath)(dir), {
409
+ void (0, mkdir_js_1.mkdir)((0, normalize_windows_path_js_1.normalizeWindowsPath)(dir), {
410
410
  uid: this.uid,
411
411
  gid: this.gid,
412
412
  processUid: this.processUid,
@@ -5,6 +5,7 @@
5
5
  import { posix as pathModule } from 'node:path';
6
6
  import * as large from './large-numbers.js';
7
7
  import * as types from './types.js';
8
+ const notNegative = (n) => n === undefined || n < 0 ? undefined : n;
8
9
  export class Header {
9
10
  cksumValid = false;
10
11
  needPax = false;
@@ -63,10 +64,9 @@ export class Header {
63
64
  exForFields?.uid ?? gexForFields?.uid ?? decNumber(buf, off + 108, 8);
64
65
  this.gid =
65
66
  exForFields?.gid ?? gexForFields?.gid ?? decNumber(buf, off + 116, 8);
66
- this.size =
67
- exForFields?.size ??
68
- gexForFields?.size ??
69
- decNumber(buf, off + 124, 12);
67
+ this.size = notNegative(exForFields?.size ??
68
+ gexForFields?.size ??
69
+ decNumber(buf, off + 124, 12));
70
70
  this.mtime =
71
71
  exForFields?.mtime ??
72
72
  gexForFields?.mtime ??
@@ -152,6 +152,7 @@ export class Header {
152
152
  // null/undefined values are ignored.
153
153
  return !(v === null ||
154
154
  v === undefined ||
155
+ (k === 'size' && Number(v) < 0) ||
155
156
  (k === 'path' && gex) ||
156
157
  (k === 'linkpath' && gex) ||
157
158
  k === 'global');