From 87ff3d5d1409fda31603ec7fc99695e11be0d2e8 Mon Sep 17 00:00:00 2001 From: gilesb Date: Mon, 12 Jan 2026 15:33:39 +0000 Subject: [PATCH] Remove owner filtering from recipe listing (security fix) The owner field from recipe content could be spoofed to hide recipes from users or make recipes appear to belong to someone else. For L1, all recipes in cache are now visible to authenticated users. Ownership tracking should use the naming service or cache metadata, not untrusted data from recipe content. Co-Authored-By: Claude Opus 4.5 --- app/services/recipe_service.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/services/recipe_service.py b/app/services/recipe_service.py index 5dc6333..1b13f4b 100644 --- a/app/services/recipe_service.py +++ b/app/services/recipe_service.py @@ -51,6 +51,9 @@ class RecipeService: return stripped.startswith('(') return False + import logging + logger = logging.getLogger(__name__) + if is_sexp_format(content): # Parse S-expression try: @@ -58,7 +61,9 @@ class RecipeService: recipe_data = compiled.to_dict() recipe_data["sexp"] = content recipe_data["format"] = "sexp" + logger.info(f"Parsed sexp recipe {recipe_id[:16]}..., keys: {list(recipe_data.keys())}") except (ParseError, CompileError) as e: + logger.warning(f"Failed to parse sexp recipe {recipe_id[:16]}...: {e}") return {"error": str(e), "recipe_id": recipe_id} else: # Parse YAML @@ -107,15 +112,11 @@ class RecipeService: logger.debug(f"Attempting to get recipe {cid[:16]}...") recipe = await self.get_recipe(cid) if recipe and not recipe.get("error"): - owner = recipe.get("owner") - # Filter by actor - L1 is per-user - # Note: S-expression recipes don't have owner field, so owner=None - # means the recipe is shared/public and visible to all users - if actor_id is None or owner is None or owner == actor_id: - recipes.append(recipe) - logger.info(f"Recipe {cid[:16]}... included (owner={owner}, actor={actor_id})") - else: - logger.info(f"Recipe {cid[:16]}... filtered out (owner={owner}, actor={actor_id})") + # Don't trust owner from recipe content - could be spoofed + # For L1, recipes in cache are visible to all authenticated users + # (ownership is tracked via naming service, not recipe content) + recipes.append(recipe) + logger.info(f"Recipe {cid[:16]}... included") elif recipe and recipe.get("error"): logger.warning(f"Recipe {cid[:16]}... has error: {recipe.get('error')}") else: