QA: SPRINT-006 review iteration 1 — FAILED (blocking 5, 2 critical)

This commit is contained in:
2026-04-04 15:11:34 +09:00
committed by nabomhalang
parent 9a86d04731
commit 286f472afd

View File

@@ -0,0 +1,52 @@
# 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 권장