extract.js 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. module.exports = Extract;
  2. const Parse = require('./parse');
  3. const fs = require('fs-extra');
  4. const path = require('path');
  5. const stream = require('stream');
  6. const duplexer2 = require('duplexer2');
  7. function Extract (opts) {
  8. // make sure path is normalized before using it
  9. opts.path = path.resolve(path.normalize(opts.path));
  10. const parser = new Parse(opts);
  11. const outStream = new stream.Writable({objectMode: true});
  12. outStream._write = async function(entry, encoding, cb) {
  13. // to avoid zip slip (writing outside of the destination), we resolve
  14. // the target path, and make sure it's nested in the intended
  15. // destination, or not extract it otherwise.
  16. // NOTE: Need to normalize to forward slashes for UNIX OS's to properly
  17. // ignore the zip slipped file entirely
  18. const extractPath = path.join(opts.path, entry.path.replace(/\\/g, '/'));
  19. if (extractPath.indexOf(opts.path) != 0) {
  20. return cb();
  21. }
  22. if (entry.type == 'Directory') {
  23. await fs.ensureDir(extractPath);
  24. return cb();
  25. }
  26. await fs.ensureDir(path.dirname(extractPath));
  27. const writer = opts.getWriter ? opts.getWriter({path: extractPath}) : fs.createWriteStream(extractPath);
  28. entry.pipe(writer)
  29. .on('error', cb)
  30. .on('close', cb);
  31. };
  32. const extract = duplexer2(parser, outStream);
  33. parser.once('crx-header', function(crxHeader) {
  34. extract.crxHeader = crxHeader;
  35. });
  36. parser
  37. .pipe(outStream)
  38. .on('finish', function() {
  39. extract.emit('close');
  40. });
  41. extract.promise = function() {
  42. return new Promise(function(resolve, reject) {
  43. extract.on('close', resolve);
  44. extract.on('error', reject);
  45. });
  46. };
  47. return extract;
  48. }