Skip to content

Commit

Permalink
Prevent another symlink escape case
Browse files Browse the repository at this point in the history
  • Loading branch information
trptcolin committed Feb 28, 2020
1 parent 2d797ce commit 30ff4d5
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 9 deletions.
Binary file added fixtures/slip2.zip
Binary file not shown.
2 changes: 1 addition & 1 deletion index.js
Expand Up @@ -75,7 +75,7 @@ const extractFile = (input, output, opts) => runPlugins(input, opts).then(files
.then(() => {
return Promise.all([fsP.realpath(path.dirname(dest)), fsP.realpath(output)])
.then(([realDestinationDir, realOutputDir]) => {
if (realDestinationDir.indexOf(realOutputDir) !== 0) {
if (dest.indexOf(realOutputDir) !== 0 || realDestinationDir.indexOf(realOutputDir) !== 0) {
throw (new Error('Refusing to write outside output directory: ' + realDestinationDir));
}
});
Expand Down
22 changes: 14 additions & 8 deletions test.js
Expand Up @@ -10,10 +10,12 @@ import m from '.';
const fsP = pify(fs);
const rimrafP = pify(rimraf);

test.after('ensure decompressed files and directories are cleaned up', async () => {
test.serial.afterEach('ensure decompressed files and directories are cleaned up', async () => {
await rimrafP(path.join(__dirname, 'directory'));
await rimrafP(path.join(__dirname, 'dist'));
await rimrafP(path.join(__dirname, 'file.txt'));
await rimrafP(path.join(__dirname, 'edge_case_dots'));
await rimrafP(path.join(__dirname, 'symlink'));
await rimrafP(path.join(__dirname, 'test.jpg'));
});

Expand Down Expand Up @@ -57,14 +59,12 @@ test.serial('extract file to directory', async t => {
t.true(await pathExists(path.join(__dirname, 'test.jpg')));
});

test('extract symlink', async t => {
test.serial('extract symlink', async t => {
await m(path.join(__dirname, 'fixtures', 'symlink.tar'), __dirname, {strip: 1});
t.is(await fsP.realpath(path.join(__dirname, 'symlink')), path.join(__dirname, 'file.txt'));
await fsP.unlink(path.join(__dirname, 'symlink'));
await fsP.unlink(path.join(__dirname, 'file.txt'));
});

test('extract directory', async t => {
test.serial('extract directory', async t => {
await m(path.join(__dirname, 'fixtures', 'directory.tar'), __dirname);
t.true(await pathExists(path.join(__dirname, 'directory')));
});
Expand Down Expand Up @@ -115,19 +115,25 @@ test('throw when a location outside the root is given', async t => {
}, {message: /Refusing/});
});

test('throw when a location outside the root including symlinks is given', async t => {
test.serial('throw when a location outside the root including symlinks is given', async t => {
await t.throwsAsync(async () => {
await m(path.join(__dirname, 'fixtures', 'slip.zip'), 'dist');
}, {message: /Refusing/});
});

test('throw when a directory outside the root including symlinks is given', async t => {
test.serial('throw when a top-level symlink outside the root is given', async t => {
await t.throwsAsync(async () => {
await m(path.join(__dirname, 'fixtures', 'slip2.zip'), 'dist');
}, {message: /Refusing/});
});

test.serial('throw when a directory outside the root including symlinks is given', async t => {
await t.throwsAsync(async () => {
await m(path.join(__dirname, 'fixtures', 'slipping_directory.tar.gz'), 'dist');
}, {message: /Refusing/});
});

test('allows filenames and directories to be written with dots in their names', async t => {
test.serial('allows filenames and directories to be written with dots in their names', async t => {
const files = await m(path.join(__dirname, 'fixtures', 'edge_case_dots.tar.gz'), __dirname);
t.is(files.length, 6);
t.deepEqual(files.map(f => f.path).sort(), [
Expand Down

0 comments on commit 30ff4d5

Please sign in to comment.