diff --git a/app/Http/Controllers/Merchant/StorefrontController.php b/app/Http/Controllers/Merchant/StorefrontController.php index 7e3c373..d20d260 100644 --- a/app/Http/Controllers/Merchant/StorefrontController.php +++ b/app/Http/Controllers/Merchant/StorefrontController.php @@ -64,7 +64,7 @@ class StorefrontController extends Controller 'menu_cover' => ['nullable', 'image', 'mimes:jpeg,jpg,png,gif,webp', 'max:8192'], 'item_images' => ['nullable', 'array'], 'item_images.*' => ['array'], - 'item_images.*.*' => ['image', 'max:4096'], + 'item_images.*.*' => ['mimes:jpeg,jpg,png,gif,webp', 'max:4096'], ]); $data = array_merge($request->all(), [ @@ -103,7 +103,7 @@ class StorefrontController extends Controller 'menu_cover' => ['nullable', 'image', 'mimes:jpeg,jpg,png,gif,webp', 'max:8192'], 'item_images' => ['nullable', 'array'], 'item_images.*' => ['array'], - 'item_images.*.*' => ['image', 'max:4096'], + 'item_images.*.*' => ['mimes:jpeg,jpg,png,gif,webp', 'max:4096'], ]); $data = array_merge($request->all(), [ diff --git a/app/Services/Qr/QrCodeManagerService.php b/app/Services/Qr/QrCodeManagerService.php index 50ce0e7..984f3c5 100644 --- a/app/Services/Qr/QrCodeManagerService.php +++ b/app/Services/Qr/QrCodeManagerService.php @@ -330,7 +330,7 @@ class QrCodeManagerService continue; } $mime = $file->getMimeType() ?: ''; - if (! str_starts_with($mime, 'image/')) { + if (! str_starts_with($mime, 'image/') || $this->isSvgUpload($file)) { continue; } $subdir = $type === QrCode::TYPE_SHOP ? 'shop-items' : 'menu-items'; @@ -472,11 +472,23 @@ class QrCodeManagerService return ['path' => $path, 'type' => $type, 'size' => (int) $file->getSize()]; } + /** + * SVG is rejected for customer uploads: it can embed JavaScript and would + * execute as stored XSS when served inline on a public storefront page. + */ + private function isSvgUpload(UploadedFile $file): bool + { + $mime = strtolower($file->getMimeType() ?: ''); + $ext = strtolower($file->getClientOriginalExtension() ?: ''); + + return $mime === 'image/svg+xml' || $mime === 'image/svg' || $ext === 'svg' || $ext === 'svgz'; + } + private function storeMenuBrandImage(User $user, UploadedFile $file, string $subdir): string { $mime = $file->getMimeType() ?: ''; - if (! str_starts_with($mime, 'image/')) { - throw new RuntimeException('Only image files are supported.'); + if (! str_starts_with($mime, 'image/') || $this->isSvgUpload($file)) { + throw new RuntimeException('Logos and images must be PNG, JPG, GIF or WebP (SVG is not allowed).'); } $ext = $file->getClientOriginalExtension() ?: 'jpg'; diff --git a/resources/views/merchant/storefronts/partials/editor-fields.blade.php b/resources/views/merchant/storefronts/partials/editor-fields.blade.php index db8b4eb..17053e7 100644 --- a/resources/views/merchant/storefronts/partials/editor-fields.blade.php +++ b/resources/views/merchant/storefronts/partials/editor-fields.blade.php @@ -92,11 +92,11 @@
- +
- +
@@ -116,12 +116,12 @@
-
-
@@ -166,7 +166,7 @@ class="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2 text-sm">
-
@@ -199,12 +199,12 @@
-
-
@@ -256,7 +256,7 @@ class="mt-2 w-full rounded-lg border border-slate-200 px-3 py-2 text-sm">
-