From a356b94c34506ee93e9e7b6c5721bea47fb979ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Wed, 8 Jul 2026 23:30:16 +0200 Subject: [PATCH] Respect the attachment max file size limit for attachment file downloads --- .../Attachments/AttachmentSubmitHandler.php | 24 +++++++++- .../SystemSettings/AttachmentsSettings.php | 14 ++++++ .../AttachmentsSettingsTest.php | 47 +++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tests/Settings/SystemSettings/AttachmentsSettingsTest.php diff --git a/src/Services/Attachments/AttachmentSubmitHandler.php b/src/Services/Attachments/AttachmentSubmitHandler.php index 1b90091f..2bc18c6e 100644 --- a/src/Services/Attachments/AttachmentSubmitHandler.php +++ b/src/Services/Attachments/AttachmentSubmitHandler.php @@ -78,6 +78,7 @@ class AttachmentSubmitHandler protected FileTypeFilterTools $filterTools, protected AttachmentsSettings $settings, protected readonly SVGSanitizer $SVGSanitizer, + private readonly AttachmentsSettings $attachmentsSettings, #[Autowire(env: "bool:ALLOW_ATTACHMENT_DOWNLOADS_FROM_LOCALNETWORK")] private readonly bool $allow_local_network_downloads = false, ) @@ -399,9 +400,30 @@ class AttachmentSubmitHandler //Open a temporary file in the attachment folder $fs->mkdir($attachment_folder); $fileHandler = fopen($tmp_path, 'wb'); + + $bytesDownloaded = 0; + $maxSize = $this->attachmentsSettings->getMaxFileSizeInMegabytes() * 1024 * 1024; //Convert to bytes + //Write the downloaded data to file foreach ($this->httpClient->stream($response) as $chunk) { - fwrite($fileHandler, $chunk->getContent()); + $content = $chunk->getContent(); + $bytesDownloaded += strlen($content); + + //Ensure the size does not get too large to avoid filling up the disk easily. + //If the file is too big, cancel the download and delete the temporary file. + if ($bytesDownloaded > $maxSize) { + $response->cancel(); + fclose($fileHandler); + unlink($tmp_path); //Delete the temporary file, because it is too big + + throw new AttachmentDownloadException( + sprintf( + 'The downloaded file is too big! Maximum size is %d MB!', + $this->settings->getMaxFileSizeInMegabytes() + )); + } + + fwrite($fileHandler, $content); } fclose($fileHandler); diff --git a/src/Settings/SystemSettings/AttachmentsSettings.php b/src/Settings/SystemSettings/AttachmentsSettings.php index 5aa3f91d..235b7b85 100644 --- a/src/Settings/SystemSettings/AttachmentsSettings.php +++ b/src/Settings/SystemSettings/AttachmentsSettings.php @@ -65,4 +65,18 @@ class AttachmentsSettings envVar: "bool:ATTACHMENT_SHOW_HTML_FILES", envVarMode: EnvVarMode::OVERWRITE )] public bool $showHTMLAttachments = false; + + public function getMaxFileSizeInMegabytes(): int + { + $size = $this->maxFileSize; + $unit = strtoupper(substr($size, -1)); + $value = (int) rtrim($size, 'KMG'); + + return match ($unit) { + 'K' => (int) ceil($value / 1024), + 'M' => $value, + 'G' => $value * 1024, + default => $value, // No unit specified, assume megabytes + }; + } } diff --git a/tests/Settings/SystemSettings/AttachmentsSettingsTest.php b/tests/Settings/SystemSettings/AttachmentsSettingsTest.php new file mode 100644 index 00000000..16acc13c --- /dev/null +++ b/tests/Settings/SystemSettings/AttachmentsSettingsTest.php @@ -0,0 +1,47 @@ +. + */ + +namespace App\Tests\Settings\SystemSettings; + +use App\Settings\SystemSettings\AttachmentsSettings; +use App\Tests\SettingsTestHelper; +use PHPUnit\Framework\TestCase; + +class AttachmentsSettingsTest extends TestCase +{ + + public function testGetMaxFileSizeInMegabytes(): void + { + $settings = SettingsTestHelper::createSettingsDummy(AttachmentsSettings::class); + + $settings->maxFileSize = '100M'; + $this->assertEquals(100, $settings->getMaxFileSizeInMegabytes()); + + $settings->maxFileSize = '1G'; + $this->assertEquals(1024, $settings->getMaxFileSizeInMegabytes()); + + //We round up to the next megabyte if the file size is smaller than 1 MB + $settings->maxFileSize = '500K'; + $this->assertEquals(1, $settings->getMaxFileSizeInMegabytes()); + + $settings->maxFileSize = '13.5M'; + $this->assertEquals(13, $settings->getMaxFileSizeInMegabytes()); + } +}