diff --git a/backend/openapi.json b/backend/openapi.json index d9ee123b..0b1c4191 100644 --- a/backend/openapi.json +++ b/backend/openapi.json @@ -3229,14 +3229,17 @@ "type" : "object", "properties" : { "title" : { - "type" : "string" + "type" : "string", + "maxLength" : 200, + "minLength" : 0 }, "items" : { "type" : "array", "items" : { "$ref" : "#/components/schemas/Item" }, - "minItems" : 1 + "maxItems" : 30, + "minItems" : 0 } }, "required" : [ "items" ] @@ -3245,10 +3248,14 @@ "type" : "object", "properties" : { "question" : { - "type" : "string" + "type" : "string", + "maxLength" : 500, + "minLength" : 0 }, "answer" : { - "type" : "string" + "type" : "string", + "maxLength" : 5000, + "minLength" : 0 } } }, diff --git a/backend/src/main/java/com/stackup/stackup/coverletter/application/CoverLetterService.java b/backend/src/main/java/com/stackup/stackup/coverletter/application/CoverLetterService.java index 710b3cc2..8fb2d2ff 100644 --- a/backend/src/main/java/com/stackup/stackup/coverletter/application/CoverLetterService.java +++ b/backend/src/main/java/com/stackup/stackup/coverletter/application/CoverLetterService.java @@ -98,7 +98,8 @@ private List parse(String json) { try { return JSON.readValue(json, ITEM_LIST); } catch (JsonProcessingException e) { - log.warn("cover letter items parse failed, return empty. raw={}", json, e); + // 자소서 답변 본문(PII)은 로그에 남기지 않는다 — 길이만 기록. + log.warn("cover letter items parse failed, return empty. length={}", json.length(), e); return List.of(); } } diff --git a/backend/src/main/java/com/stackup/stackup/coverletter/presentation/dto/CoverLetterCreateRequest.java b/backend/src/main/java/com/stackup/stackup/coverletter/presentation/dto/CoverLetterCreateRequest.java index ef0ca2a2..dd778881 100644 --- a/backend/src/main/java/com/stackup/stackup/coverletter/presentation/dto/CoverLetterCreateRequest.java +++ b/backend/src/main/java/com/stackup/stackup/coverletter/presentation/dto/CoverLetterCreateRequest.java @@ -2,14 +2,21 @@ import com.stackup.stackup.coverletter.application.dto.CoverLetterCreateCommand; import com.stackup.stackup.coverletter.application.dto.CoverLetterItem; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; import java.util.List; +// 서버측 입력 상한 — JSON @RequestBody 는 multipart 20MB 제한이 적용되지 않으므로 +// 거대 payload(DoS·AI 비용 증폭)를 bean-validation 으로 차단한다. 상한은 프론트 입력 캡과 일치. public record CoverLetterCreateRequest( - String title, - @NotEmpty List items + @Size(max = 200) String title, + @NotEmpty @Size(max = 30) List<@Valid Item> items ) { - public record Item(String question, String answer) { + public record Item( + @Size(max = 500) String question, + @Size(max = 5000) String answer + ) { } public CoverLetterCreateCommand toCommand() {