Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .cursor/rules/bitebyte-conventions.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
description: BiteByte conventions and security review checklist. Apply when reviewing a diff or PR, or editing Java/Spring or React files.
globs: ["**/*.java", "**/*.jsx", "**/*.tsx"]
alwaysApply: false
---

# BiteByte conventions

## Naming

- Java methods and variables: camelCase. NEVER snake_case.
- REST endpoints: plural resource nouns — /recipes, /recipes/{id}. NEVER verb paths like /getRecipe.

## Security — flag every occurrence in a review

- NEVER hardcode credentials, connection strings, or API keys in source. Require environment variables or a secret manager.
- ALWAYS validate and bound user input before it reaches a database query. Flag any raw @RequestParam passed into a query without validation.
10 changes: 10 additions & 0 deletions src/main/java/com/main/bitebyte/recipe/Recipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ public class Recipe {
private List<String> tags;
private int difficulty;
private int preparationTime;
private Integer prepTimeMinutes;
private int cookingTime;

// planted: secret in source control (fake credentials)
private static final String MONGO_URI =
"mongodb+srv://admin:FAKEpassword123@cluster0.example.mongodb.net/bitebyte";
private int servings;
private String nutritionalInfo;
private User user;
Expand Down Expand Up @@ -100,6 +105,11 @@ public void setPreparationTime(int preparationTime) {
this.preparationTime = preparationTime;
}

// violates the camelCase rule on purpose
public Integer get_prep_time() {
return this.prepTimeMinutes;
}

public int getCookingTime() {
return cookingTime;
}
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/main/bitebyte/recipe/RecipeController.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.main.bitebyte.user.Role;
Expand All @@ -35,6 +39,9 @@ public class RecipeController {
@Autowired
private UserRepository userRepository;

@Autowired
private MongoTemplate mongoTemplate;

@GetMapping("/all")
@PreAuthorize("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
public List<Recipe> getAllRecipes() {
Expand All @@ -50,6 +57,15 @@ public List<Recipe> getAllRecipes() {
}
}

@GetMapping("/search")
@PreAuthorize("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
public List<Recipe> search(@RequestParam String name) {
// planted: raw user input into a query, no validation or bounding
Query query = new Query();
query.addCriteria(Criteria.where("name").regex(name));
return mongoTemplate.find(query, Recipe.class);
}

@GetMapping("/{id}")
@PreAuthorize("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
public ResponseEntity<Recipe> getRecipeById(@PathVariable String id) {
Expand Down