diff --git a/fixtures/slip2.zip b/fixtures/slip2.zip new file mode 100644 index 0000000..bd6180b Binary files /dev/null and b/fixtures/slip2.zip differ diff --git a/index.js b/index.js index c4ff7dc..f4671b1 100644 --- a/index.js +++ b/index.js @@ -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)); } }); diff --git a/test.js b/test.js index 33dd3a3..b38bf62 100644 --- a/test.js +++ b/test.js @@ -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')); }); @@ -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'))); }); @@ -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(), [