diff --git a/app/Controllers/MediaController.php b/app/Controllers/MediaController.php index ed012dd2..b448bfae 100644 --- a/app/Controllers/MediaController.php +++ b/app/Controllers/MediaController.php @@ -41,12 +41,15 @@ public function show(Request $request, Response $response, string $userCode, str $filesystem = $this->storage; - if (isBot($request->getHeaderLine('User-Agent'))) { + $userAgent = $request->getHeaderLine('User-Agent'); + $mime = $filesystem->getMimetype($media->storage_path); + + if (isBot($userAgent) && (!isDiscord($userAgent) || (isDiscord($userAgent) && !isDisplayableImage($mime)))) { return $this->streamMedia($request, $response, $filesystem, $media); } try { - $media->mimetype = $filesystem->getMimetype($media->storage_path); + $media->mimetype = $mime; $size = $filesystem->getSize($media->storage_path); $type = explode('/', $media->mimetype)[0]; @@ -114,7 +117,7 @@ public function getRaw(Request $request, Response $response, string $userCode, s { $media = $this->getMedia($userCode, $mediaCode, false); - if (!$media || !$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false)) { + if (!$media || (!$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false))) { throw new HttpNotFoundException($request); } @@ -122,8 +125,7 @@ public function getRaw(Request $request, Response $response, string $userCode, s throw new HttpBadRequestException($request); } - // If contains html, return it as text/plain - if (strpos($this->storage->getMimetype($media->storage_path), 'text/htm') !== false) { + if (must_be_escaped($this->storage->getMimetype($media->storage_path))) { $response = $this->streamMedia($request, $response, $this->storage, $media); return $response->withHeader('Content-Type', 'text/plain'); } @@ -146,7 +148,7 @@ public function download(Request $request, Response $response, string $userCode, { $media = $this->getMedia($userCode, $mediaCode, false); - if (!$media || !$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false)) { + if (!$media || (!$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false))) { throw new HttpNotFoundException($request); } diff --git a/app/Web/Session.php b/app/Web/Session.php index b96632ca..60b853c7 100644 --- a/app/Web/Session.php +++ b/app/Web/Session.php @@ -26,7 +26,7 @@ public function __construct(string $name, $path = '') $params = session_get_cookie_params(); session_set_cookie_params( $params['lifetime'], - $params['path'].'; SameSite=Lax', + $params['path'].'; SameSite=Strict', $params['domain'], $params['secure'], $params['httponly'] @@ -38,7 +38,7 @@ public function __construct(string $name, $path = '') 'save_path' => $path, 'cookie_httponly' => true, 'gc_probability' => 25, - 'cookie_samesite' => 'Lax', // works only for php >= 7.3 + 'cookie_samesite' => 'Strict', // works only for php >= 7.3 ]); if (!$started) { diff --git a/app/helpers.php b/app/helpers.php index 01c4115a..aa56eb95 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -67,8 +67,6 @@ function isDisplayableImage(?string $mime): bool 'image/x-icon', 'image/jpeg', 'image/png', - 'image/svg', - 'image/svg+xml', 'image/tiff', 'image/webp', ]); @@ -345,6 +343,29 @@ function isBot(string $userAgent) } } +if (!function_exists('isDiscord')) { + /** + * @param string $userAgent + * + * @return bool + */ + function isDiscord(string $userAgent): bool + { + $bots = [ + 'discord', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0' // discord image bot + ]; + + foreach ($bots as $bot) { + if (stripos($userAgent, $bot) !== false) { + return true; + } + } + + return false; + } +} + if (!function_exists('mime2font')) { /** * Convert get the icon from the file mimetype. @@ -495,3 +516,27 @@ function platform_mail($mailbox = 'no-reply'): string return $mailbox.'@'.str_ireplace('www.', '', parse_url(resolve('config')['base_url'], PHP_URL_HOST)); } } + +if (!function_exists('must_be_escaped')) { + /** + * Return the system no-reply mail. + * + * @param $mime + * @return bool + */ + function must_be_escaped($mime): bool + { + $mimes = [ + 'text/htm', + 'image/svg' + ]; + + foreach ($mimes as $m) { + if (stripos($mime, $m) !== false) { + return true; + } + } + + return false; + } +}