# Chat App 버그 수정 보고서 **수정일**: 2026-01-19 **검토 범위**: frontend/, worker/ --- ## 요약 | 우선순위 | 발견 | 수정 | |---------|------|------| | Critical | 4 | 4 | | High | 4 | 4 | | Medium | 9 | 9 | | **합계** | **17** | **17** | --- ## Critical Issues ### 1. WebSocket Race Condition **파일**: `frontend/src/hooks/useWebSocket.ts` **증상**: 빠른 연속 호출 시 중복 WebSocket 연결 생성 **원인**: 연결 상태 확인 전에 lock 획득이 이루어지지 않음 **수정**: ```typescript const connect = useCallback(() => { // CRITICAL: Atomic lock acquisition to prevent race conditions if (isConnectingRef.current) { return; } // Acquire lock FIRST, before any other checks isConnectingRef.current = true; // ... ``` ### 2. RateLimiter Memory Leak **파일**: `worker/src/RateLimiter.ts` **증상**: 장시간 운영 시 메모리 사용량 증가 **원인**: `cleanup()` 메서드가 만료된 엔트리를 제대로 삭제하지 않음 **수정**: - blocked 상태 만료 시 즉시 삭제 - 빈 timestamp 배열 엔트리 삭제 - timestamp 배열 정리 로직 개선 ### 3. API Key Exposure in Logs **파일**: `worker/src/AIManager.ts` **증상**: 에러 로그에 API 키가 노출될 가능성 **원인**: `sanitizeErrorForLogging()` 함수의 패턴 누락 **수정**: 다양한 API 키 형식 지원 - OpenAI: `sk-proj-*`, `sk-svcacct-*` - Anthropic: `sk-ant-api03-*` - AWS: `AKIA*`, `ASIA*` - Cloudflare tokens - Basic auth credentials ### 4. Silent Storage Failure **파일**: `worker/src/ChatRoom.ts` **증상**: DO storage 실패 시 메시지 유실 **원인**: `saveMessage()`에 retry 로직 부재 **수정**: ```typescript const STORAGE_MAX_RETRIES = 3; const STORAGE_RETRY_BASE_DELAY_MS = 50; private async saveMessage(message: StoredMessage): Promise { for (let attempt = 1; attempt <= STORAGE_MAX_RETRIES; attempt++) { try { // storage operation return; } catch (error) { if (attempt < STORAGE_MAX_RETRIES) { const jitter = Math.random() * STORAGE_RETRY_BASE_DELAY_MS; await delay(STORAGE_RETRY_BASE_DELAY_MS * attempt + jitter); } } } } ``` --- ## High Priority Issues ### 5. XSS via URL Encoding **파일**: `frontend/src/utils/sanitize.ts` **증상**: `javascript:` 같은 인코딩된 XSS 공격 가능 **원인**: URL 검증 시 인코딩된 문자열 미처리 **수정**: `isUrlSafe()` 함수 추가 ```typescript function isUrlSafe(url: string): boolean { const decoded = decodeURIComponent(url); const doubleDecoded = decodeURIComponent(decoded); // Check all versions for dangerous protocols const dangerousProtocols = [ /^javascript:/, /^vbscript:/, /^data:/, /^file:/, /^about:/, /^blob:/, ]; // ... } ``` ### 6. TOCTOU Race Condition (Silence/Violations) **파일**: `worker/src/ChatRoom.ts` **증상**: 동시 요청 시 silence 체크 우회 가능 **원인**: read-check-write가 atomic하지 않음 **수정**: DO transaction 사용 ```typescript private async isUserSilenced(userName: string): Promise { return await this.ctx.storage.transaction(async (txn) => { const entry = await txn.get(storageKey); if (!entry) return false; if (Date.now() >= entry.expiresAt) { await txn.delete(storageKey); return false; } return true; }); } ``` ### 7. Unvalidated JSON Parsing **파일**: `worker/src/AIManager.ts` **증상**: OpenAI API 응답 형식 변경 시 런타임 에러 **원인**: 응답 JSON 검증 부재 **수정**: Zod schema 추가 ```typescript const OpenAIResponseSchema = z.object({ choices: z.array(z.object({ message: z.object({ content: z.string().nullable(), tool_calls: z.array(OpenAIToolCallSchema).optional(), }), finish_reason: z.string(), })), }); ``` ### 8. Missing API Timeout **파일**: `worker/src/Context7Client.ts` **증상**: 외부 API 무응답 시 무한 대기 **원인**: fetch 요청에 timeout 미설정 **수정**: `fetchWithTimeout()` 함수 추가 (10초 timeout) --- ## Medium Priority Issues ### 9. Frontend Message Array Memory Leak **파일**: `frontend/src/components/ChatRoom.tsx`, `frontend/src/config.ts` **증상**: 장시간 채팅 시 브라우저 메모리 증가 **원인**: 메시지 배열 무한 증가 **수정**: ```typescript // config.ts export const CHAT_CONFIG = { maxDisplayMessages: 200, } as const; // ChatRoom.tsx function limitMessages(messages: DisplayMessage[]): DisplayMessage[] { if (messages.length > CHAT_CONFIG.maxDisplayMessages) { return messages.slice(-CHAT_CONFIG.maxDisplayMessages); } return messages; } ``` ### 10. Config Mismatch **파일**: `frontend/src/config.ts` **증상**: 프론트엔드에서 2000자 메시지 전송 시 잘림 **원인**: 프론트엔드(1000) vs 백엔드(2000) 제한 불일치 **수정**: `maxMessageLength: 2000`으로 통일 ### 11. Context7 Cache Unbounded Growth **파일**: `worker/src/Context7Client.ts` **증상**: 캐시 크기 무한 증가 가능 **원인**: 캐시 cleanup 로직 부재 **수정**: - `MAX_CACHE_SIZE = 100` 제한 - `cleanupExpired()`: 만료 엔트리 정리 - `evictOldest()`: LRU 방식 eviction - `cleanupIfNeeded()`: 1분 간격 자동 정리 ### 12. Serialization Error Handling **파일**: `worker/src/ChatRoom.ts` **증상**: WebSocket attachment 직렬화 실패 시 silent failure **원인**: `serializeAttachment()` 에러 처리 부재 **수정**: ```typescript private setSession(ws: WebSocket, session: Session): boolean { try { ws.serializeAttachment(session); return true; } catch (error) { console.error('Failed to serialize session attachment:', error); return false; } } ``` --- ## 수정된 파일 목록 | 파일 | 수정 내용 | |------|----------| | `frontend/src/hooks/useWebSocket.ts` | Race condition 수정 | | `frontend/src/utils/sanitize.ts` | XSS URL 검증 강화 | | `frontend/src/components/ChatRoom.tsx` | 메시지 배열 제한 | | `frontend/src/config.ts` | 설정값 통일 및 추가 | | `worker/src/ChatRoom.ts` | Storage retry, TOCTOU 수정, serialization 에러 처리 | | `worker/src/AIManager.ts` | API key redaction, Zod validation | | `worker/src/RateLimiter.ts` | Memory leak 수정 | | `worker/src/Context7Client.ts` | Timeout 추가, cache cleanup | --- ## 테스트 권장사항 1. **WebSocket 연결 테스트**: 빠른 연속 연결/해제 시 중복 연결 없음 확인 2. **XSS 테스트**: `javascript:alert(1)` 같은 인코딩된 URL 차단 확인 3. **메모리 테스트**: 장시간 채팅 후 브라우저 메모리 사용량 확인 4. **Storage 테스트**: DO storage 일시적 실패 시 retry 동작 확인 5. **동시성 테스트**: 동일 사용자 동시 요청 시 silence 체크 정상 동작 확인