docs(w4): 배지표시·하드닝 변경이력 + L3 배포후 체크리스트 + 회고 교훈 3건

- changes 2026-06-29-w3-w4: 후속 세션(배지 배선·하드닝 b1/b2·DDL 적용완료·L3 이월) 기록
- maintenance/post-deploy-verification-checklist.md 신설: L3 deferred 5기능 +
  b2 자산 수동이전 절차(static/profile/8 → ~/.bibimbap/uploads)
- verification-strategies: 교훈 3건(needs 결정분기 구조화·표시배선 후행 독립트랙
  실증·런타임 의존 L1/L3 레이어 분리)

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:
이정수 2026-06-29 15:01:27 +09:00
parent 9041bb7c70
commit a4866b6420
5 changed files with 183 additions and 9 deletions

View File

@ -1,16 +1,21 @@
---
kind: change
title: "W3(잔여) + W4 기능 구현 (W3-1·W3-4·W3-3·W3-5·W4)"
title: "W3(잔여) + W4 기능 구현 + 배지 표시 배선 + 보안 하드닝 b1/b2 (W3-1·W3-4·W3-3·W3-5·W4 + 20260629-142115)"
session_id: 20260629-100849
followup_session_id: 20260629-142115
created_at: 2026-06-29
status: implemented
related_design_summary: "../work-log/2026-06-23-w2-w4-full-design-summary.md"
related_design: "../../.atp/work-session/20260623-104307/implementation/"
related_impl: "../../.atp/work-session/20260629-100849/implementation/"
related_followup_impl: "../../.atp/work-session/20260629-142115/implementation/"
related_verification: "../../.atp/work-session/20260629-100849/verification.md"
related_followup_verification: "../../.atp/work-session/20260629-142115/verification.md"
related_report: "../../.atp/work-session/20260629-100849/report.md"
related_followup_report: "../../.atp/work-session/20260629-142115/report.md"
related_security_checklist: "../security/security-remediation-checklist.md"
related_prev_change: "./2026-06-24-w2-jam-platform.md"
related_post_deploy: "../maintenance/post-deploy-verification-checklist.md"
---
# W3(잔여) + W4 기능 구현 변경 이력 (W3-1·W3-4·W3-3·W3-5·W4)
@ -211,8 +216,57 @@ L1 347/347 GREEN(baseline 318→347, BadgeServiceTest 12 + BadgeManageController
| 항목 | 사유 |
|---|---|
| 실 dev DB(`bibimbap`) DDL 미적용 | L2 는 throwaway DB 로 통과했으나 실 dev DB 에는 미적용(파괴적 조작 회피). 신규 DDL 5종(tag/games-hub/board/game-upload/badge) **+ (W2 carryover) jam DDL 3종 = 총 8종** 을 `db/apply-local-ddl.sh` 로 일괄 적용 필요(§6 수동게이트). L3 선행. |
| L3 런타임 스모크(WAR 기동 후) | ① 검색 `/games/search` 태그·정렬·잼필터 + 방문수 dedupe ② 허브 `/` keyset 더보기 + 진행중 잼 배너 ③ 포스팅 작성(POST_WRITE)/sanitize 렌더/OG 미리보기/유니티 피드 폴링 + **★실 HTTPS 외부 fetch 동작**(option-b hostname-connect 실 TLS — 단위는 HTTP만) ④ 업로드 zip-slip/포맷 거부 + 정상 Unity 빌드 + replaceUuid 교체 ⑤ 배지 자동부여(리뷰·업로드)·수동 grant/revoke(BADGE_MANAGE)·리뷰작성자 칩. 자동 미수행. |
| **W4 표시 deferral** | 리뷰작성자 배지 경로만 구현. **프로필 myBadges 주입·게임카드 creator 배지 미구현**(의도된 deferral). 게임카드 SSR 은 `WebMvcController` hub + `SearchController` 가 담당(W3-4/타 W 산출물)이라 본 작업 범위 밖. 후속 작업에서 WebMvcController/SearchController 수정으로 creator 배지 부착 필요. profile.jsp 본인 배지는 구현됨. |
| W3-3 SSRF 잔여 rebinding TOCTOU | option-b hostname-connect 의 connect 시 재resolve 창(DNS 캐시로 최소화, 위협모델 LOW — URL 제출자=권한자). 하드닝 옵션: `networkaddress.cache.ttl` 설정 검토. |
| W3-5 배포 하드닝 | (1) **업로드 저장루트 static 트리 밖 이전**(LOW: static 트리 `.tmp` 노출 가능) (2) **gameRoot 경로 이중중첩 정합**(`app.upload.game-storage-path=src/main/resources/static/game` → `resolve('game')``.../static/game/game`). 설계 스코프-아웃(canonical 경계는 보장). dev 자산 위치 영향이라 사용자 config 결정 필요. |
| 실 dev DB(`bibimbap`) DDL 미적용 | L2 는 throwaway DB 로 통과했으나 실 dev DB 에는 미적용(파괴적 조작 회피). 신규 DDL 5종(tag/games-hub/board/game-upload/badge) **+ (W2 carryover) jam DDL 3종 = 총 8종** 을 `db/apply-local-ddl.sh` 로 일괄 적용 필요(§6 수동게이트). L3 선행. → **[해소됨]** 아래 §후속 세션(20260629-142115) 참조. |
| L3 런타임 스모크(WAR 기동 후) | ① 검색 `/games/search` 태그·정렬·잼필터 + 방문수 dedupe ② 허브 `/` keyset 더보기 + 진행중 잼 배너 ③ 포스팅 작성(POST_WRITE)/sanitize 렌더/OG 미리보기/유니티 피드 폴링 + **★실 HTTPS 외부 fetch 동작**(option-b hostname-connect 실 TLS — 단위는 HTTP만) ④ 업로드 zip-slip/포맷 거부 + 정상 Unity 빌드 + replaceUuid 교체 ⑤ 배지 자동부여(리뷰·업로드)·수동 grant/revoke(BADGE_MANAGE)·리뷰작성자 칩. → **[배포 후 과제로 이월]** 상세 체크리스트: [maintenance/post-deploy-verification-checklist.md](../maintenance/post-deploy-verification-checklist.md). |
| **W4 표시 deferral** | 리뷰작성자 배지 경로만 구현. **프로필 myBadges 주입·게임카드 creator 배지 미구현**(의도된 deferral). 게임카드 SSR 은 `WebMvcController` hub + `SearchController` 가 담당(W3-4/타 W 산출물)이라 본 작업 범위 밖. → **[해소됨]** 아래 §후속 세션(20260629-142115) 참조. |
| W3-3 SSRF 잔여 rebinding TOCTOU | option-b hostname-connect 의 connect 시 재resolve 창(DNS 캐시로 최소화, 위협모델 LOW — URL 제출자=권한자). 하드닝 옵션: `networkaddress.cache.ttl` 설정 검토. → **[best-effort 해소]** 아래 §후속 세션(20260629-142115) 참조. |
| W3-5 배포 하드닝 | (1) **업로드 저장루트 static 트리 밖 이전**(LOW: static 트리 `.tmp` 노출 가능) (2) **gameRoot 경로 이중중첩 정합**(`app.upload.game-storage-path=src/main/resources/static/game` → `resolve('game')``.../static/game/game`). 설계 스코프-아웃(canonical 경계는 보장). dev 자산 위치 영향이라 사용자 config 결정 필요. → **[해소됨]** 아래 §후속 세션(20260629-142115) 참조. |
---
## 후속 세션 (20260629-142115) — 배지 표시 배선 완료 + 보안 하드닝 b1/b2
관련 세션: `.atp/work-session/20260629-142115/report.md` | 구현 소유권: `.atp/work-session/20260629-142115/implementation/` | 검증: `.atp/work-session/20260629-142115/verification.md`
### 작업 1 — DB DDL 사용자 직접 적용 (no-op 기록)
직전 세션 needs_user_verification "실 dev DB DDL 미적용" 항목에 대해 **사용자가 직접 실 dev DB 에 신규 8종(tag/games-hub/board/game-upload/badge DDL 5종 + W2 jam DDL 3종)을 적용 완료**한 것을 보고 수신. 이 항목은 해소됨으로 처리. 코드/스키마 변경 없음.
### 작업 2 — W4 배지 표시면 배선 (`3d10449`)
| 서브 | 내용 |
|---|---|
| `WebMvcController` | `UserBadgesQueryMapper` 주입 → hub 게임카드 `creatorBadges` model attr 추가 |
| `SearchController` | `UserBadgesQueryMapper` 주입 → 검색 결과 게임카드 `creatorBadges` model attr 추가 |
| `index.jsp` | 게임카드 creator 배지 칩 표시 (hub + 검색 결과 통합) |
| `GameReviewController` | authorBadges 정본 패턴 이미 존재 — 패턴 답습, 신규 SQL 0 |
| profile.jsp | `myBadges` 주입 표시 (이전 세션에서 구현 완료 확인, 이번 배선으로 완전 연결) |
- 신규 SQL 0. 기존 `UserBadgesQueryMapper.listActiveBadgeKeysByUserIds` 배치쿼리 재사용(N+1 차단).
- L1 352/352 GREEN(baseline 347→352, +5 WebMvcControllerTest/SearchControllerTest 신규). 회귀 0.
- 직전 세션 "W4 표시 deferral" 완전 해소.
### 작업 4 — 보안 하드닝 b1/b2 (`9041bb7`)
#### b1 — SsrfSafeFetcher DNS rebinding TOCTOU 완화
- `@PostConstruct initDnsCachePolicy()` 추가: `java.security.Security.setProperty("networkaddress.cache.ttl", "30")`.
- **best-effort**: @PostConstruct 가 첫 InetAddress 조회 이전에 실행되면 유효. 결정적 보장이 필요하면 JVM 레벨(`$JAVA_HOME/conf/security/java.security`)이 정본.
- 소스 내 주석으로 한계 안내 명시(AC-b1-2 정적 검증 PASS).
- L3 스모크 확인 항목 추가: 앱 기동 후 `java.security.Security.getProperty("networkaddress.cache.ttl")=="30"` 확인 — 미반영 시 JVM 레벨 java.security 설정으로 격상. 상세: [maintenance/post-deploy-verification-checklist.md](../maintenance/post-deploy-verification-checklist.md).
#### b2 — 업로드 저장루트 static 서빙 트리 밖 이전
- `application.properties`: `app.upload.game-storage-path=${user.home}/.bibimbap/uploads` 로 변경.
- 효과: 업로드물이 static 서빙 트리 밖에 저장 → 웹서버 직접 노출 차단, 컨트롤러 권한게이트 경유 강제.
- **수동 이전 필요**: `src/main/resources/static/profile/8/*`(user 8 프로필 이미지)를 `~/.bibimbap/uploads/profile/8/` 로 수동 이전 필요. 미이전 시 user 8 프로필 이미지 404. `static/game` 은 비어있어 게임 자산 이전 불필요.
- 상세 절차: [maintenance/post-deploy-verification-checklist.md](../maintenance/post-deploy-verification-checklist.md) §자산 수동 이전.
- L1 353/353 GREEN(baseline 352→353, SsrfSafeFetcherTest.initDnsCachePolicySetsPositiveTtl 신규 1). 회귀 0.
### 검증 결과
최종 `./mvnw -o test` **353/353 GREEN**(Tests run: 353, Failures: 0, Errors: 0, Skipped: 0). 전 항목 pass. 상세: `.atp/work-session/20260629-142115/verification.md`.
### 이월 (배포 후 과제)
L3 런타임 스모크 전체 + b2 자산 수동 이전 → [maintenance/post-deploy-verification-checklist.md](../maintenance/post-deploy-verification-checklist.md) 로 이월.

View File

@ -6,5 +6,5 @@
- [2026-06-18-w3-2-comments-reviews.md](./2026-06-18-w3-2-comments-reviews.md) — W3-2 댓글/리뷰 분리 구현. game_comments 서버 영속화 전환 + game_reviews 도메인 신설. 신규 API 9개(댓글 C1~C4, 리뷰 R1~R5), DDL 2종, 권한(작성자/운영자), cascade 확장. L1 31테스트 PASS. L3 스모크·DDL 적용은 needs_user_verification. 좋아요는 범위밖.
- [2026-06-22-w3-2-comments-reviews-enhancement.md](./2026-06-22-w3-2-comments-reviews-enhancement.md) — W3-2 고도화(코어 위에 얹음). 다축 평점(game_review_axes 6축·육각형 SVG 레이더) + A1~A3 일관성(commentView 통일·작성자 마스킹 QG-2 해결·edited/updatedAt) + B1~B3 목록규모(페이지네이션·본문10자·TextNormalizer) + C1~C6 UX + game_review_stats 집계뷰(C3 재분류 — W2-3 동결 무관 확정). 신규 4파일+변경 13. L1 43/43 GREEN. DDL·L3 스모크 needs_user_verification.
- [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). 태그+검색(tags/game_tags/jam_tags/game_views·다중태그 AND/OR·정렬키6·방문수 dedupe·CONTENT_MODERATE) + 메인허브(keyset 혼합방향 OR분해·진행중 잼 배너) + 포스팅보드(posts/categories·POST_WRITE enforcement·commonmark/jsoup sanitize·SsrfSafeFetcher 9항목 option-b·@Scheduled 유니티피드) + 업로드보안(ZipSecurity zip-slip/심링크/zipbomb·ATOMIC_MOVE·game_upload_audit_log) + 배지/평판(badges/user_badges/reputation_events·자동임계+수동 BADGE_MANAGE 하이브리드·리뷰작성자 칩). 신규 DDL 5종 + pom jsoup/commonmark. 5 커밋 35f1dc3~305cc73. 최종 L1 347/347 GREEN(회귀 0), L2 dev DB contract 전 PASS(격리 throwaway DB), adversarial SSRF/zip-slip 감사(W3-3 vacuous SSRF 포착→option-b 전환). 실 dev DB DDL 8종 일괄 적용·L3 스모크·W4 표시 deferral(게임카드 creator 배지) needs_user_verification.
- [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-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.

View File

@ -203,7 +203,7 @@ SSRF·zip-slip·경로 boundary·sanitize 같은 보안핵심은 verification-ad
기능 A 의 UI 표시가 선행 기능 B 의 컨트롤러/뷰(예: 목록·카드 SSR)에 배선돼야 할 때, "B 산출물 접근 금지" scope-fence 를 그대로 두면 A 의 표시 AC 가 미완으로 남는다. orchestrator 는 표시면 의존을 사전 식별해 **해당 컨트롤러를 A 의 명시적 편집 허용 대상으로 fence 예외 선언**하거나, 표시 AC 를 의도적 deferral 로 분리·문서화한다.
> 근거: W4 세션(20260629) — 배지 표시 AC(프로필·게임카드)가 WebMvcController/SearchController(W3-1/3-4) SSR 에 의존하나 scope-fence 로 차단 → 리뷰작성자 칩만 구현, 프로필·게임카드는 deferral 로 분리.
> 근거: W4 세션(20260629) — 배지 표시 AC(프로필·게임카드)가 WebMvcController/SearchController(W3-1/3-4) SSR 에 의존하나 scope-fence 로 차단 → 리뷰작성자 칩만 구현, 프로필·게임카드는 deferral 로 분리. **후속 세션(20260629-142115)에서 그 deferral 을 독립 트랙으로 처리(컨트롤러 매퍼주입 + index.jsp 칩렌더, 5파일 352/352 GREEN) → 완전 해소.** fence 예외 선언과 후행 독립 트랙 분리가 **동등하게 유효한 두 경로**임이 실증됨 — 선행 기능과 충돌 자원이 없으면 후행 분리가 롤백 경계도 깔끔하다.
### 설계 트레이드오프 concern 은 1차 결론까지 못박기
@ -211,6 +211,18 @@ SSRF·zip-slip·경로 boundary·sanitize 같은 보안핵심은 verification-ad
> 근거: W3-3 SSRF — 설계 concern 이 "IP핀닝 또는 connect후 peer검증" 을 열어둠 → 1차가 IP핀닝(Host override) 선택, HTTPS SNI 깨짐 + restricted-header 미검증. fix1 에서 option-b(hostname-connect)로 전환.
### needs_user_verification 은 "미완료 목록" 이 아니라 "결정 분기" 단위로 구조화
세션 종료 시 `needs_user_verification` 을 단순 잔여 작업 나열로 적으면, 다음 세션이 각 항목마다 "어떻게 할까요" 재질의로 시작한다. 대신 각 항목을 **사용자가 한 번에 고를 수 있는 결정 분기**(예: 직접 적용 / 지금 구현 / 배포 후 이월 / 추가 하드닝)로 미리 구조화하면, 다음 세션 착수 시 결정이 1라운드에 수렴하고 재질의가 0이 된다. 항목마다 "기본 가정값 + 영향 범위" 를 병기한다(§2.2 open_questions 규약과 정합).
> 근거: 세션 20260629-142115 — 직전 세션이 needs 4건을 결정 분기로 명시 → 사용자 4건 일괄 결정, 재질의 0, 모든 분기 첫 라운드 수렴.
### 런타임/환경 의존 보장은 L1(값 확인)과 L3(실반영) 레이어로 분리
설정값이 "코드에서 세팅되는가" 와 "런타임에 실제 반영되는가" 가 다른 레이어인 경우(예: `Security.setProperty("networkaddress.cache.ttl")``InetAddressCachePolicy` lazy init 경합으로 best-effort), 단위검증으로 **값 설정만** 확인하고 **실제 런타임 반영은 L3 스모크**로 분리 배정한다. best-effort 한계와 결정적 정본(예: JVM `$JAVA_HOME/conf/security/java.security`)을 코드 주석·체크리스트에 함께 명시해, "값=설정됨" 을 "동작=보장됨" 으로 오인하지 않게 한다.
> 근거: 세션 20260629-142115 b1 — `SsrfSafeFetcher.initDnsCachePolicy()` 단위검증은 `Security.getProperty=="30"` 만 확인(L1), 실 rebinding 방어 반영은 배포 후 체크리스트(L3)로 이월. 런타임 미반영 시 JVM 레벨 격상 경로 명시.
---
## 프로토콜 개선 권고 (외부 번들 — 미적용)

View File

@ -4,4 +4,4 @@
## 목록
_(아직 문서 없음)_
- [post-deploy-verification-checklist.md](./post-deploy-verification-checklist.md) — 배포 후 검증 체크리스트(L3 deferred). W3/W4 + 하드닝 b1/b2 배포 후 수동 확인 항목: 검색/허브/포스팅/업로드/배지/SSRF ttl 런타임 반영 + b2 자산 수동 이전 절차(user 8 프로필 `~/.bibimbap/uploads/profile/8/`). DB DDL 8종 적용 완료(사용자 직접 적용) 기록 포함.

View File

@ -0,0 +1,108 @@
---
kind: maintenance
title: "배포 후 검증 체크리스트 — W3/W4 + 하드닝 b1/b2 (L3 deferred)"
created_at: 2026-06-29
session_id: 20260629-142115
status: pending
related_change: "../changes/2026-06-29-w3-w4-features.md"
related_security_checklist: "../security/security-remediation-checklist.md"
---
# 배포 후 검증 체크리스트 — W3/W4 + 하드닝 b1/b2
L3 런타임 스모크는 세션 20260629-100849 및 20260629-142115 에서 자동 수행하지 않고 **배포 후 과제로 이월**한 항목이다.
이 문서는 실 dev 환경(WAR 기동 + 실 DB) 에서 수동 확인해야 할 체크리스트다.
관련 변경이력: [2026-06-29-w3-w4-features.md](../changes/2026-06-29-w3-w4-features.md).
---
## 사전 조건
배포 전 아래 항목이 완료되어야 한다.
- [x] **DB DDL 8종 적용 완료** (사용자 보고 수신 2026-06-29): tag/games-hub/board/game-upload/badge DDL 5종 + W2 jam DDL 3종을 `db/apply-local-ddl.sh` 로 실 dev DB 에 일괄 적용.
- [ ] **b2 자산 수동 이전** — 아래 §자산 수동 이전 참조. WAR 기동 전 수행 권장.
---
## §자산 수동 이전 (b2 신규 저장루트)
`application.properties` 변경(`app.upload.game-storage-path=${user.home}/.bibimbap/uploads`)으로 업로드 저장루트가 `static/` 트리 밖으로 이전됐다.
**이전이 필요한 자산:**
```
# 이전 경로 → 새 경로
src/main/resources/static/profile/8/ → ~/.bibimbap/uploads/profile/8/
```
- `static/profile/8/` 에 user 8 프로필 이미지가 존재할 경우 새 경로로 수동 이전 필요.
- **미이전 시**: user 8 프로필 이미지 404 발생.
- `static/game/` 은 비어있으므로 게임 자산 이전 불필요.
**이전 명령 예시 (디렉토리가 존재하는 경우):**
```bash
mkdir -p ~/.bibimbap/uploads/profile/8
cp -r src/main/resources/static/profile/8/. ~/.bibimbap/uploads/profile/8/
# 확인 후 원본 삭제(선택)
```
완료 후 이 체크리스트 항목에 체크:
- [ ] user 8 프로필 이미지 `~/.bibimbap/uploads/profile/8/` 에 존재 확인
---
## L3 런타임 스모크 체크리스트
WAR 기동 후 아래 기능을 수동으로 확인한다.
### 1. 검색 (`/games/search`)
- [ ] 태그 필터(단일/다중 AND/OR) 정상 동작
- [ ] 정렬키 6종(최신/조회수/평점/리뷰수/잼연관 등) 전환 확인
- [ ] 잼 필터(`?jam=slug`) 경유 잼 게임 목록 표시
- [ ] 방문수 dedupe — 동일 사용자 짧은 interval 내 재방문 시 view_count 미증가 확인
### 2. 허브 (`/`)
- [ ] keyset 페이징 더보기(스크롤/버튼) — 중복 없이 다음 페이지 로드
- [ ] 진행중 잼 배너 표시 (`JamsMapper.listActive` → `/games/search?jam=` 라우팅)
- [ ] **게임카드 creator 배지 칩 표시** (이번 세션 신규 — `WebMvcController` creatorBadges 주입)
### 3. 포스팅 보드
- [ ] 포스팅 작성 (POST_WRITE 권한 보유 계정으로 로그인 후 `/posts/new`)
- [ ] 마크다운 sanitize 렌더 — XSS 시도(`<script>`)
- [ ] OG 미리보기 (`OgPreviewService`) — 외부 URL 입력 후 og:title/og:image 렌더
- [ ] 유니티 피드 폴링 — `@Scheduled` 실행 확인(로그 또는 `unity_feed_items` 테이블 증가)
- [ ] **★실 HTTPS 외부 fetch** — option-b hostname-connect 실 TLS 동작 확인 (단위는 HTTP mock만 사용했으므로 실 환경 필수)
### 4. 업로드 보안 (Unity WebGL)
- [ ] zip-slip 시도 거부 확인 (`../` 경로 포함 zip 업로드 → 거부)
- [ ] 포맷 거부 — wasm/data/loader 파일 미포함 zip 거부
- [ ] 정상 Unity 빌드 업로드 + replaceUuid 경로 교체 성공
- [ ] **업로드물이 `~/.bibimbap/uploads/game/` 에 생성·서빙되는지 확인** (b2 신규 경로)
### 5. 배지 / 평판
- [ ] 배지 자동부여 — 리뷰 10건 작성 후 Reviewer 배지 자동 부여
- [ ] 배지 자동부여 — 게임 업로드 3건 후 Technician 배지 자동 부여
- [ ] 수동 grant/revoke — BADGE_MANAGE 권한 계정으로 `/manage/badges` 접근 + grant/revoke 동작
- [ ] 리뷰 작성자 배지 칩 — 게임 상세 리뷰 목록에서 리뷰어 배지 칩 표시
- [ ] **프로필 myBadges 표시** (이번 세션 신규 — `profile.jsp` 본인 배지 주입)
### 6. 보안 하드닝 b1 — SSRF DNS 캐시 TTL
- [ ] **★SSRF ttl 런타임 반영 확인**: 앱 기동 후 `java.security.Security.getProperty("networkaddress.cache.ttl")` 값이 `"30"` 인지 확인.
- 반영됨: `@PostConstruct initDnsCachePolicy()` best-effort 성공.
- **미반영 시 조치**: JVM 레벨 설정으로 격상 — `$JAVA_HOME/conf/security/java.security``networkaddress.cache.ttl=30` 추가. (결정적 보장은 JVM 레벨이 정본.)
---
## 완료 기준
위 체크리스트 전항목 체크 + §자산 수동 이전 완료 시 L3 스모크 통과.
결과를 이 문서에 직접 기록하거나 관련 work-log 에 append.