Skip to content

Commit da671d6

Browse files
committed
Security: Ensure SVG files are sanitized and properly served download
See advisory GHSA-273p-jw9w-3g22
1 parent 7c4965e commit da671d6

1 file changed

Lines changed: 22 additions & 22 deletions

File tree

src/CoreBundle/Controller/ResourceController.php

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,28 @@ private function processFile(Request $request, ResourceNode $resourceNode, Resou
602602
// This covers files uploaded before the MIME-type allowlist was introduced.
603603
$isSocialAttachment = 'social_post_attachments' === (string) $request->attributes->get('type');
604604

605+
// SVG: sanitize before serving in any mode (view or download).
606+
// Glide is raster-only and cannot process SVG; sanitization strips embedded scripts regardless of how the file was stored.
607+
if ('image/svg+xml' === $mimeType) {
608+
$raw = $resourceNodeRepo->getResourceNodeFileContent($resourceNode, $resourceFile);
609+
$content = (new SvgSanitizer())->sanitize((string) $raw);
610+
611+
if (false === $content || '' === $content) {
612+
throw new BadRequestHttpException('Invalid SVG file');
613+
}
614+
615+
$response = new Response($content);
616+
$dispositionMode = 'download' === $mode
617+
? ResponseHeaderBag::DISPOSITION_ATTACHMENT
618+
: ResponseHeaderBag::DISPOSITION_INLINE;
619+
$disposition = $response->headers->makeDisposition($dispositionMode, $fileName);
620+
$response->headers->set('Content-Disposition', $disposition);
621+
$response->headers->set('Content-Type', 'image/svg+xml');
622+
$response->headers->set('X-Content-Type-Options', 'nosniff');
623+
624+
return $response;
625+
}
626+
605627
switch ($mode) {
606628
case 'download':
607629
$forceDownload = true;
@@ -612,28 +634,6 @@ private function processFile(Request $request, ResourceNode $resourceNode, Resou
612634
default:
613635
$forceDownload = false;
614636

615-
// SVG must not go through Glide (raster-only library); serve directly after sanitization.
616-
// Sanitizing at serve time ensures scripts are stripped regardless of how the file was stored.
617-
if ('image/svg+xml' === $mimeType) {
618-
$raw = $resourceNodeRepo->getResourceNodeFileContent($resourceNode, $resourceFile);
619-
$content = (new SvgSanitizer())->sanitize($raw);
620-
621-
if (false === $content || '' === $content) {
622-
throw new BadRequestHttpException('Invalid SVG file');
623-
}
624-
625-
$response = new Response($content);
626-
$disposition = $response->headers->makeDisposition(
627-
ResponseHeaderBag::DISPOSITION_INLINE,
628-
$fileName
629-
);
630-
$response->headers->set('Content-Disposition', $disposition);
631-
$response->headers->set('Content-Type', 'image/svg+xml');
632-
$response->headers->set('X-Content-Type-Options', 'nosniff');
633-
634-
return $response;
635-
}
636-
637637
// If it's an image then send it to Glide.
638638
if (str_contains($mimeType, 'image')) {
639639
$glide = $this->getGlide();

0 commit comments

Comments
 (0)