Files
chat-app/docs/BUG_FIXES_2026-01.md
kappa 554c578345 Initial commit: Anvil Lounge chat application
- React frontend with Vite + TypeScript
- Cloudflare Worker backend with Durable Objects
- AI-powered chat moderation via OpenAI
- WebSocket-based real-time messaging
- XSS prevention, rate limiting, input validation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:17:27 +09:00

6.7 KiB

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 획득이 이루어지지 않음 수정:

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 로직 부재 수정:

const STORAGE_MAX_RETRIES = 3;
const STORAGE_RETRY_BASE_DELAY_MS = 50;

private async saveMessage(message: StoredMessage): Promise<void> {
  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 증상: &#106;avascript: 같은 인코딩된 XSS 공격 가능 원인: URL 검증 시 인코딩된 문자열 미처리 수정: isUrlSafe() 함수 추가

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 사용

private async isUserSilenced(userName: string): Promise<boolean> {
  return await this.ctx.storage.transaction(async (txn) => {
    const entry = await txn.get<SilenceEntry>(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 추가

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 증상: 장시간 채팅 시 브라우저 메모리 증가 원인: 메시지 배열 무한 증가 수정:

// 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() 에러 처리 부재 수정:

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 테스트: &#106;avascript:alert(1) 같은 인코딩된 URL 차단 확인
  3. 메모리 테스트: 장시간 채팅 후 브라우저 메모리 사용량 확인
  4. Storage 테스트: DO storage 일시적 실패 시 retry 동작 확인
  5. 동시성 테스트: 동일 사용자 동시 요청 시 silence 체크 정상 동작 확인