From d932f24694a889e2477e7a13dd6c83792181767c Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 7 May 2026 22:18:04 +0200 Subject: [PATCH] fix(security): cap PersonController size param at 50 to prevent resource exhaustion Addresses @Nora review: ?sort=documentCount&size=999999 could trigger a full-table query and large serialization. Cap enforced at controller boundary. Co-Authored-By: Claude Sonnet 4.6 --- .../familienarchiv/person/PersonController.java | 3 ++- .../familienarchiv/person/PersonControllerTest.java | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/org/raddatz/familienarchiv/person/PersonController.java b/backend/src/main/java/org/raddatz/familienarchiv/person/PersonController.java index c082ca0b..5c47cbde 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/person/PersonController.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/person/PersonController.java @@ -40,7 +40,8 @@ public class PersonController { @RequestParam(required = false, defaultValue = "0") int size, @RequestParam(required = false) String sort) { if ("documentCount".equals(sort) && size > 0 && q == null) { - return ResponseEntity.ok(personService.findTopByDocumentCount(size)); + int safeSize = Math.min(size, 50); + return ResponseEntity.ok(personService.findTopByDocumentCount(safeSize)); } return ResponseEntity.ok(personService.findAll(q)); } diff --git a/backend/src/test/java/org/raddatz/familienarchiv/person/PersonControllerTest.java b/backend/src/test/java/org/raddatz/familienarchiv/person/PersonControllerTest.java index 7d8044c7..c7800da1 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/person/PersonControllerTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/person/PersonControllerTest.java @@ -92,6 +92,18 @@ class PersonControllerTest { .andExpect(jsonPath("$[0].firstName").value("Käthe")); } + @Test + @WithMockUser(authorities = "READ_ALL") + void getPersons_capsTopByDocumentCount_atFifty() throws Exception { + ArgumentCaptor sizeCaptor = ArgumentCaptor.forClass(Integer.class); + when(personService.findTopByDocumentCount(sizeCaptor.capture())).thenReturn(Collections.emptyList()); + + mockMvc.perform(get("/api/persons").param("sort", "documentCount").param("size", "999")) + .andExpect(status().isOk()); + + assertThat(sizeCaptor.getValue()).isEqualTo(50); + } + private PersonSummaryDTO mockPersonSummary(String firstName, String lastName) { return new PersonSummaryDTO() { public java.util.UUID getId() { return UUID.randomUUID(); }