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,
+ );
+ });
}