Fail gracefully when tar file contains duplicate entries (#3805)
diff --git a/lib/src/io.dart b/lib/src/io.dart index 54736c9..4a09496 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart
@@ -983,6 +983,7 @@ destination = path.absolute(destination); final reader = TarReader(stream.transform(gzip.decoder)); + final paths = <String>{}; while (await reader.moveNext()) { final entry = reader.current; @@ -991,6 +992,11 @@ // Tar file names always use forward slashes ...path.posix.split(entry.name), ]); + if (!paths.add(filePath)) { + // The tar file contained the same entry twice. Assume it is broken. + await reader.cancel(); + throw FormatException('Tar file contained duplicate path ${entry.name}'); + } if (!path.isWithin(destination, filePath)) { // The tar contains entries that would be written outside of the
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart index 64c5936..d2928ae 100644 --- a/lib/src/source/hosted.dart +++ b/lib/src/source/hosted.dart
@@ -1161,8 +1161,11 @@ var tempDir = cache.createTempDir(); try { - await extractTarGz(readBinaryFileAsStream(archivePath), tempDir); - + try { + await extractTarGz(readBinaryFileAsStream(archivePath), tempDir); + } on FormatException catch (e) { + dataError('Failed to extract `$archivePath`: ${e.message}.'); + } ensureDir(p.dirname(destPath)); } catch (e) { deleteEntry(tempDir);
diff --git a/test/get/hosted/get_test.dart b/test/get/hosted/get_test.dart index 73434be..692b605 100644 --- a/test/get/hosted/get_test.dart +++ b/test/get/hosted/get_test.dart
@@ -4,6 +4,7 @@ import 'package:path/path.dart' as p; import 'package:pub/src/exit_codes.dart' as exit_codes; +import 'package:pub/src/exit_codes.dart'; import 'package:pub/src/io.dart'; import 'package:test/test.dart'; import 'package:yaml/yaml.dart'; @@ -388,4 +389,21 @@ ); }); }); + + test('Fails gracefully on tar.gz with duplicate entries', () async { + final server = await servePackages(); + server.serve( + 'foo', + '1.0.0', + contents: [ + d.dir('blah', [d.file('myduplicatefile'), d.file('myduplicatefile')]) + ], + ); + await d.appDir(dependencies: {'foo': 'any'}).create(); + await pubGet( + error: + contains('Tar file contained duplicate path blah/myduplicatefile.'), + exitCode: DATA, + ); + }); }