Socket
Socket
Sign inDemoInstall

extract-zip

Package Overview
Dependencies
Maintainers
2
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

extract-zip - npm Package Compare versions

Comparing version 1.7.0 to 2.0.0

.eslintrc.typescript.js

11

cli.js
#!/usr/bin/env node
/* eslint-disable no-process-exit */
var extract = require('./')

@@ -13,9 +15,6 @@

extract(source, {dir: dest}, function (err, results) {
if (err) {
extract(source, { dir: dest })
.catch(function (err) {
console.error('error!', err)
process.exit(1)
} else {
process.exit(0)
}
})
})

@@ -1,53 +0,43 @@

var fs = require('fs')
var path = require('path')
var yauzl = require('yauzl')
var mkdirp = require('mkdirp')
var concat = require('concat-stream')
var debug = require('debug')('extract-zip')
const debug = require('debug')('extract-zip')
// eslint-disable-next-line node/no-unsupported-features/node-builtins
const { createWriteStream, promises: fs } = require('fs')
const getStream = require('get-stream')
const path = require('path')
const { promisify } = require('util')
const stream = require('stream')
const yauzl = require('yauzl')
module.exports = function (zipPath, opts, cb) {
debug('creating target directory', opts.dir)
const openZip = promisify(yauzl.open)
const pipeline = promisify(stream.pipeline)
if (path.isAbsolute(opts.dir) === false) {
return cb(new Error('Target directory is expected to be absolute'))
class Extractor {
constructor (zipPath, opts) {
this.zipPath = zipPath
this.opts = opts
}
mkdirp(opts.dir, function (err) {
if (err) return cb(err)
async extract () {
debug('opening', this.zipPath, 'with opts', this.opts)
fs.realpath(opts.dir, function (err, canonicalDir) {
if (err) return cb(err)
this.zipfile = await openZip(this.zipPath, { lazyEntries: true })
this.canceled = false
opts.dir = canonicalDir
openZip(opts)
})
})
function openZip () {
debug('opening', zipPath, 'with opts', opts)
yauzl.open(zipPath, {lazyEntries: true}, function (err, zipfile) {
if (err) return cb(err)
var cancelled = false
zipfile.on('error', function (err) {
if (err) {
cancelled = true
return cb(err)
}
return new Promise((resolve, reject) => {
this.zipfile.on('error', err => {
this.canceled = true
reject(err)
})
zipfile.readEntry()
this.zipfile.readEntry()
zipfile.on('close', function () {
if (!cancelled) {
this.zipfile.on('close', () => {
if (!this.canceled) {
debug('zip extraction complete')
cb()
resolve()
}
})
zipfile.on('entry', function (entry) {
if (cancelled) {
debug('skipping entry', entry.fileName, {cancelled: cancelled})
this.zipfile.on('entry', async entry => {
/* istanbul ignore if */
if (this.canceled) {
debug('skipping entry', entry.fileName, { cancelled: this.canceled })
return

@@ -58,155 +48,130 @@ }

if (/^__MACOSX\//.test(entry.fileName)) {
// dir name starts with __MACOSX/
zipfile.readEntry()
if (entry.fileName.startsWith('__MACOSX/')) {
this.zipfile.readEntry()
return
}
var destDir = path.dirname(path.join(opts.dir, entry.fileName))
const destDir = path.dirname(path.join(this.opts.dir, entry.fileName))
mkdirp(destDir, function (err) {
if (err) {
cancelled = true
zipfile.close()
return cb(err)
}
try {
await fs.mkdir(destDir, { recursive: true })
fs.realpath(destDir, function (err, canonicalDestDir) {
if (err) {
cancelled = true
zipfile.close()
return cb(err)
}
const canonicalDestDir = await fs.realpath(destDir)
const relativeDestDir = path.relative(this.opts.dir, canonicalDestDir)
var relativeDestDir = path.relative(opts.dir, canonicalDestDir)
if (relativeDestDir.split(path.sep).includes('..')) {
throw new Error(`Out of bound path "${canonicalDestDir}" found while processing file ${entry.fileName}`)
}
if (relativeDestDir.split(path.sep).indexOf('..') !== -1) {
cancelled = true
zipfile.close()
return cb(new Error('Out of bound path "' + canonicalDestDir + '" found while processing file ' + entry.fileName))
}
extractEntry(entry, function (err) {
// if any extraction fails then abort everything
if (err) {
cancelled = true
zipfile.close()
return cb(err)
}
debug('finished processing', entry.fileName)
zipfile.readEntry()
})
})
})
await this.extractEntry(entry)
debug('finished processing', entry.fileName)
this.zipfile.readEntry()
} catch (err) {
this.canceled = true
this.zipfile.close()
reject(err)
}
})
})
}
function extractEntry (entry, done) {
if (cancelled) {
debug('skipping entry extraction', entry.fileName, {cancelled: cancelled})
return setImmediate(done)
}
async extractEntry (entry) {
/* istanbul ignore if */
if (this.canceled) {
debug('skipping entry extraction', entry.fileName, { cancelled: this.canceled })
return
}
if (opts.onEntry) {
opts.onEntry(entry, zipfile)
}
if (this.opts.onEntry) {
this.opts.onEntry(entry, this.zipfile)
}
var dest = path.join(opts.dir, entry.fileName)
const dest = path.join(this.opts.dir, entry.fileName)
// convert external file attr int into a fs stat mode int
var mode = (entry.externalFileAttributes >> 16) & 0xFFFF
// check if it's a symlink or dir (using stat mode constants)
var IFMT = 61440
var IFDIR = 16384
var IFLNK = 40960
var symlink = (mode & IFMT) === IFLNK
var isDir = (mode & IFMT) === IFDIR
// convert external file attr int into a fs stat mode int
const mode = (entry.externalFileAttributes >> 16) & 0xFFFF
// check if it's a symlink or dir (using stat mode constants)
const IFMT = 61440
const IFDIR = 16384
const IFLNK = 40960
const symlink = (mode & IFMT) === IFLNK
let isDir = (mode & IFMT) === IFDIR
// Failsafe, borrowed from jsZip
if (!isDir && entry.fileName.slice(-1) === '/') {
isDir = true
}
// Failsafe, borrowed from jsZip
if (!isDir && entry.fileName.endsWith('/')) {
isDir = true
}
// check for windows weird way of specifying a directory
// https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566
var madeBy = entry.versionMadeBy >> 8
if (!isDir) isDir = (madeBy === 0 && entry.externalFileAttributes === 16)
// check for windows weird way of specifying a directory
// https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566
const madeBy = entry.versionMadeBy >> 8
if (!isDir) isDir = (madeBy === 0 && entry.externalFileAttributes === 16)
// if no mode then default to default modes
if (mode === 0) {
if (isDir) {
if (opts.defaultDirMode) mode = parseInt(opts.defaultDirMode, 10)
if (!mode) mode = 493 // Default to 0755
} else {
if (opts.defaultFileMode) mode = parseInt(opts.defaultFileMode, 10)
if (!mode) mode = 420 // Default to 0644
}
}
debug('extracting entry', { filename: entry.fileName, isDir: isDir, isSymlink: symlink })
debug('extracting entry', { filename: entry.fileName, isDir: isDir, isSymlink: symlink })
// reverse umask first (~)
const umask = ~process.umask()
// & with processes umask to override invalid perms
const procMode = this.getExtractedMode(mode, isDir) & umask
// reverse umask first (~)
var umask = ~process.umask()
// & with processes umask to override invalid perms
var procMode = mode & umask
// always ensure folders are created
const destDir = isDir ? dest : path.dirname(dest)
// always ensure folders are created
var destDir = dest
if (!isDir) destDir = path.dirname(dest)
const mkdirOptions = { recursive: true }
if (isDir) {
mkdirOptions.mode = procMode
}
debug('mkdir', { dir: destDir, ...mkdirOptions })
await fs.mkdir(destDir, mkdirOptions)
if (isDir) return
debug('mkdirp', {dir: destDir})
mkdirp(destDir, function (err) {
if (err) {
debug('mkdirp error', destDir, {error: err})
cancelled = true
return done(err)
}
debug('opening read stream', dest)
const readStream = await promisify(this.zipfile.openReadStream.bind(this.zipfile))(entry)
if (isDir) return done()
if (symlink) {
const link = await getStream(readStream)
debug('creating symlink', link, dest)
await fs.symlink(link, dest)
} else {
await pipeline(readStream, createWriteStream(dest, { mode: procMode }))
}
}
debug('opening read stream', dest)
zipfile.openReadStream(entry, function (err, readStream) {
if (err) {
debug('openReadStream error', err)
cancelled = true
return done(err)
}
getExtractedMode (entryMode, isDir) {
let mode = entryMode
// Set defaults, if necessary
if (mode === 0) {
if (isDir) {
if (this.opts.defaultDirMode) {
mode = parseInt(this.opts.defaultDirMode, 10)
}
readStream.on('error', function (err) {
console.log('read err', err)
})
if (!mode) {
mode = 0o755
}
} else {
if (this.opts.defaultFileMode) {
mode = parseInt(this.opts.defaultFileMode, 10)
}
if (symlink) writeSymlink()
else writeStream()
if (!mode) {
mode = 0o644
}
}
}
function writeStream () {
var writeStream = fs.createWriteStream(dest, {mode: procMode})
readStream.pipe(writeStream)
return mode
}
}
writeStream.on('finish', function () {
done()
})
module.exports = async function (zipPath, opts) {
debug('creating target directory', opts.dir)
writeStream.on('error', function (err) {
debug('write error', {error: err})
cancelled = true
return done(err)
})
}
if (!path.isAbsolute(opts.dir)) {
throw new Error('Target directory is expected to be absolute')
}
// AFAICT the content of the symlink file itself is the symlink target filename string
function writeSymlink () {
readStream.pipe(concat(function (data) {
var link = data.toString()
debug('creating symlink', link, dest)
fs.symlink(link, dest, function (err) {
if (err) cancelled = true
done(err)
})
}))
}
})
})
}
})
}
await fs.mkdir(opts.dir, { recursive: true })
opts.dir = await fs.realpath(opts.dir)
return new Extractor(zipPath, opts).extract()
}
{
"name": "extract-zip",
"version": "1.7.0",
"version": "2.0.0",
"description": "unzip a zip file into a directory using 100% javascript",
"main": "index.js",
"types": "index.d.ts",
"bin": {

@@ -10,4 +11,14 @@ "extract-zip": "cli.js"

"scripts": {
"test": "standard && node test/test.js"
"ava": "ava",
"coverage": "nyc ava",
"lint": "yarn lint:js && yarn lint:ts && yarn tsd",
"lint:js": "eslint .",
"lint:ts": "eslint --config .eslintrc.typescript.js --ext .ts .",
"test": "yarn lint && ava",
"tsd": "tsd"
},
"files": [
"*.js",
"index.d.ts"
],
"author": "max ogden",

@@ -21,17 +32,51 @@ "license": "BSD-2-Clause",

],
"engines": {
"node": ">= 10.12.0"
},
"dependencies": {
"concat-stream": "^1.6.2",
"debug": "^2.6.9",
"mkdirp": "^0.5.4",
"debug": "^4.1.1",
"get-stream": "^5.1.0",
"yauzl": "^2.10.0"
},
"optionalDependencies": {
"@types/yauzl": "^2.9.1"
},
"devDependencies": {
"rimraf": "^2.2.8",
"standard": "^5.2.2",
"tape": "^4.2.0",
"temp": "^0.8.3"
"@typescript-eslint/eslint-plugin": "^2.25.0",
"@typescript-eslint/parser": "^2.25.0",
"ava": "^3.5.1",
"eslint": "^6.8.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-ava": "^10.2.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"fs-extra": "^9.0.0",
"husky": "^4.2.3",
"lint-staged": "^10.0.9",
"nyc": "^15.0.0",
"tsd": "^0.11.0",
"typescript": "^3.8.3"
},
"directories": {
"test": "test"
"eslintConfig": {
"extends": [
"eslint:recommended",
"plugin:ava/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:node/recommended",
"plugin:promise/recommended",
"standard"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": "yarn lint:js --fix",
"*.ts": "yarn lint:ts --fix"
}
}

@@ -7,8 +7,10 @@ # extract-zip

[![NPM](https://nodei.co/npm/extract-zip.png?global=true)](https://nodei.co/npm/extract-zip/)
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
[![Build Status](https://travis-ci.org/maxogden/extract-zip.svg?branch=master)](https://travis-ci.org/maxogden/extract-zip)
[![NPM](https://nodei.co/npm/extract-zip.png?global=true)](https://npm.im/extract-zip)
[![Uses JS Standard Style](https://cdn.jsdelivr.net/gh/standard/standard/badge.svg)](https://github.com/standard/standard)
[![Build Status](https://github.com/maxogden/extract-zip/workflows/CI/badge.svg)](https://github.com/maxogden/extract-zip/actions?query=workflow%3ACI)
## Installation
Make sure you have Node 10 or greater installed.
Get the library:

@@ -28,7 +30,13 @@

```js
var extract = require('extract-zip')
extract(source, {dir: target}, function (err) {
// extraction is complete. make sure to handle the err
})
```javascript
const extract = require('extract-zip')
async main () {
try {
await extract(source, { dir: target })
console.log('Extraction complete')
} catch (err) {
// handle any errors
}
}
```

@@ -38,5 +46,5 @@

- `dir` - defaults to `process.cwd()`
- `defaultDirMode` - integer - Directory Mode (permissions) will default to `493` (octal `0755` in integer)
- `defaultFileMode` - integer - File Mode (permissions) will default to `420` (octal `0644` in integer)
- `dir` (required) - the path to the directory where the extracted files are written
- `defaultDirMode` - integer - Directory Mode (permissions), defaults to `0o755`
- `defaultFileMode` - integer - File Mode (permissions), defaults to `0o644`
- `onEntry` - function - if present, will be called with `(entry, zipfile)`, entry is every entry from the zip file forwarded from the `entry` event from yauzl. `zipfile` is the `yauzl` instance

@@ -43,0 +51,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc