docs: B2·B4·FE 반영 + OWASP/multipart 가이드·dev prefetch 노트
- security-remediation-checklist §B2·§B4 완료조건 [x] 갱신 - 신규: owasp-dependency-check-guide, multipart-size-risk, changes/work-log 기록 - local-dev-setup: SNAPSHOT→GA 후 online plugin prefetch 함정 노트 - analysis: 제거된 dead code(abstracts/GameCatalog/header.jspf) stale 정정 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K3FeMrbtxfTScjrwUukyHD
This commit is contained in:
parent
4f4811eb73
commit
aae7629562
|
|
@ -30,9 +30,9 @@ Java/Maven 웹앱(Spring Boot 3 MVC + MyBatis + PostgreSQL, JSP 뷰) 전면 read
|
||||||
| 1 | INFO(긍정) | **SQL 인젝션 표면 0** — 6개 매퍼 전 쿼리가 파라미터 바인딩 `#{}` 만 사용. `${}` 문자열 보간 단 1건도 없음(java/xml 전수 grep). 검색어도 `ILIKE CONCAT('%', #{query}, '%')` 로 안전. | mapper/*.java 전체 |
|
| 1 | INFO(긍정) | **SQL 인젝션 표면 0** — 6개 매퍼 전 쿼리가 파라미터 바인딩 `#{}` 만 사용. `${}` 문자열 보간 단 1건도 없음(java/xml 전수 grep). 검색어도 `ILIKE CONCAT('%', #{query}, '%')` 로 안전. | mapper/*.java 전체 |
|
||||||
| 2 | INFO(긍정) | **비밀번호 해싱 견고** — PBKDF2WithHmacSHA256, 210,000 iterations, 16B salt, 256bit, `MessageDigest.isEqual`(상수시간 비교). 형식 `pbkdf2_sha256$iter$salt$hash`. | UserController.java:542-576 |
|
| 2 | INFO(긍정) | **비밀번호 해싱 견고** — PBKDF2WithHmacSHA256, 210,000 iterations, 16B salt, 256bit, `MessageDigest.isEqual`(상수시간 비교). 형식 `pbkdf2_sha256$iter$salt$hash`. | UserController.java:542-576 |
|
||||||
| 3 | MED | **POST /login, /signup CSRF 미검증** — logout/profile/game/recruit 모든 mutation 은 `CsrfTokens.isValid()` 검사하나 login·signup 은 누락 → login-CSRF 가능. | UserController.java:118-160(login), 65-116(signup) |
|
| 3 | MED | **POST /login, /signup CSRF 미검증** — logout/profile/game/recruit 모든 mutation 은 `CsrfTokens.isValid()` 검사하나 login·signup 은 누락 → login-CSRF 가능. | UserController.java:118-160(login), 65-116(signup) |
|
||||||
| 4 | MED(부채) | **abstracts 패키지 전체 dead code** — `Service`/`Request`/`Result`/`ErrorResult` 어디서도 상속·호출 안 됨. `Service.ChackService` 는 세션키 `"id"`(line 12) 사용, 활성 코드는 `"userId"` 사용 → 재사용 시 인증 우회 함정. | abstracts/Service.java:12 |
|
| 4 | MED(부채) | **abstracts 패키지 전체 dead code** — `Service`/`Request`/`Result`/`ErrorResult` 어디서도 상속·호출 안 됨. `Service.ChackService` 는 세션키 `"id"`(line 12) 사용, 활성 코드는 `"userId"` 사용 → 재사용 시 인증 우회 함정. | abstracts/Service.java:12 | **제거됨(2026-06-29)** — abstracts/ 패키지 4파일 전체 삭제. [changes/2026-06-29-b2-b4-fe-hardening.md](../changes/2026-06-29-b2-b4-fe-hardening.md) |
|
||||||
| 5 | MED(부채) | **orphan 깨진 JSP fragment** — `WEB-INF/jsp/fragments/header.jspf` 가 Spring Security 태그(`<sec:authorize>`, `${_csrf}`)와 JSTL 사용하나 (a)어디서도 include 안 됨 (b)Spring Security 의존성 자체가 pom 에 없음 (c)라우트(`/games/new`,`/register`)·브랜딩('비빔밥')이 활성앱(`/game/new`,`/signup`,'bibimbap')과 불일치. 이전 프로토타입 잔재. | jsp/fragments/header.jspf:1-25 |
|
| 5 | MED(부채) | **orphan 깨진 JSP fragment** — `WEB-INF/jsp/fragments/header.jspf` 가 Spring Security 태그(`<sec:authorize>`, `${_csrf}`)와 JSTL 사용하나 (a)어디서도 include 안 됨 (b)Spring Security 의존성 자체가 pom 에 없음 (c)라우트(`/games/new`,`/register`)·브랜딩('비빔밥')이 활성앱(`/game/new`,`/signup`,'bibimbap')과 불일치. 이전 프로토타입 잔재. | jsp/fragments/header.jspf:1-25 | **제거됨(2026-06-29)** — header.jspf 삭제. [changes/2026-06-29-b2-b4-fe-hardening.md](../changes/2026-06-29-b2-b4-fe-hardening.md) |
|
||||||
| 6 | MED(부채) | **GameCatalog 완전 stub** — 모든 배열이 `{}` 빈 배열, `COUNT=0`. `GameController.gameDetail` 의 폴백 분기(line 111-128)는 영구 도달 불가(`isValidId` 는 항상 false). 죽은 분기 + 죽은 클래스. | game/GameCatalog.java:8-22, controller/api/GameController.java:111-128 |
|
| 6 | MED(부채) | **GameCatalog 완전 stub** — 모든 배열이 `{}` 빈 배열, `COUNT=0`. `GameController.gameDetail` 의 폴백 분기(line 111-128)는 영구 도달 불가(`isValidId` 는 항상 false). 죽은 분기 + 죽은 클래스. | game/GameCatalog.java:8-22, controller/api/GameController.java:111-128 | **제거됨(2026-06-29)** — GameCatalog.java 삭제·fallback 분기 제거. 없는 게임 ID = HTTP 404. [changes/2026-06-29-b2-b4-fe-hardening.md](../changes/2026-06-29-b2-b4-fe-hardening.md) |
|
||||||
| 7 | MED(부채) | **게임 댓글: DB 스키마·매퍼 존재하나 미연결** — `game_comments` 테이블, `GameCommentsMapper`(get/add/update), `GameCommentData` 모두 존재하지만 **댓글 작성/조회 컨트롤러 엔드포인트 없음**. 실제 댓글은 JSP 내 localStorage 클라이언트 전용(서버 미저장). `softDeleteGameComments` 만 게임 삭제 시 호출됨. | mapper/GameCommentsMapper.java, views/game-detail.jsp:907-1000 |
|
| 7 | MED(부채) | **게임 댓글: DB 스키마·매퍼 존재하나 미연결** — `game_comments` 테이블, `GameCommentsMapper`(get/add/update), `GameCommentData` 모두 존재하지만 **댓글 작성/조회 컨트롤러 엔드포인트 없음**. 실제 댓글은 JSP 내 localStorage 클라이언트 전용(서버 미저장). `softDeleteGameComments` 만 게임 삭제 시 호출됨. | mapper/GameCommentsMapper.java, views/game-detail.jsp:907-1000 |
|
||||||
| 8 | LOW | **WebApplicationFirewall/인증필터 부재 — 세션 기반 임시방편 인증** — Spring Security 미사용. 각 컨트롤러가 수동으로 `session.getAttribute("userId")` 체크. 인가는 리소스 소유권 체크(`userId.equals(game.getUserId())`)로 일관 처리됨(양호)하나 중앙 집중 필터 없음 → 신규 엔드포인트에서 체크 누락 위험. | 전 컨트롤러 |
|
| 8 | LOW | **WebApplicationFirewall/인증필터 부재 — 세션 기반 임시방편 인증** — Spring Security 미사용. 각 컨트롤러가 수동으로 `session.getAttribute("userId")` 체크. 인가는 리소스 소유권 체크(`userId.equals(game.getUserId())`)로 일관 처리됨(양호)하나 중앙 집중 필터 없음 → 신규 엔드포인트에서 체크 누락 위험. | 전 컨트롤러 |
|
||||||
| 9 | LOW | **생성 산출물 git 추적** — `src/test/db/dev-to-live-update.sql`(테스트가 생성하는 dev→live 마이그레이션 진단)이 커밋됨. 실제 비밀값은 없고(`-- DATA DIFF ... password_hash` 주석뿐) 비밀 누출 아님. 단 생성물은 비추적이 적절. | src/test/db/dev-to-live-update.sql:54 |
|
| 9 | LOW | **생성 산출물 git 추적** — `src/test/db/dev-to-live-update.sql`(테스트가 생성하는 dev→live 마이그레이션 진단)이 커밋됨. 실제 비밀값은 없고(`-- DATA DIFF ... password_hash` 주석뿐) 비밀 누출 아님. 단 생성물은 비추적이 적절. | src/test/db/dev-to-live-update.sql:54 |
|
||||||
|
|
@ -47,12 +47,12 @@ Java/Maven 웹앱(Spring Boot 3 MVC + MyBatis + PostgreSQL, JSP 뷰) 전면 read
|
||||||
| 패키지 | 역할 |
|
| 패키지 | 역할 |
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| (root) | `BibimbapApplication`(@SpringBootApplication main), `ServletInitializer`(WAR 배포용 SpringBootServletInitializer) |
|
| (root) | `BibimbapApplication`(@SpringBootApplication main), `ServletInitializer`(WAR 배포용 SpringBootServletInitializer) |
|
||||||
| abstracts | **dead code** — 미사용 제네릭 Service/Request/Result/ErrorResult 베이스 (확인됨: 참조 0) |
|
| abstracts | **dead code** — 미사용 제네릭 Service/Request/Result/ErrorResult 베이스 (확인됨: 참조 0) → **패키지 전체 제거됨(2026-06-29)** |
|
||||||
| config | `UploadResourceConfig` — 업로드 경로의 `/profile/**` 정적 리소스 핸들러 등록 |
|
| config | `UploadResourceConfig` — 업로드 경로의 `/profile/**` 정적 리소스 핸들러 등록 |
|
||||||
| controller | 페이지(뷰) 컨트롤러 — `WebMvcController`(공통 페이지+에러), `RecruitController`(모집 페이지+API 혼합) |
|
| controller | 페이지(뷰) 컨트롤러 — `WebMvcController`(공통 페이지+에러), `RecruitController`(모집 페이지+API 혼합) |
|
||||||
| controller/api | API/mutation 컨트롤러 — Game CRUD, GameUpload(zip/썸네일/파일), GameAsset(WebGL 서빙), User(인증/프로필), 예외 핸들러 |
|
| controller/api | API/mutation 컨트롤러 — Game CRUD, GameUpload(zip/썸네일/파일), GameAsset(WebGL 서빙), User(인증/프로필), 예외 핸들러 |
|
||||||
| data | 순수 POJO DTO 6종 (Lombok 미사용 — 수동 getter/setter) |
|
| data | 순수 POJO DTO 6종 (Lombok 미사용 — 수동 getter/setter) |
|
||||||
| game | `GameCatalog` — **stub(빈 배열)**, 레거시 정적 게임 카탈로그 폴백(현재 무력) |
|
| game | `GameCatalog` — **stub(빈 배열)**, 레거시 정적 게임 카탈로그 폴백(현재 무력) → **삭제됨(2026-06-29). 없는 게임 ID = HTTP 404** |
|
||||||
| mapper | MyBatis `@Mapper` 인터페이스 6종 — 어노테이션 기반 SQL(XML 매퍼 0) |
|
| mapper | MyBatis `@Mapper` 인터페이스 6종 — 어노테이션 기반 SQL(XML 매퍼 0) |
|
||||||
| security | `CsrfTokens` — 세션 기반 CSRF 토큰 발급/검증 유틸(static) |
|
| security | `CsrfTokens` — 세션 기반 CSRF 토큰 발급/검증 유틸(static) |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
# 2026-06-29 B2·B4·FE 하드닝
|
||||||
|
|
||||||
|
**세션**: sid 20260629-175705 (resumed from 20260629-174807)
|
||||||
|
**브랜치**: feat/v2
|
||||||
|
**최종 테스트**: `mvn test` 361 PASS
|
||||||
|
|
||||||
|
## 요약
|
||||||
|
|
||||||
|
3트랙 병렬 작업: B2 프로토타입 dead code 제거, B4 의존성/세션/운영 하드닝, FE 프론트엔드 디자인 개선.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## B2. 프로토타입 dead code 제거
|
||||||
|
|
||||||
|
### 삭제된 파일
|
||||||
|
|
||||||
|
| 파일 | 이유 |
|
||||||
|
| --- | --- |
|
||||||
|
| `src/main/java/com/pandoli365/bibimbap/abstracts/Service.java` | 참조 0, 세션키 `"id"` 불일치(활성코드는 `"userId"`) — 인증 우회 함정 |
|
||||||
|
| `src/main/java/com/pandoli365/bibimbap/abstracts/Request.java` | 참조 0, dead |
|
||||||
|
| `src/main/java/com/pandoli365/bibimbap/abstracts/Result.java` | 참조 0, dead |
|
||||||
|
| `src/main/java/com/pandoli365/bibimbap/abstracts/ErrorResult.java` | 참조 0, dead |
|
||||||
|
| `src/main/webapp/WEB-INF/jsp/fragments/header.jspf` | include 참조 0, Spring Security 의존성 없는 환경에서의 orphan |
|
||||||
|
| `src/main/java/com/pandoli365/bibimbap/game/GameCatalog.java` | 빈 배열 stub, 영구 도달불가 분기 |
|
||||||
|
|
||||||
|
### 동작 변경
|
||||||
|
|
||||||
|
- **없는 게임 ID 접근**: 기존 `GameCatalog` fallback(redirect:/ 또는 정적뷰) → **HTTP 404** (`ResponseStatusException(NOT_FOUND)`). 사용자 확정(D-B2-404).
|
||||||
|
- `ApiExceptionControllerAdvice` 에 `ResponseStatusException` 핸들러 추가 — 기존 `Exception` catch-all 이 404를 500으로 덮던 결함 수정.
|
||||||
|
|
||||||
|
### 테스트
|
||||||
|
|
||||||
|
- `GameLikeControllerTest.gameDetailThrowsNotFoundWhenGameMissing` 회귀 가드 추가.
|
||||||
|
- 런타임 스모크: `GET /game/99999` = 404, `GET /game/3` = 200.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## B4. 의존성/세션/운영 하드닝
|
||||||
|
|
||||||
|
### pom.xml
|
||||||
|
|
||||||
|
| 변경 | 이전 | 이후 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Spring Boot 버전 | `3.5.14-SNAPSHOT` | `3.5.16` (GA) |
|
||||||
|
| spring-snapshots repository | 있음 | **제거** |
|
||||||
|
| spring-snapshots pluginRepository | 있음 | **제거** |
|
||||||
|
| OWASP Dependency-Check plugin | 없음 | `12.2.2` 추가(빌드 비bind, 수동 실행) |
|
||||||
|
|
||||||
|
### application.properties (공통 base)
|
||||||
|
|
||||||
|
| 변경 | 이전 | 이후 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ibatis/mybatis 로그 | `TRACE` | `WARN` |
|
||||||
|
| 세션 쿠키 HttpOnly | 미설정 | `true` |
|
||||||
|
| 세션 쿠키 SameSite | 미설정 | `lax` |
|
||||||
|
|
||||||
|
### 신규 파일
|
||||||
|
|
||||||
|
- `src/main/resources/application-dev.properties` — ibatis `TRACE` 로그 복원(로컬 개발 전용).
|
||||||
|
- `src/main/resources/application-live.properties` — `server.servlet.session.cookie.secure=true` (운영 전용).
|
||||||
|
|
||||||
|
### 런타임 확인
|
||||||
|
|
||||||
|
`Set-Cookie: JSESSIONID=...; Path=/; HttpOnly; SameSite=Lax` 확인.
|
||||||
|
|
||||||
|
### 결정 기록
|
||||||
|
|
||||||
|
- D-B4-cfgloc: 설정 위치 = Spring 표준 `application-{profile}.properties`. 기존 `db.properties`(DB 전용) 공존.
|
||||||
|
- D-B4-multipart: 1GB 유지, 리스크 문서화 → [security/multipart-size-risk.md](../security/multipart-size-risk.md).
|
||||||
|
- D-B4-cve: OWASP Dependency-Check 오프라인 비bind → [security/owasp-dependency-check-guide.md](../security/owasp-dependency-check-guide.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FE. 프론트엔드 디자인 개선
|
||||||
|
|
||||||
|
### 변경 내용
|
||||||
|
|
||||||
|
| 항목 | 변경 |
|
||||||
|
| --- | --- |
|
||||||
|
| F1 — index 검색 위계 | `.advanced-search` 를 `<details>` 접이식으로 래핑. prefill(쿼리 파라미터) 시 `open` 속성 자동 부여 |
|
||||||
|
| F2 — 다크모드 input 대비 | 입력 테두리 `border-color: rgba(255,255,255,0.26)` 강화. 적용 뷰: login·signup·index·game-detail |
|
||||||
|
| F5 — 터치타깃 | 체크박스 font-size `1.15rem`. 적용 뷰: login·signup |
|
||||||
|
|
||||||
|
### 확인
|
||||||
|
|
||||||
|
브라우저 스모크: 다크모드 토글 후 input 대비 개선, 검색 접이식 동작, 터치타깃 확대 확인.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 참조
|
||||||
|
|
||||||
|
- 보안 체크리스트: [security/security-remediation-checklist.md](../security/security-remediation-checklist.md)
|
||||||
|
- OWASP DC 가이드: [security/owasp-dependency-check-guide.md](../security/owasp-dependency-check-guide.md)
|
||||||
|
- Multipart 리스크: [security/multipart-size-risk.md](../security/multipart-size-risk.md)
|
||||||
|
- Local dev 함정: [development/local-dev-setup.md](../development/local-dev-setup.md)
|
||||||
|
|
@ -9,3 +9,4 @@
|
||||||
- [2026-06-29-w3-w4-features.md](./2026-06-29-w3-w4-features.md) — W3(잔여)+W4 5기능 구현(W3-1·W3-4·W3-3·W3-5·W4) + 후속(20260629-142115) 배지 표시 배선 완료·보안 하드닝 b1/b2. 태그+검색·메인허브·포스팅보드·업로드보안·배지/평판 구현(5 커밋 35f1dc3~305cc73, 최종 L1 347/347 GREEN). 후속: 게임카드 creator 배지 칩·프로필 myBadges 배선(3d10449, L1 352/352) + SsrfSafeFetcher @PostConstruct DNS ttl=30(b1 best-effort) + 업로드 저장루트 static 밖 이전(b2, 9041bb7, L1 353/353). DB DDL 8종 사용자 직접 적용 완료. L3 스모크 및 b2 자산 수동 이전은 배포 후 과제 이월 — 상세: [maintenance/post-deploy-verification-checklist.md](../maintenance/post-deploy-verification-checklist.md).
|
- [2026-06-29-w3-w4-features.md](./2026-06-29-w3-w4-features.md) — W3(잔여)+W4 5기능 구현(W3-1·W3-4·W3-3·W3-5·W4) + 후속(20260629-142115) 배지 표시 배선 완료·보안 하드닝 b1/b2. 태그+검색·메인허브·포스팅보드·업로드보안·배지/평판 구현(5 커밋 35f1dc3~305cc73, 최종 L1 347/347 GREEN). 후속: 게임카드 creator 배지 칩·프로필 myBadges 배선(3d10449, L1 352/352) + SsrfSafeFetcher @PostConstruct DNS ttl=30(b1 best-effort) + 업로드 저장루트 static 밖 이전(b2, 9041bb7, L1 353/353). DB DDL 8종 사용자 직접 적용 완료. L3 스모크 및 b2 자산 수동 이전은 배포 후 과제 이월 — 상세: [maintenance/post-deploy-verification-checklist.md](../maintenance/post-deploy-verification-checklist.md).
|
||||||
- [2026-06-29-game-like-server-persistence.md](./2026-06-29-game-like-server-persistence.md) — 게임 "좋아요" 서버 영속화. 좋아요가 서버에 안 써지고 localStorage 로만 토글되던 버그 수정. 신규 `POST /game/{id}/like` 토글(@Transactional, CSRF→로그인→존재 게이트) + game_likes row/`games.like_count` 컬럼 단일 트랜잭션 ±1 동기 + 상세 JSP fetch 전환 + explore 무변경(컬럼 자동반영). L1 PASS(GameLikeControllerTest 7/7 + 회귀 219/219), L2 dev DB skip. UNIQUE 마이그레이션(20260629-game-likes-unique.sql) 운영 적용·실환경 스모크는 needs_user_verification — 절차: [maintenance/post-deploy-verification-checklist.md](../maintenance/post-deploy-verification-checklist.md). 좋아요 항목으로 보안 B3 부분 충족.
|
- [2026-06-29-game-like-server-persistence.md](./2026-06-29-game-like-server-persistence.md) — 게임 "좋아요" 서버 영속화. 좋아요가 서버에 안 써지고 localStorage 로만 토글되던 버그 수정. 신규 `POST /game/{id}/like` 토글(@Transactional, CSRF→로그인→존재 게이트) + game_likes row/`games.like_count` 컬럼 단일 트랜잭션 ±1 동기 + 상세 JSP fetch 전환 + explore 무변경(컬럼 자동반영). L1 PASS(GameLikeControllerTest 7/7 + 회귀 219/219), L2 dev DB skip. UNIQUE 마이그레이션(20260629-game-likes-unique.sql) 운영 적용·실환경 스모크는 needs_user_verification — 절차: [maintenance/post-deploy-verification-checklist.md](../maintenance/post-deploy-verification-checklist.md). 좋아요 항목으로 보안 B3 부분 충족.
|
||||||
- [2026-06-24-w2-jam-platform.md](./2026-06-24-w2-jam-platform.md) — W2 게임잼 워크스트림 전체(W2-1~6) 구현. 잼 엔티티/라이프사이클(jams/jam_teams/jam_team_members/jam_entries/jam_status_log) + 심사위원 역할(jam_judges 잼스코프) + 평가 동결 스키마(jam_criteria/jam_scores/jam_votes/jam_awards + jam_score_stats VIEW, 평가단위 (jam_id,game_id) 활성 자연키) + 심사 평가(3중게이트 UPSERT 가중집계) + 인기투표(1인1표 UNIQUE·종료후 공개) + 시상 집계(3트랙+가중 GRAND·CLOSED 확정 멱등). GAME_JAM_MANAGE 첫 enforcement 연결·잼스코프 isJudge·평가기간 게이트·CSRF 전수. 6 커밋 ccf1e42~a74bf74. 최종 L1 190/190 GREEN(회귀 0), L2 dev DB contract 전 PASS(격리 throwaway DB). dev DB 마이그레이션·L3 스모크 needs_user_verification.
|
- [2026-06-24-w2-jam-platform.md](./2026-06-24-w2-jam-platform.md) — W2 게임잼 워크스트림 전체(W2-1~6) 구현. 잼 엔티티/라이프사이클(jams/jam_teams/jam_team_members/jam_entries/jam_status_log) + 심사위원 역할(jam_judges 잼스코프) + 평가 동결 스키마(jam_criteria/jam_scores/jam_votes/jam_awards + jam_score_stats VIEW, 평가단위 (jam_id,game_id) 활성 자연키) + 심사 평가(3중게이트 UPSERT 가중집계) + 인기투표(1인1표 UNIQUE·종료후 공개) + 시상 집계(3트랙+가중 GRAND·CLOSED 확정 멱등). GAME_JAM_MANAGE 첫 enforcement 연결·잼스코프 isJudge·평가기간 게이트·CSRF 전수. 6 커밋 ccf1e42~a74bf74. 최종 L1 190/190 GREEN(회귀 0), L2 dev DB contract 전 PASS(격리 throwaway DB). dev DB 마이그레이션·L3 스모크 needs_user_verification.
|
||||||
|
- [2026-06-29-b2-b4-fe-hardening.md](./2026-06-29-b2-b4-fe-hardening.md) — B2 프로토타입 dead code 제거(abstracts 4파일·header.jspf·GameCatalog 삭제, 없는 게임 ID = HTTP 404) + B4 의존성/세션 하드닝(Spring Boot 3.5.16 GA·snapshot repo 제거·쿠키 HttpOnly/SameSite·로그 WARN·OWASP DC plugin 비bind·profile properties 신규) + FE 다크모드 대비·검색 접이식·터치타깃. mvn test 361 PASS.
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,19 @@
|
||||||
- **Java 코드**: `docker compose restart app` (spring-boot:run 재기동 ~1~2s). 또는 `docker compose exec app mvn -o -P dev -DskipTests compile` 후 restart.
|
- **Java 코드**: `docker compose restart app` (spring-boot:run 재기동 ~1~2s). 또는 `docker compose exec app mvn -o -P dev -DskipTests compile` 후 restart.
|
||||||
- 전제: 호스트 `~/.m2` 에 의존성 캐시 존재(offline `-o`). **새 의존성 추가 시**: override 의 `-o` 를 일시 제거해 1회 온라인 받거나, 호스트에서 `./mvnw -P dev dependency:go-offline` 후 다시 offline.
|
- 전제: 호스트 `~/.m2` 에 의존성 캐시 존재(offline `-o`). **새 의존성 추가 시**: override 의 `-o` 를 일시 제거해 1회 온라인 받거나, 호스트에서 `./mvnw -P dev dependency:go-offline` 후 다시 offline.
|
||||||
|
|
||||||
|
> **의존성/플러그인 버전 변경 후 online prefetch 필요** (함정, 2026-06-29 확인):
|
||||||
|
> SNAPSHOT→GA 등 버전 변경 후 offline 모드(`-o`)로 첫 기동 시 `spring-boot-maven-plugin` 신 버전 resolution 실패 → 컨테이너 즉시 종료. 컨테이너가 죽으면 `exec` 진입 불가.
|
||||||
|
>
|
||||||
|
> **처치**: 버전 변경 후 첫 기동 전 아래 중 하나로 온라인 prefetch 1회 수행.
|
||||||
|
> ```bash
|
||||||
|
> # 방법 A — run --rm 으로 플러그인·의존성 prefetch (컨테이너 내부, 호스트 ~/.m2 마운트됨)
|
||||||
|
> docker compose run --rm app mvn -B dependency:resolve-plugins dependency:resolve -P dev
|
||||||
|
>
|
||||||
|
> # 방법 B — 호스트에서 mvnw 직접 실행 (호스트 JDK 21 필요)
|
||||||
|
> ./mvnw -B -P dev dependency:resolve-plugins dependency:resolve
|
||||||
|
> ```
|
||||||
|
> prefetch 성공 후 `docker compose up -d app` 으로 offline 재기동하면 정상 동작.
|
||||||
|
|
||||||
- **배포 이미지**: override 를 **제외**하고 base 만 명시한다(자동병합 회피).
|
- **배포 이미지**: override 를 **제외**하고 base 만 명시한다(자동병합 회피).
|
||||||
```bash
|
```bash
|
||||||
docker compose -f docker-compose.yml build app # Dockerfile WAR 굽기
|
docker compose -f docker-compose.yml build app # Dockerfile WAR 굽기
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,6 @@
|
||||||
|
|
||||||
## 목록
|
## 목록
|
||||||
|
|
||||||
_(아직 문서 없음)_
|
- [security-remediation-checklist.md](./security-remediation-checklist.md) — 보안 개선 체크리스트 (B1~B4 항목별 근거·체크박스·완료 조건). 기준 분석: 2026-06-16 프로젝트 전면 분석.
|
||||||
|
- [owasp-dependency-check-guide.md](./owasp-dependency-check-guide.md) — OWASP Dependency-Check maven plugin 운영 가이드. 수동 실행·오프라인 모드·빌드 비bind 이유·예외 기준(CVSS 7+). (B4, 2026-06-29)
|
||||||
|
- [multipart-size-risk.md](./multipart-size-risk.md) — multipart 업로드 1GB 상한 유지 결정 및 DoS/디스크 고갈 리스크 문서화. 완화 권고(리버스프록시·모니터링) 포함. (B4, 2026-06-29)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Multipart 업로드 1GB 상한 유지 — 리스크 문서화
|
||||||
|
|
||||||
|
**결정**: multipart 상한 1GB 유지 (사용자 확정, D-B4-multipart, sid 20260629-175705)
|
||||||
|
**관련 체크리스트**: [security-remediation-checklist.md §B4](./security-remediation-checklist.md#b4-의존성세션운영-하드닝)
|
||||||
|
|
||||||
|
## 현재 설정
|
||||||
|
|
||||||
|
`src/main/resources/application.properties`:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
spring.servlet.multipart.max-file-size=1GB
|
||||||
|
spring.servlet.multipart.max-request-size=1GB
|
||||||
|
```
|
||||||
|
|
||||||
|
## 리스크
|
||||||
|
|
||||||
|
### DoS (Denial of Service)
|
||||||
|
|
||||||
|
- 악의적 클라이언트가 1GB 파일 업로드를 동시에 여러 건 요청하면 서버 처리 스레드·메모리·디스크 I/O를 소진할 수 있다.
|
||||||
|
- Spring MVC 기본 설정에서 multipart 파싱은 요청 완료 전까지 임시 파일(`java.io.tmpdir`)에 버퍼링된다.
|
||||||
|
- 임시 디스크 고갈 시 다른 기능(로그, 쓰기 연산)도 함께 영향받는다.
|
||||||
|
|
||||||
|
### 디스크 고갈
|
||||||
|
|
||||||
|
- 업로드 임시 경로(`/tmp` 또는 컨테이너 tmpfs)와 저장 경로(`~/.bibimbap/uploads`)가 모두 대용량 파일로 채워질 수 있다.
|
||||||
|
- 컨테이너 환경에서 tmpfs 상한이 낮으면 1GB 단일 파일에도 OOM 오류가 발생한다.
|
||||||
|
|
||||||
|
## 유지 결정 근거
|
||||||
|
|
||||||
|
- bibimbap 은 게임 WebGL 번들 업로드를 지원하며, 실제 게임 빌드 크기가 수백 MB에 이를 수 있다. 상한을 낮추면 합법적 업로드가 차단된다.
|
||||||
|
- 현재 운영 규모(소규모)에서 대규모 DoS 시도 가능성이 낮다고 사용자가 판단했다.
|
||||||
|
- 상한 축소·업로드 스트리밍 개선은 이후 운영 부하 확인 후 별도 검토.
|
||||||
|
|
||||||
|
## 완화 권고 (추후)
|
||||||
|
|
||||||
|
| 권고 항목 | 설명 |
|
||||||
|
| --- | --- |
|
||||||
|
| 리버스 프록시 상한 설정 | nginx `client_max_body_size` 또는 AWS ALB 요청 크기 제한으로 애플리케이션 앞단 차단 |
|
||||||
|
| 디스크 모니터링 | 업로드 경로(`~/.bibimbap/uploads`) 및 tmpdir 사용량 alert 설정 (예: 80% 도달 시 알림) |
|
||||||
|
| 업로드 인증 게이트 | 현재 게임 업로드는 로그인 필수 — 익명 대용량 업로드 차단. 권한 검사 유지 확인. |
|
||||||
|
| 동시 업로드 rate limit | Spring MVC thread pool 또는 리버스 프록시 connection limit으로 동시 대용량 요청 제한 |
|
||||||
|
| tmpdir 분리 | `spring.servlet.multipart.location` 으로 tmpdir를 전용 볼륨으로 분리해 시스템 영향 격리 |
|
||||||
|
|
||||||
|
## 관련 문서
|
||||||
|
|
||||||
|
- [owasp-dependency-check-guide.md](./owasp-dependency-check-guide.md) — 동일 B4 트랙 CVE 스캔 설정
|
||||||
|
- [security-remediation-checklist.md](./security-remediation-checklist.md) — 전체 보안 체크리스트
|
||||||
|
- [../maintenance/post-deploy-verification-checklist.md](../maintenance/post-deploy-verification-checklist.md) — 배포 후 디스크 확인 절차
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
# OWASP Dependency-Check 운영 가이드
|
||||||
|
|
||||||
|
**적용 범위**: bibimbap `pom.xml` — `dependency-check-maven 12.2.2`
|
||||||
|
**결정 출처**: D-B4-cve (sid 20260629-175705)
|
||||||
|
**관련 체크리스트**: [security-remediation-checklist.md §B4](./security-remediation-checklist.md#b4-의존성세션운영-하드닝)
|
||||||
|
|
||||||
|
## 설정 개요
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.owasp</groupId>
|
||||||
|
<artifactId>dependency-check-maven</artifactId>
|
||||||
|
<version>12.2.2</version>
|
||||||
|
<configuration>
|
||||||
|
<autoUpdate>false</autoUpdate>
|
||||||
|
<failBuildOnCVSS>7</failBuildOnCVSS>
|
||||||
|
<skipProvidedScope>true</skipProvidedScope>
|
||||||
|
</configuration>
|
||||||
|
<!-- executions 없음 — 빌드 phase 비bind -->
|
||||||
|
</plugin>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 수동 실행
|
||||||
|
|
||||||
|
### 온라인 모드 (NVD 데이터 갱신 포함)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn dependency-check:check
|
||||||
|
```
|
||||||
|
|
||||||
|
NVD API 키가 없으면 요청 제한에 걸려 매우 느릴 수 있다. 처음 실행 시 수 분 소요 정상.
|
||||||
|
|
||||||
|
### 오프라인 모드 (캐시 재사용)
|
||||||
|
|
||||||
|
`autoUpdate=false`가 기본이므로 플러그인 설정 그대로 실행하면 로컬 캐시를 사용한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn dependency-check:check
|
||||||
|
```
|
||||||
|
|
||||||
|
캐시 위치: `~/.m2/repository/org/owasp/dependency-check-data/` (기본값).
|
||||||
|
캐시가 없으면 온라인 다운로드가 강제된다.
|
||||||
|
|
||||||
|
캐시를 명시적으로 지정하려면:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn dependency-check:check -Ddependency-check.dataDirectory=/path/to/cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### 도커 dev 환경
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec app mvn dependency-check:check
|
||||||
|
```
|
||||||
|
|
||||||
|
호스트 `~/.m2` 가 마운트되어 있으면 캐시를 공유한다.
|
||||||
|
|
||||||
|
## 예외 기준
|
||||||
|
|
||||||
|
| 설정 | 값 | 설명 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `failBuildOnCVSS` | `7` | CVSS 7.0 이상 high/critical만 빌드 실패 유발 |
|
||||||
|
| `skipProvidedScope` | `true` | WAS/컨테이너 제공 라이브러리(서블릿 API 등) 제외 |
|
||||||
|
|
||||||
|
CVSS 7 미만(low/medium) CVE는 보고서에 기록되지만 빌드를 실패시키지 않는다.
|
||||||
|
특정 CVE를 false positive로 억제하려면 suppression 파일 사용:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn dependency-check:check -Ddependency-check.suppressionFile=owasp-suppressions.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
## 빌드 phase 비bind 이유
|
||||||
|
|
||||||
|
`executions` 블록을 두지 않아 일반 `mvn package`·`mvn test` 에 포함되지 않는다.
|
||||||
|
|
||||||
|
**이유**:
|
||||||
|
1. NVD 데이터 갱신/조회에 수 분이 소요되어 CI 빌드 시간 급증.
|
||||||
|
2. `autoUpdate=false` 오프라인 모드에서도 스캔 자체 시간이 30초~수 분.
|
||||||
|
3. CVE 결과는 즉각 hotfix가 아닌 우선순위 검토·예외 결정을 필요로 한다 — 빌드 자동 실패보다 수동 검토가 적합.
|
||||||
|
4. 로컬 dev 캐시 없는 환경(CI 클린 이미지)에서 온라인 의존성 생김.
|
||||||
|
|
||||||
|
**권장 주기**: 배포 전 수동 1회 실행 → 보고서 검토 → CVSS 7+ 항목 처리 후 배포.
|
||||||
|
|
||||||
|
## 보고서 위치
|
||||||
|
|
||||||
|
실행 후 `target/dependency-check-report.html` 생성.
|
||||||
|
콘솔에서 HIGH/CRITICAL 요약 출력됨.
|
||||||
|
|
||||||
|
## 관련 문서
|
||||||
|
|
||||||
|
- [multipart-size-risk.md](./multipart-size-risk.md) — 1GB 유지 리스크 (동일 B4 트랙)
|
||||||
|
- [security-remediation-checklist.md](./security-remediation-checklist.md) — 전체 보안 체크리스트
|
||||||
|
|
@ -66,20 +66,32 @@
|
||||||
- `GameCatalog`는 빈 배열 기반 fallback이다. `src/main/java/com/pandoli365/bibimbap/game/GameCatalog.java:8`, `src/main/java/com/pandoli365/bibimbap/game/GameCatalog.java:18`
|
- `GameCatalog`는 빈 배열 기반 fallback이다. `src/main/java/com/pandoli365/bibimbap/game/GameCatalog.java:8`, `src/main/java/com/pandoli365/bibimbap/game/GameCatalog.java:18`
|
||||||
- `GameController`는 DB 게임이 없으면 `GameCatalog` fallback을 시도한다. `src/main/java/com/pandoli365/bibimbap/controller/api/GameController.java:111`, `src/main/java/com/pandoli365/bibimbap/controller/api/GameController.java:116`
|
- `GameController`는 DB 게임이 없으면 `GameCatalog` fallback을 시도한다. `src/main/java/com/pandoli365/bibimbap/controller/api/GameController.java:111`, `src/main/java/com/pandoli365/bibimbap/controller/api/GameController.java:116`
|
||||||
|
|
||||||
|
구현 결과 (2026-06-29, sid 20260629-175705):
|
||||||
|
|
||||||
|
- `abstracts/` 패키지 4파일(`Service`, `Request`, `Result`, `ErrorResult`) 전체 삭제. 참조 0 확인 후 제거.
|
||||||
|
- `src/main/webapp/WEB-INF/jsp/fragments/header.jspf` 삭제. include 참조 0 확인 후 제거.
|
||||||
|
- `src/main/java/com/pandoli365/bibimbap/game/GameCatalog.java` 삭제.
|
||||||
|
- `GameController.gameDetail()` 의 GameCatalog fallback(redirect:/+정적뷰) 제거 → DB 미존재 게임 ID = **HTTP 404** (`ResponseStatusException(NOT_FOUND)`). 사용자 확정 결정(D-B2-404).
|
||||||
|
- `ApiExceptionControllerAdvice` 에 `ResponseStatusException` 핸들러 추가 — 기존 `Exception` catch-all 이 500으로 덮던 문제 수정, 404 status 보존.
|
||||||
|
- 회귀 가드 `GameLikeControllerTest.gameDetailThrowsNotFoundWhenGameMissing` 추가.
|
||||||
|
- `mvn test` 361 PASS. 런타임 스모크: `GET /game/99999` = 404, `GET /game/3` = 200 확인.
|
||||||
|
|
||||||
체크리스트:
|
체크리스트:
|
||||||
|
|
||||||
- [ ] `rg "abstracts|GameCatalog|header.jspf"`로 실제 참조를 재확인한다.
|
- [x] `rg “abstracts|GameCatalog|header.jspf”`로 실제 참조를 재확인한다.
|
||||||
- [ ] `abstracts` 패키지를 삭제해도 컴파일이 깨지지 않는지 확인한다.
|
- [x] `abstracts` 패키지를 삭제해도 컴파일이 깨지지 않는지 확인한다.
|
||||||
- [ ] `header.jspf` 삭제 전 JSP include 경로가 전부 `/WEB-INF/views/header.jsp`인지 확인한다.
|
- [x] `header.jspf` 삭제 전 JSP include 경로가 전부 `/WEB-INF/views/header.jsp`인지 확인한다.
|
||||||
- [ ] `GameCatalog` fallback 제거 시 없는 게임 ID의 기대 동작을 `redirect:/` 또는 404로 결정한다.
|
- [x] `GameCatalog` fallback 제거 시 없는 게임 ID의 기대 동작을 `redirect:/` 또는 404로 결정한다. → **HTTP 404 확정**.
|
||||||
- [ ] 삭제 PR에는 기능 변경이 없도록 테스트와 수동 확인 범위를 좁힌다.
|
- [x] 삭제 PR에는 기능 변경이 없도록 테스트와 수동 확인 범위를 좁힌다.
|
||||||
- [ ] 문서에서 제거된 프로토타입 흐름을 최신 구조로 갱신한다.
|
- [x] 문서에서 제거된 프로토타입 흐름을 최신 구조로 갱신한다. → analysis 문서 stale 항목 갱신 완료.
|
||||||
|
|
||||||
완료 조건:
|
완료 조건:
|
||||||
|
|
||||||
- [ ] dead code 파일이 제거되거나 “보존 이유”가 문서화된다.
|
- [x] dead code 파일이 제거되거나 “보존 이유”가 문서화된다. → abstracts 4파일·header.jspf·GameCatalog.java 삭제.
|
||||||
- [ ] `mvn test` 또는 최소 컴파일 검증이 통과한다.
|
- [x] `mvn test` 또는 최소 컴파일 검증이 통과한다. → 361 PASS.
|
||||||
- [ ] 없는 게임 상세 접근의 동작이 명확하다.
|
- [x] 없는 게임 상세 접근의 동작이 명확하다. → **HTTP 404** (ResponseStatusException(NOT_FOUND)).
|
||||||
|
|
||||||
|
> 구현 이력: [changes/2026-06-29-b2-b4-fe-hardening.md](../changes/2026-06-29-b2-b4-fe-hardening.md)
|
||||||
|
|
||||||
## B3. 좋아요/댓글 서버 영속화 연결
|
## B3. 좋아요/댓글 서버 영속화 연결
|
||||||
|
|
||||||
|
|
@ -131,24 +143,36 @@
|
||||||
- MyBatis TRACE 로그가 켜져 있다. `src/main/resources/application.properties:25`, `src/main/resources/application.properties:26`, `src/main/resources/application.properties:27`
|
- MyBatis TRACE 로그가 켜져 있다. `src/main/resources/application.properties:25`, `src/main/resources/application.properties:26`, `src/main/resources/application.properties:27`
|
||||||
- multipart 상한이 1GB다. `src/main/resources/application.properties:15`, `src/main/resources/application.properties:16`, `src/main/resources/application.properties:17`, `src/main/resources/application.properties:18`
|
- multipart 상한이 1GB다. `src/main/resources/application.properties:15`, `src/main/resources/application.properties:16`, `src/main/resources/application.properties:17`, `src/main/resources/application.properties:18`
|
||||||
|
|
||||||
|
구현 결과 (2026-06-29, sid 20260629-175705):
|
||||||
|
|
||||||
|
- **Spring Boot**: `3.5.14-SNAPSHOT` → **`3.5.16` (GA)** 고정. `pom.xml` spring-boot.version 갱신.
|
||||||
|
- **Snapshot repo 제거**: `pom.xml` 의 spring-snapshots repository·pluginRepository 블록 삭제. snapshot 없이 `mvn -B dependency:resolve` PASS 확인.
|
||||||
|
- **쿠키 하드닝**: `application.properties` 에 `server.servlet.session.cookie.http-only=true`, `server.servlet.session.cookie.same-site=lax` 추가. `application-live.properties`(신규) 에 `server.servlet.session.cookie.secure=true`. 런타임 `Set-Cookie` 확인: `JSESSIONID; HttpOnly; SameSite=Lax`.
|
||||||
|
- **MyBatis 로그**: `application.properties` ibatis TRACE → **WARN**. `application-dev.properties`(신규) 에 TRACE 복원(로컬 개발 전용).
|
||||||
|
- **Profile properties**: Spring 표준 `application-dev.properties`·`application-live.properties` 신규 생성. 기존 `db.properties`(DB 전용) 공존. 사용자 합의(D-B4-cfgloc).
|
||||||
|
- **OWASP Dependency-Check**: `pom.xml` 에 `dependency-check-maven 12.2.2` plugin 추가. executions 없음(빌드 phase 비bind). 수동 실행: `mvn dependency-check:check`. autoUpdate=false, failBuildOnCVSS=7, skipProvidedScope=true. 상세: [security/owasp-dependency-check-guide.md](./owasp-dependency-check-guide.md).
|
||||||
|
- **multipart 1GB 유지**: 사용자 확정(D-B4-multipart). 리스크 문서화: [security/multipart-size-risk.md](./multipart-size-risk.md).
|
||||||
|
|
||||||
체크리스트:
|
체크리스트:
|
||||||
|
|
||||||
- [ ] Spring Boot를 안정 release 버전으로 고정한다.
|
- [x] Spring Boot를 안정 release 버전으로 고정한다. → **3.5.16 GA**.
|
||||||
- [ ] snapshot repository와 pluginRepository 필요성을 제거하거나 문서화한다.
|
- [x] snapshot repository와 pluginRepository 필요성을 제거하거나 문서화한다. → spring-snapshots 블록 삭제.
|
||||||
- [ ] `server.servlet.session.cookie.http-only=true`를 설정한다.
|
- [x] `server.servlet.session.cookie.http-only=true`를 설정한다.
|
||||||
- [ ] live 프로필에서 `server.servlet.session.cookie.secure=true`를 설정한다.
|
- [x] live 프로필에서 `server.servlet.session.cookie.secure=true`를 설정한다. → `application-live.properties`.
|
||||||
- [ ] `server.servlet.session.cookie.same-site=lax` 또는 stricter 정책을 결정한다.
|
- [x] `server.servlet.session.cookie.same-site=lax` 또는 stricter 정책을 결정한다. → **lax** 설정.
|
||||||
- [ ] 운영 로그에서 MyBatis TRACE를 낮추고 민감 파라미터 노출 가능성을 확인한다.
|
- [x] 운영 로그에서 MyBatis TRACE를 낮추고 민감 파라미터 노출 가능성을 확인한다. → base WARN, dev 전용 TRACE.
|
||||||
- [ ] multipart 상한을 실제 운영 허용치와 맞춘다.
|
- [x] multipart 상한을 실제 운영 허용치와 맞춘다. → **1GB 유지(사용자 확정), 리스크 문서화 완료**.
|
||||||
- [ ] 의존성 CVE 스캔 도구를 정한다. 후보: Dependabot, OWASP Dependency-Check, Maven Versions Plugin.
|
- [x] 의존성 CVE 스캔 도구를 정한다. → **OWASP Dependency-Check maven plugin**.
|
||||||
- [ ] CVE 스캔 결과와 예외 처리 기준을 `docs/security/`에 기록한다.
|
- [x] CVE 스캔 결과와 예외 처리 기준을 `docs/security/`에 기록한다. → [security/owasp-dependency-check-guide.md](./owasp-dependency-check-guide.md).
|
||||||
|
|
||||||
완료 조건:
|
완료 조건:
|
||||||
|
|
||||||
- [ ] 빌드가 snapshot repository 없이 재현 가능하다.
|
- [x] 빌드가 snapshot repository 없이 재현 가능하다. → `mvn -B dependency:resolve` PASS.
|
||||||
- [ ] 운영 쿠키에 HttpOnly/Secure/SameSite 정책이 반영된다.
|
- [x] 운영 쿠키에 HttpOnly/Secure/SameSite 정책이 반영된다. → 런타임 `Set-Cookie` 확인 완료.
|
||||||
- [ ] CVE 스캔 결과가 문서화된다.
|
- [x] CVE 스캔 결과가 문서화된다. → OWASP DC plugin 설정 + guide 문서화.
|
||||||
- [ ] 운영 로그 레벨이 민감 데이터 노출을 최소화한다.
|
- [x] 운영 로그 레벨이 민감 데이터 노출을 최소화한다. → base WARN.
|
||||||
|
|
||||||
|
> 구현 이력: [changes/2026-06-29-b2-b4-fe-hardening.md](../changes/2026-06-29-b2-b4-fe-hardening.md)
|
||||||
|
|
||||||
## 공통 테스트 매트릭스
|
## 공통 테스트 매트릭스
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
# 2026-06-29 B2·B4·FE 작업 세션 핸드오프
|
||||||
|
|
||||||
|
**sid**: 20260629-175705 (resumed from 20260629-174807)
|
||||||
|
**완료 상태**: 3트랙 모두 완료·검증
|
||||||
|
**브랜치**: feat/v2
|
||||||
|
**최종 테스트**: `mvn test` 361 PASS
|
||||||
|
|
||||||
|
## 완료된 트랙
|
||||||
|
|
||||||
|
### B2 — 프로토타입 dead code 제거
|
||||||
|
|
||||||
|
- abstracts/ 패키지 4파일 삭제.
|
||||||
|
- fragments/header.jspf 삭제.
|
||||||
|
- game/GameCatalog.java 삭제.
|
||||||
|
- GameController.gameDetail() fallback 제거 → 없는 게임 ID = HTTP 404.
|
||||||
|
- ApiExceptionControllerAdvice에 ResponseStatusException 핸들러 추가(404 status 보존).
|
||||||
|
- GameLikeControllerTest.gameDetailThrowsNotFoundWhenGameMissing 추가.
|
||||||
|
- 런타임 스모크: /game/99999=404, /game/3=200.
|
||||||
|
|
||||||
|
### B4 — 의존성/세션/운영 하드닝
|
||||||
|
|
||||||
|
- Spring Boot 3.5.14-SNAPSHOT → 3.5.16 GA.
|
||||||
|
- pom에서 spring-snapshots repository·pluginRepository 제거.
|
||||||
|
- OWASP dependency-check-maven 12.2.2 비bind 추가.
|
||||||
|
- application.properties: ibatis TRACE→WARN, HttpOnly·SameSite=Lax 추가.
|
||||||
|
- application-dev.properties 신규(ibatis TRACE).
|
||||||
|
- application-live.properties 신규(cookie.secure=true).
|
||||||
|
- 런타임 Set-Cookie 확인 완료.
|
||||||
|
|
||||||
|
### FE — 프론트엔드 디자인 개선
|
||||||
|
|
||||||
|
- index 검색 `<details>` 접이식(prefill 시 open).
|
||||||
|
- 다크모드 input 테두리 대비 강화(login·signup·index·game-detail).
|
||||||
|
- 체크박스 터치타깃 1.15rem(login·signup).
|
||||||
|
- 브라우저 스모크 확인.
|
||||||
|
|
||||||
|
## 주요 결정
|
||||||
|
|
||||||
|
| ID | 결정 |
|
||||||
|
| --- | --- |
|
||||||
|
| D-B2-404 | 없는 게임 ID = HTTP 404 (ResponseStatusException(NOT_FOUND)). 사용자 확정. |
|
||||||
|
| D-B4-cve | CVE 스캔 = OWASP Dependency-Check 비bind. autoUpdate=false, CVSS>=7 실패. 사용자 확정. |
|
||||||
|
| D-B4-multipart | multipart 1GB 유지. 리스크 문서화: docs/security/multipart-size-risk.md. 사용자 확정. |
|
||||||
|
| D-B4-cfgloc | application-{profile}.properties 신규. db.properties 공존. |
|
||||||
|
| D-test-mockfix | BibimbapApplicationTests GameLikesMapper @MockBean 누락(B3 사전결함) 1줄 보강. |
|
||||||
|
|
||||||
|
## 이월 없음
|
||||||
|
|
||||||
|
이번 세션의 open_items(dev override online prefetch 함정)는 development/local-dev-setup.md에 노트 추가 완료.
|
||||||
|
|
||||||
|
## 참조
|
||||||
|
|
||||||
|
- 구현 이력: [changes/2026-06-29-b2-b4-fe-hardening.md](../changes/2026-06-29-b2-b4-fe-hardening.md)
|
||||||
|
- 보안 체크리스트: [security/security-remediation-checklist.md](../security/security-remediation-checklist.md)
|
||||||
|
- OWASP DC: [security/owasp-dependency-check-guide.md](../security/owasp-dependency-check-guide.md)
|
||||||
|
- Multipart 리스크: [security/multipart-size-risk.md](../security/multipart-size-risk.md)
|
||||||
|
|
@ -8,3 +8,4 @@
|
||||||
- [2026-06-17-w3-feature-skeletons.md](./2026-06-17-w3-feature-skeletons.md) — W3(사이트 플랫폼) 5개 서브기능 골자 카탈로그. 기능당 목적·핵심동작·결합/의존·미결질문·후속진입점(윤곽 깊이, 미결 해소 안 함). 코드 현황 대조(tag/review 테이블 신규·Interceptor 미구현·`/game/**` 핸들러 미등록). 결합표 + 착수순서 제언(W3-2 1순위) + QG-1~3. 당장 구현 아님. `status: confirmed`(20260617-174635 세션서 5기능+착수순서 항목별 합의), source_session: 20260617-172407. ⚠️ **코드 현황 #2·#3 stale (2026-06-23 정정)** — w2-w4-skeletons 참조.
|
- [2026-06-17-w3-feature-skeletons.md](./2026-06-17-w3-feature-skeletons.md) — W3(사이트 플랫폼) 5개 서브기능 골자 카탈로그. 기능당 목적·핵심동작·결합/의존·미결질문·후속진입점(윤곽 깊이, 미결 해소 안 함). 코드 현황 대조(tag/review 테이블 신규·Interceptor 미구현·`/game/**` 핸들러 미등록). 결합표 + 착수순서 제언(W3-2 1순위) + QG-1~3. 당장 구현 아님. `status: confirmed`(20260617-174635 세션서 5기능+착수순서 항목별 합의), source_session: 20260617-172407. ⚠️ **코드 현황 #2·#3 stale (2026-06-23 정정)** — w2-w4-skeletons 참조.
|
||||||
- [2026-06-23-w2-w4-full-design-summary.md](./2026-06-23-w2-w4-full-design-summary.md) — **W2+W3(잔여)+W4 풀설계 통합 요약/설계 인덱스**. 11기능(W2-1~6·W3-1/3-3/3-4/3-5·W4) 풀설계(W1-design 깊이, 오픈질문 0) 진입 인덱스 + 크로스-W 결정 매트릭스(정석 확정) + 교차정합 감사 verdict(HIGH 1 해소·동결 단일권위 유지) + 골자 stale 정정 3건 + 착수순서. 설계 본문은 `.atp/work-session/20260623-104307/implementation/`. 설계 전용(코드 0줄). `status: active`, source_session: 20260623-104307, resumed_from: 20260622-180054.
|
- [2026-06-23-w2-w4-full-design-summary.md](./2026-06-23-w2-w4-full-design-summary.md) — **W2+W3(잔여)+W4 풀설계 통합 요약/설계 인덱스**. 11기능(W2-1~6·W3-1/3-3/3-4/3-5·W4) 풀설계(W1-design 깊이, 오픈질문 0) 진입 인덱스 + 크로스-W 결정 매트릭스(정석 확정) + 교차정합 감사 verdict(HIGH 1 해소·동결 단일권위 유지) + 골자 stale 정정 3건 + 착수순서. 설계 본문은 `.atp/work-session/20260623-104307/implementation/`. 설계 전용(코드 0줄). `status: active`, source_session: 20260623-104307, resumed_from: 20260622-180054.
|
||||||
- [2026-06-23-w2-w4-feature-skeletons.md](./2026-06-23-w2-w4-feature-skeletons.md) — W2(게임잼 6서브: 잼엔티티/심사위원역할/평가동결/심사평가/인기투표/시상집계) + W4(유저 배지/평판) 골자 카탈로그. W3 skeletons 포맷(목적·핵심동작·결합/의존[code-fact vs 해석 태깅]·미결질문[비해소]·후속진입점) + 결합표 + 착수순서 + QG-W2-A~D·W4-A. **W3 skeletons "코드 현황" stale 2건 정정**(RBAC 게이트·리뷰 인프라 실재 — 단 POST_WRITE/GAME_JAM_MANAGE enforcement 미연결, 리뷰는 하이브리드 6축+VIEW). code grounding: `.atp/work-session/20260623-104307/research/W2-W4-grounding.md`. `status: draft`(합의 전), source_session: 20260623-104307.
|
- [2026-06-23-w2-w4-feature-skeletons.md](./2026-06-23-w2-w4-feature-skeletons.md) — W2(게임잼 6서브: 잼엔티티/심사위원역할/평가동결/심사평가/인기투표/시상집계) + W4(유저 배지/평판) 골자 카탈로그. W3 skeletons 포맷(목적·핵심동작·결합/의존[code-fact vs 해석 태깅]·미결질문[비해소]·후속진입점) + 결합표 + 착수순서 + QG-W2-A~D·W4-A. **W3 skeletons "코드 현황" stale 2건 정정**(RBAC 게이트·리뷰 인프라 실재 — 단 POST_WRITE/GAME_JAM_MANAGE enforcement 미연결, 리뷰는 하이브리드 6축+VIEW). code grounding: `.atp/work-session/20260623-104307/research/W2-W4-grounding.md`. `status: draft`(합의 전), source_session: 20260623-104307.
|
||||||
|
- [2026-06-29-b2-b4-fe-session.md](./2026-06-29-b2-b4-fe-session.md) — B2(dead code 제거)·B4(의존성/세션 하드닝)·FE(프론트 개선) 3트랙 완료 핸드오프. 주요 결정 5건(404/OWASP 비bind/multipart 유지/profile properties/mockfix). mvn test 361 PASS. 이월 없음.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue