feat(recipe): validate heroImageUrl content type before persisting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 09:08:45 +02:00
parent b1eb9ed964
commit 90cff0c4d2
2 changed files with 42 additions and 0 deletions

View File

@@ -2,6 +2,7 @@ package com.recipeapp.recipe;
import com.recipeapp.common.ConflictException;
import com.recipeapp.common.ResourceNotFoundException;
import com.recipeapp.common.ValidationException;
import com.recipeapp.household.HouseholdRepository;
import com.recipeapp.household.entity.Household;
import com.recipeapp.recipe.dto.*;
@@ -60,6 +61,8 @@ public class RecipeService {
Household household = householdRepository.findById(householdId)
.orElseThrow(() -> new ResourceNotFoundException("Household not found"));
validateHeroImageUrl(request.heroImageUrl());
Recipe recipe = new Recipe(household, request.name(),
request.serves() != null ? request.serves().shortValue() : 0,
request.cookTimeMin() != null ? request.cookTimeMin().shortValue() : 0,
@@ -80,6 +83,8 @@ public class RecipeService {
Recipe recipe = findRecipe(householdId, recipeId);
Household household = recipe.getHousehold();
validateHeroImageUrl(request.heroImageUrl());
recipe.setName(request.name());
recipe.setServes(request.serves() != null ? request.serves().shortValue() : 0);
recipe.setCookTimeMin(request.cookTimeMin() != null ? request.cookTimeMin().shortValue() : 0);
@@ -183,6 +188,18 @@ public class RecipeService {
return new IngredientCategoryResponse(category.getId(), category.getName());
}
// ── Image validation ──
private static final java.util.regex.Pattern ALLOWED_IMAGE_PATTERN =
java.util.regex.Pattern.compile("^data:image/(jpeg|jpg|png|gif|webp);base64,");
private void validateHeroImageUrl(String heroImageUrl) {
if (heroImageUrl == null || heroImageUrl.isBlank()) return;
if (!ALLOWED_IMAGE_PATTERN.matcher(heroImageUrl).find()) {
throw new ValidationException("Ungültiger Bildtyp. Erlaubt sind: JPEG, PNG, GIF, WebP.");
}
}
// ── Private helpers ──
private Recipe findRecipe(UUID householdId, UUID recipeId) {