Skip to content

Commit

Permalink
Rewrite extract to user duplexer2 to separate the events of the inbou…
Browse files Browse the repository at this point in the history
…nd stream and outbound stream

add tests for broken zipfiles
  • Loading branch information
ZJONSSON committed May 21, 2019
1 parent 076b76a commit 5e26471
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 37 deletions.
65 changes: 28 additions & 37 deletions lib/extract.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,51 @@ module.exports = Extract;

var Parse = require('./parse');
var Writer = require('fstream').Writer;
var util = require('util');
var path = require('path');

util.inherits(Extract, Parse);
var stream = require('stream');
var duplexer2 = require('duplexer2');
var Promise = require('bluebird');

function Extract (opts) {
if (!(this instanceof Extract))
return new Extract(opts);

var self = this;

var finishCb;
var pending = 0;
var _final = typeof this._final === 'function' ? this._final : undefined;

function checkFinished() {
if (pending === 0 && finishCb) {
_final ? _final(finishCb) : finishCb();
}
}

this._final = function(cb) {
finishCb = cb;
checkFinished();
};

// make sure path is normalized before using it
opts.path = path.normalize(opts.path);

Parse.call(self,opts);
var parser = new Parse(opts);

self.on('entry', function(entry) {
if (entry.type == 'Directory') return;
var outStream = new stream.Transform({objectMode: true});
outStream._transform = function(entry, encoding, cb) {

if (entry.type == 'Directory') return cb();

// to avoid zip slip (writing outside of the destination), we resolve
// the target path, and make sure it's nested in the intended
// destination, or not extract it otherwise.
var extractPath = path.join(opts.path, entry.path);
if (extractPath.indexOf(opts.path) != 0) {
return;
return cb();
}

const writer = opts.getWriter ? opts.getWriter({path: extractPath}) : Writer({ path: extractPath });

pending += 1;
entry.pipe(writer)
.on('error',function(e) {
self.emit('error',e);
pending -= 1;
checkFinished();
})
.on('close', function() {
pending -= 1;
checkFinished();
.on('error', cb)
.on('close', cb);
};

var extract = duplexer2(parser,outStream);

parser
.pipe(outStream)
.on('finish',function() {
extract.emit('close');
});
});

extract.promise = function() {
return new Promise(function(resolve, reject) {
extract.on('finish', resolve);
extract.on('error',reject);
});
};

return extract;
}
43 changes: 43 additions & 0 deletions test/broken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

var test = require('tap').test;
var fs = require('fs');
var path = require('path');
var temp = require('temp');
var unzip = require('../');


test("Parse a broken zipfile", function (t) {
var archive = path.join(__dirname, '../testData/compressed-standard/broken.zip');

fs.createReadStream(archive)
.pipe(unzip.Parse())
.on('entry', function(entry) {
return entry.autodrain();
})
.promise()
.catch(function(e) {
t.same(e.message, 'FILE_ENDED');
t.end();
});
});


test("extract a broken", function (t) {
var archive = path.join(__dirname, '../testData/compressed-standard/broken.zip');

temp.mkdir('node-unzip-', function (err, dirPath) {
if (err) {
throw err;
}
var unzipExtractor = unzip.Extract({ path: dirPath });

fs.createReadStream(archive)
.pipe(unzipExtractor)
.promise()
.catch(function(e) {
t.same(e.message,'FILE_ENDED');
t.end();
});
});
});
Binary file added testData/compressed-standard/broken.zip
Binary file not shown.

0 comments on commit 5e26471

Please sign in to comment.