# SPRINT-006 QA Review — Iteration 1 - **검증일시:** 2026-04-04 15:10 KST - **검증자:** 다랑이 (Evaluator) - **결과:** ❌ FAILED (blocking 5건) ## 검증 항목 | 항목 | 결과 | |------|------| | npm install | ✅ | | prisma generate | ✅ | | npm test (backend) | ✅ 26/26 pass | | npm run build (backend) | ✅ | | npm run build (frontend) | ✅ 16 routes | ## 🔴 Blocking 이슈 ### B1: JWT_SECRET fallback 'changeme' — 3곳 (Critical) - **파일:** auth.service.ts:85, auth.module.ts:22, events.gateway.ts:48 - **문제:** JWT_SECRET 미설정 시 'changeme'로 토큰 서명 → 공격자가 유효한 JWT 위조 가능 - **모순:** jwt.strategy.ts는 throw하는데 나머지는 fallback → 불일치 - **수정:** 3곳 모두 `?? 'changeme'` 제거, ConfigService.getOrThrow 또는 throw 처리 ### B2: User.role 기본값 "admin" (Critical) - **파일:** prisma/schema.prisma - **문제:** 모든 가입자 자동 admin → 초대코드만 알면 SSH restart, 하네스 수정 등 전체 인프라 제어 - **수정:** `@default("viewer")` + admin 승격 별도 로직 + AdminController에 RoleGuard 추가 ### B3: WebSocket JWT 실패 시 disconnect 안 함 - **파일:** events.gateway.ts:52-53 - **문제:** invalid token 시 연결 유지 → 미인증 실시간 데이터 수신 - **수정:** catch 블록에서 `client.disconnect()` 호출 ### B4: `throw new Error` → HttpException - **파일:** auth.controller.ts:32 - **문제:** refresh token 없을 때 `throw new Error` → 500 반환 - **수정:** `throw new BadRequestException('Refresh token required')` ### B5: register 엔드포인트 rate limit 없음 - **파일:** auth.controller.ts:18-20 - **문제:** 초대코드 brute-force 가능 - **수정:** `@UseGuards(ThrottlerGuard) @Throttle({ default: { limit: 3, ttl: 60000 } })` 추가 ## Non-blocking 이슈 (7건) - N1: refresh token localStorage 평문 (httpOnly cookie 전환 권장) - N2: AuthProvider 토큰 만료 시 자동 refresh 안 함 - N3: register에서 refreshToken 미저장 - N4: login 후 userId: 0 하드코딩 - N5: DTO 서비스 파일 내 정의 (별도 파일 권장) - N6: adminFetch 주석-코드 불일치 - N7: refresh secret suffix 방식 → 별도 env 권장