[CORE][GSFile] Respect mimetype whitelist and extensions blacklist before saving files

This commit is contained in:
Diogo Peralta Cordeiro 2021-10-24 15:16:37 +01:00
parent b7d9da8ae6
commit bccafd0d7b
Signed by: diogo
GPG Key ID: 18D2D35001FBFAB0
2 changed files with 64 additions and 4 deletions

View File

@ -24,6 +24,7 @@ declare(strict_types = 1);
namespace App\Core; namespace App\Core;
use App\Core\DB\DB; use App\Core\DB\DB;
use App\Util\Exception\FileNotAllowedException;
use function App\Core\I18n\_m; use function App\Core\I18n\_m;
use App\Entity\Attachment; use App\Entity\Attachment;
use App\Util\Common; use App\Util\Common;
@ -92,7 +93,12 @@ class GSFile
$attachment->setWidth($width); $attachment->setWidth($width);
$attachment->setHeight($height); $attachment->setHeight($height);
$attachment->setSize($file->getSize()); $attachment->setSize($file->getSize());
if (self::isMimetypeAllowed($mimetype)) {
$file->move(Common::config('attachments', 'dir'), $hash); $file->move(Common::config('attachments', 'dir'), $hash);
DB::persist($attachment);
} else {
throw new FileNotAllowedException();
}
} }
} catch (NotFoundException) { } catch (NotFoundException) {
// Create an Attachment // Create an Attachment
@ -123,13 +129,37 @@ class GSFile
'width' => $width, 'width' => $width,
'height' => $height, 'height' => $height,
]); ]);
if (self::isMimetypeAllowed($mimetype)) {
$file->move(Common::config('attachments', 'dir'), $hash); $file->move(Common::config('attachments', 'dir'), $hash);
DB::persist($attachment); DB::persist($attachment);
} else {
$attachment->setFilename(null);
$attachment->setMimetype(null);
$attachment->setSize(null);
$attachment->setWidth(null);
$attachment->setHeight(null);
DB::persist($attachment);
throw new FileNotAllowedException($mimetype);
}
Event::handle('AttachmentStoreNew', [&$attachment]); Event::handle('AttachmentStoreNew', [&$attachment]);
} }
return $attachment; return $attachment;
} }
/**
* Tests against common config attachment `supported` mimetypes and `ext_blacklist`.
*
* @param string $mimetype
* @return bool true if allowed, false otherwise
*/
public static function isMimetypeAllowed(string $mimetype): bool {
$passed_whitelist = in_array($mimetype, array_keys(Common::config('attachments', 'supported')));
$mime = new MimeTypes();
$passed_blacklist = count(array_intersect($mime->getExtensions($mimetype), Common::config('attachments', 'ext_blacklist'))) === 0;
unset($mime);
return $passed_whitelist && $passed_blacklist;
}
/** /**
* Include $filepath in the response, for viewing or downloading. * Include $filepath in the response, for viewing or downloading.
* *

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types = 1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
namespace App\Util\Exception;
class FileNotAllowedException extends \InvalidArgumentException
{
public function __construct(string $mimetype)
{
parent::__construct("File mimetype not allowed: '{$mimetype}'.");
}
}