Issue
In internal/router/router.go (lines 53–57), the single-recipe endpoint is registered under the unauthenticated public group:
apiPublic := r.Group("/v1")
{
// ...
apiPublic.GET("/recipes/:recipe_id", recipeHandler.GetRecipe)
}
Any request — authenticated or not — can fetch any recipe by its integer ID. Because recipe IDs are sequential integers (GORM default), an unauthenticated actor can enumerate all recipes in the database by iterating IDs.
Impact
- Privacy: Recipes may contain personal dietary restrictions, family member data embedded in the prompt context, or other sensitive personalization details.
- Enumeration: Sequential IDs allow trivial full-dataset scraping without credentials.
- Data leakage: Even if recipes are intended to be shareable, the current design provides no opt-in or access control mechanism.
Suggested Fix
Move the route under apiProtected (with VerifyTokenMiddleware). If public sharing is a deliberate feature, implement an explicit sharing model (e.g., a shareable link with a random token, or a public: true flag checked before returning the recipe):
// Option A: require auth for all recipe reads
apiProtected.GET("/recipes/:recipe_id", middleware.AttachUserToContext(userService), recipeHandler.GetRecipe)
// Option B: return recipe only if public flag is set or user owns it
// (implement access check in GetRecipeByID service method)
Issue
In
internal/router/router.go(lines 53–57), the single-recipe endpoint is registered under the unauthenticated public group:Any request — authenticated or not — can fetch any recipe by its integer ID. Because recipe IDs are sequential integers (GORM default), an unauthenticated actor can enumerate all recipes in the database by iterating IDs.
Impact
Suggested Fix
Move the route under
apiProtected(withVerifyTokenMiddleware). If public sharing is a deliberate feature, implement an explicit sharing model (e.g., a shareable link with a random token, or apublic: trueflag checked before returning the recipe):