From f96173cba5426018b07d2c2cb171f639cb0d1f2e Mon Sep 17 00:00:00 2001 From: jmj Date: Mon, 29 Jun 2026 00:33:05 +0900 Subject: [PATCH] =?UTF-8?q?fix(cover-letter):=20=ED=9A=8C=EC=9D=98?= =?UTF-8?q?=EC=A0=81=20=EB=A6=AC=EB=B7=B0=20=ED=95=98=EB=93=9C=EB=8B=9D=20?= =?UTF-8?q?=E2=80=94=20=EC=9E=85=EB=A0=A5=20=EC=84=9C=EB=B2=84=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20+=20PII=20=EB=A1=9C=EA=B7=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 회의적 max 리뷰(3/3·2/3 검증) 통과 결함 수정. - [HIGH] 자소서 생성 API 서버측 입력 상한 부재(JSON @RequestBody 는 multipart 20MB 미적용 → DoS/AI 비용 증폭). CoverLetterCreateRequest 에 bean-validation: title @Size(200), items @NotEmpty @Size(max=30) + List<@Valid Item>, Item.question @Size(500)/answer @Size(5000). title 200자 초과도 이제 400(기존 500). - [MEDIUM] 파싱 실패 로그에 자소서 답변 본문(PII) 평문 기록 → 길이만 기록으로 변경. 백엔드 전체 테스트 통과, OpenAPI(maxLength)·프론트 타입 재생성, 프론트 빌드 통과. Co-Authored-By: Claude Opus 4.8 --- backend/openapi.json | 15 +++++++++++---- .../application/CoverLetterService.java | 3 ++- .../dto/CoverLetterCreateRequest.java | 13 ++++++++++--- 3 files changed, 23 insertions(+), 8 deletions(-) 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() {