From 56e6143fd2c735ed33febd969440fd1586d3a19e Mon Sep 17 00:00:00 2001 From: Marcel Raddatz Date: Fri, 10 Apr 2026 09:33:39 +0200 Subject: [PATCH] feat(recipes): validate image MIME type on file select Rejects non-allowlisted types (only JPEG, PNG, GIF, WebP accepted) with an inline error message. Uses image/bmp as test vector since it passes accept="image/*" but is not in the allowed set. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/lib/recipes/RecipeForm.svelte | 6 ++++++ frontend/src/lib/recipes/RecipeForm.test.ts | 22 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/frontend/src/lib/recipes/RecipeForm.svelte b/frontend/src/lib/recipes/RecipeForm.svelte index 7d7242c..c9ca6e8 100644 --- a/frontend/src/lib/recipes/RecipeForm.svelte +++ b/frontend/src/lib/recipes/RecipeForm.svelte @@ -64,6 +64,7 @@ let imageError = $state(null); const MAX_IMAGE_BYTES = 5 * 1024 * 1024; + const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp']; function handleImageChange(e: Event) { const file = (e.currentTarget as HTMLInputElement).files?.[0]; @@ -73,6 +74,11 @@ (e.currentTarget as HTMLInputElement).value = ''; return; } + if (!ALLOWED_MIME_TYPES.includes(file.type)) { + imageError = 'Dateityp nicht unterstützt. Erlaubt: JPEG, PNG, GIF, WebP.'; + (e.currentTarget as HTMLInputElement).value = ''; + return; + } imageError = null; const reader = new FileReader(); reader.onload = () => { diff --git a/frontend/src/lib/recipes/RecipeForm.test.ts b/frontend/src/lib/recipes/RecipeForm.test.ts index b03aa7a..ffa8374 100644 --- a/frontend/src/lib/recipes/RecipeForm.test.ts +++ b/frontend/src/lib/recipes/RecipeForm.test.ts @@ -189,4 +189,26 @@ describe('RecipeForm', () => { expect(screen.queryByText(/datei zu groß/i)).not.toBeInTheDocument(); }); + + it('shows error when selected file has unsupported type', async () => { + const user = userEvent.setup(); + render(RecipeForm, { props: emptyProps }); + + const bmpFile = new File(['content'], 'image.bmp', { type: 'image/bmp' }); + const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement; + await user.upload(fileInput, bmpFile); + + expect(screen.getByText(/dateityp/i)).toBeInTheDocument(); + }); + + it('does not show type error for supported image types', async () => { + const user = userEvent.setup(); + render(RecipeForm, { props: emptyProps }); + + const jpgFile = new File(['content'], 'photo.jpg', { type: 'image/jpeg' }); + const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement; + await user.upload(fileInput, jpgFile); + + expect(screen.queryByText(/dateityp/i)).not.toBeInTheDocument(); + }); });