Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update all the stuff #95

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
push:
pull_request:
workflow_dispatch:

env:
FORCE_COLOR: 2

jobs:
test:
name: Node ${{ matrix.node }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
matrix:
node: [12, 14, 16, 18]
os: [ubuntu-latest, windows-latest]

steps:
- name: Clone repository
uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}

- name: Install npm dependencies
run: npm install

- name: Run tests
run: npm run test-ci
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
yarn.lock
/coverage
5 changes: 0 additions & 5 deletions .travis.yml

This file was deleted.

145 changes: 73 additions & 72 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,71 @@
'use strict';
const path = require('path');
const fs = require('graceful-fs');
const decompressTar = require('decompress-tar');
const decompressTarbz2 = require('decompress-tarbz2');
const decompressTargz = require('decompress-targz');
const decompressUnzip = require('decompress-unzip');
const makeDir = require('make-dir');
const pify = require('pify');
const stripDirs = require('strip-dirs');
import {Buffer} from 'node:buffer';
import path from 'node:path';
import process from 'node:process';
import decompressTar from 'decompress-tar';
import decompressTarbz2 from 'decompress-tarbz2';
import decompressTargz from 'decompress-targz';
import decompressUnzip from 'decompress-unzip';
import fs from 'graceful-fs';
import makeDir from 'make-dir';
import pify from 'pify';
import stripDirs from 'strip-dirs';

const fsP = pify(fs);

const runPlugins = (input, opts) => {
if (opts.plugins.length === 0) {
const runPlugins = (input, options) => {
if (options.plugins.length === 0) {
return Promise.resolve([]);
}

return Promise.all(opts.plugins.map(x => x(input, opts))).then(files => files.reduce((a, b) => a.concat(b)));
return Promise.all(options.plugins.map(x => x(input, options)))
// eslint-disable-next-line unicorn/no-array-reduce, unicorn/prefer-spread
.then(files => files.reduce((a, b) => a.concat(b)));
};

const safeMakeDir = (dir, realOutputPath) => {
return fsP.realpath(dir)
.catch(_ => {
const parent = path.dirname(dir);
return safeMakeDir(parent, realOutputPath);
})
.then(realParentPath => {
if (realParentPath.indexOf(realOutputPath) !== 0) {
throw (new Error('Refusing to create a directory outside the output path.'));
}

return makeDir(dir).then(fsP.realpath);
});
};
const safeMakeDir = (dir, realOutputPath) => fsP.realpath(dir)
.catch(_ => {
const parent = path.dirname(dir);
return safeMakeDir(parent, realOutputPath);
})
.then(realParentPath => {
if (realParentPath.indexOf(realOutputPath) !== 0) {
throw new Error('Refusing to create a directory outside the output path.');
}

const preventWritingThroughSymlink = (destination, realOutputPath) => {
return fsP.readlink(destination)
.catch(_ => {
// Either no file exists, or it's not a symlink. In either case, this is
// not an escape we need to worry about in this phase.
return null;
})
.then(symlinkPointsTo => {
if (symlinkPointsTo) {
throw new Error('Refusing to write into a symlink');
}

// No symlink exists at `destination`, so we can continue
return realOutputPath;
});
};
return makeDir(dir).then(fsP.realpath);
});

const preventWritingThroughSymlink = (destination, realOutputPath) => fsP.readlink(destination)
// Either no file exists, or it's not a symlink. In either case, this is
// not an escape we need to worry about in this phase.
.catch(_ => null)
.then(symlinkPointsTo => {
if (symlinkPointsTo) {
throw new Error('Refusing to write into a symlink');
}

// No symlink exists at `destination`, so we can continue
return realOutputPath;
});

const extractFile = (input, output, opts) => runPlugins(input, opts).then(files => {
if (opts.strip > 0) {
const extractFile = (input, output, options) => runPlugins(input, options).then(files => {
if (options.strip > 0) {
files = files
.map(x => {
x.path = stripDirs(x.path, opts.strip);
x.path = stripDirs(x.path, options.strip);
return x;
})
.filter(x => x.path !== '.');
}

if (typeof opts.filter === 'function') {
files = files.filter(opts.filter);
if (typeof options.filter === 'function') {
// eslint-disable-next-line unicorn/no-array-callback-reference
files = files.filter(options.filter);
}

if (typeof opts.map === 'function') {
files = files.map(opts.map);
if (typeof options.map === 'function') {
// eslint-disable-next-line unicorn/no-array-callback-reference
files = files.map(options.map);
}

if (!output) {
Expand All @@ -75,7 +74,7 @@ const extractFile = (input, output, opts) => runPlugins(input, opts).then(files

return Promise.all(files.map(x => {
const dest = path.join(output, x.path);
const mode = x.mode & ~process.umask();
const mode = x.mode & ~process.umask(); // eslint-disable-line no-bitwise
const now = new Date();

if (x.type === 'directory') {
Expand All @@ -88,26 +87,23 @@ const extractFile = (input, output, opts) => runPlugins(input, opts).then(files

return makeDir(output)
.then(outputPath => fsP.realpath(outputPath))
.then(realOutputPath => {
.then(realOutputPath =>
// Attempt to ensure parent directory exists (failing if it's outside the output dir)
return safeMakeDir(path.dirname(dest), realOutputPath)
.then(() => realOutputPath);
})
safeMakeDir(path.dirname(dest), realOutputPath).then(() => realOutputPath),
)
.then(realOutputPath => {
if (x.type === 'file') {
return preventWritingThroughSymlink(dest, realOutputPath);
}

return realOutputPath;
})
.then(realOutputPath => {
return fsP.realpath(path.dirname(dest))
.then(realDestinationDir => {
if (realDestinationDir.indexOf(realOutputPath) !== 0) {
throw (new Error('Refusing to write outside output directory: ' + realDestinationDir));
}
});
})
.then(realOutputPath => fsP.realpath(path.dirname(dest))
.then(realDestinationDir => {
if (realDestinationDir.indexOf(realOutputPath) !== 0) {
throw new Error(`Refusing to write outside output directory: ${realDestinationDir}`);
}
}))
.then(() => {
if (x.type === 'link') {
return fsP.link(x.linkname, dest);
Expand All @@ -128,24 +124,29 @@ const extractFile = (input, output, opts) => runPlugins(input, opts).then(files
}));
});

module.exports = (input, output, opts) => {
const decompress = (input, output, options) => {
if (typeof input !== 'string' && !Buffer.isBuffer(input)) {
return Promise.reject(new TypeError('Input file required'));
}

if (typeof output === 'object') {
opts = output;
options = output;
output = null;
}

opts = Object.assign({plugins: [
decompressTar(),
decompressTarbz2(),
decompressTargz(),
decompressUnzip()
]}, opts);
options = {
plugins: [
decompressTar(),
decompressTarbz2(),
decompressTargz(),
decompressUnzip(),
],
...options,
};

const read = typeof input === 'string' ? fsP.readFile(input) : Promise.resolve(input);

return read.then(buf => extractFile(buf, output, opts));
return read.then(buf => extractFile(buf, output, options));
};

export default decompress;
47 changes: 22 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@
"url": "github.com/kevva"
},
"engines": {
"node": ">=4"
"node": "^12.20.0 || ^14.14.0 || >=16.0.0"
},
"scripts": {
"test": "xo && ava"
"ava": "ava",
"xo": "xo",
"test": "npm run xo && npm run ava",
"test-ci": "npm run xo && c8 ava"
},
"main": "index.js",
"type": "module",
"exports": {
".": "./index.js"
},
"files": [
"index.js"
Expand All @@ -30,32 +38,21 @@
"unzip"
],
"dependencies": {
"decompress-tar": "^4.0.0",
"decompress-tarbz2": "^4.0.0",
"decompress-targz": "^4.0.0",
"decompress-tar": "^4.1.1",
"decompress-tarbz2": "^4.1.1",
"decompress-targz": "^4.1.1",
"decompress-unzip": "^4.0.1",
"graceful-fs": "^4.1.10",
"make-dir": "^1.0.0",
"pify": "^2.3.0",
"strip-dirs": "^2.0.0"
"graceful-fs": "^4.2.10",
"make-dir": "^3.1.0",
"pify": "^5.0.0",
"strip-dirs": "^3.0.0"
},
"devDependencies": {
"ava": "*",
"esm": "^3.2.25",
"is-jpg": "^1.0.0",
"path-exists": "^3.0.0",
"pify": "^2.3.0",
"ava": "^4.3.0",
"c8": "^7.11.3",
"is-jpg": "^3.0.0",
"path-exists": "^5.0.0",
"rimraf": "^3.0.2",
"xo": "*"
},
"ava": {
"require": [
"esm"
]
},
"xo": {
"rules": {
"promise/prefer-await-to-then": "off"
}
"xo": "^0.49.0"
}
}
16 changes: 8 additions & 8 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# decompress [![Build Status](https://travis-ci.org/kevva/decompress.svg?branch=master)](https://travis-ci.org/kevva/decompress)
# decompress [![CI](https://github.com/kevva/decompress/actions/workflows/ci.yml/badge.svg)](https://github.com/kevva/decompress/actions/workflows/ci.yml)

> Extracting archives made easy
*See [decompress-cli](https://github.com/kevva/decompress-cli) for the command-line version.*

## Install

```
$ npm install decompress
```sh
npm install decompress
```


## Usage

```js
const decompress = require('decompress');
import decompress from 'decompress';

decompress('unicorn.zip', 'dist').then(files => {
console.log('done!');
Expand Down Expand Up @@ -87,15 +87,15 @@ decompress('unicorn.zip', 'dist', {

##### plugins

Type: `Array`<br>
Default: `[decompressTar(), decompressTarbz2(), decompressTargz(), decompressUnzip()]`
* Type: `Array`
* Default: `[decompressTar(), decompressTarbz2(), decompressTargz(), decompressUnzip()]`

Array of [plugins](https://www.npmjs.com/browse/keyword/decompressplugin) to use.

##### strip

Type: `number`<br>
Default: `0`
* Type: `number`
* Default: `0`

Remove leading directory components from extracted files.

Expand Down