diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php
index cbd7d0452..5b6571dc1 100644
--- a/src/Composer/Downloader/ZipDownloader.php
+++ b/src/Composer/Downloader/ZipDownloader.php
@@ -144,6 +144,10 @@ class ZipDownloader extends ArchiveDownloader
throw $processError;
}
+ if (str_contains($processError->getMessage(), 'zip bomb')) {
+ throw $processError;
+ }
+
if (!is_file($file)) {
$io->writeError(' '.$processError->getMessage().'');
$io->writeError(' This most likely is due to a custom installer plugin not handling the returned Promise from the downloader');
@@ -208,6 +212,26 @@ class ZipDownloader extends ArchiveDownloader
} else {
$retval = $zipArchive->open($file);
}
+
+ $totalSize = 0;
+ $archiveSize = filesize($file);
+ $totalFiles = $zipArchive->count();
+ if ($totalFiles > 0) {
+ for ($i = 0; $i < min($totalFiles, 5); $i++) {
+ $stat = $zipArchive->statIndex(random_int(0, $totalFiles - 1));
+ if ($stat === false) {
+ continue;
+ }
+ $totalSize += $stat['size'];
+ if ($stat['size'] > $stat['comp_size'] * 200) {
+ throw new \RuntimeException('Invalid zip file with compression ratio >99% (possible zip bomb)');
+ }
+ }
+ if ($archiveSize !== false && $totalSize > $archiveSize * 100 && $totalSize > 50*1024*1024) {
+ throw new \RuntimeException('Invalid zip file with compression ratio >99% (possible zip bomb)');
+ }
+ }
+
if (true === $retval) {
$extractResult = $zipArchive->extractTo($path);