# task-1805 완료 보고서

## SCQA

**S**: InsuWiki Firestore의 `documents/{docId}/ai_suggestions` 서브컬렉션은 인증된 모든 멤버(`isMemberOrAdmin()`)에게 무조건 read 접근을 허용하고 있었다.

**C**: 다른 사용자의 private 문서에 달린 AI 제안까지 읽을 수 있는 보안 취약점이 존재. task-1802 FloatingTermDetection 보안 수정 시 발견.

**Q**: 부모 문서의 소유자/공개 여부에 따라 ai_suggestions 접근을 제한할 수 있는가?

**A**: `firestore.rules`의 ai_suggestions read 규칙에 소유자 검증 4개 조건을 추가하여 해결. private 문서의 AI 제안은 본인(제안 생성자 또는 문서 작성자)만 읽기 가능. public/shared/레거시 문서는 기존과 동일하게 모든 멤버가 읽기 가능. write 규칙은 변경 없음.

## 변경 내용

### 수정 파일
- `/home/jay/projects/insuwiki/.worktrees/task-1805-dev3/firestore.rules` (151-160행)

### 변경 상세
기존 read 규칙 `allow read: if isAuthenticated() && isMemberOrAdmin();`를 아래 4개 OR 조건으로 교체:

1. `resource.data.userId == request.auth.uid` — 본인이 생성한 AI 제안
2. `get(.../documents/$(docId)).data.authorId == request.auth.uid` — 본인 문서의 AI 제안
3. `get(.../documents/$(docId)).data.visibility in ['public', 'shared']` — 공개 문서
4. `!('visibility' in get(.../documents/$(docId)).data)` — 레거시 문서 (visibility 필드 없음, 기존 isPublic() 로직과 일관)

write 규칙: 변경 없음 (`isAuthenticated() && isMemberOrAdmin()` 유지)

## 검증 결과

### 논리 검증 (7개 시나리오)
- 본인 AI 제안 읽기: PASS (userId 매칭)
- 본인 문서 AI 제안 읽기: PASS (authorId 매칭)
- public 문서 AI 제안 읽기: PASS
- 타인 private 문서 AI 제안 읽기: PASS (차단됨)
- shared 문서 AI 제안 읽기: PASS
- 레거시 문서 AI 제안 읽기: PASS
- write 규칙 무변경: PASS

### 마아트 독립 검증
- 판정: **PASS**
- 변경 범위: 151-160행만 변경, 나머지 파일 원본 동일
- get() 경로 및 변수 바인딩 정합성 확인

### 로키 보안 감사
- 판정: **VULNERABLE** (write 소유자 검증 부재 지적)
- 인증 우회: 없음 (`isAuthenticated() && isMemberOrAdmin()` 가드 유지)
- IDOR (read 경로): 해결됨
- 권한 상승: 없음

## 발견 이슈 및 해결

### 자체 해결 (3건)
1. **private 문서 ai_suggestions 무단 읽기** — read 규칙에 소유자 검증 4조건 추가
2. **shared visibility 누락 위험** — `in ['public', 'shared']`로 shared 문서도 명시적 허용
3. **레거시 문서 호환성** — `!('visibility' in ...)` 조건으로 visibility 필드 없는 문서도 기존과 동일하게 동작

### 범위 외 미해결 (2건)
1. **[HIGH] write 규칙 소유자 검증 부재** — 범위 외 사유: 태스크 지시서에서 write 변경 미요구. write 소유자 검증 추가 시 AI 제안 생성 흐름(Cloud Functions/Admin SDK 경유 여부) 분석 필요. 별도 태스크 권장.
2. **[LOW] isMemberOrAdmin() vs isMemberOrAbove() 혼재** — 범위 외 사유: 기존 코드 전체의 패턴 불일치이며 본 태스크 범위를 초과.

## 엣지 케이스 분석
- **userId 필드 없는 ai_suggestion**: Firestore에서 존재하지 않는 필드 접근 시 false 반환 → 다음 조건으로 fallthrough. 안전.
- **부모 문서 없음**: `get()` null → `.data` 접근 실패 → 자동 deny. 의도된 안전 동작.
- **get() quota**: 동일 경로는 Firestore 내부 캐싱으로 1회로 카운트. 요청당 10회 제한 내 안전.

## 머지 판단
- **머지 필요**: Yes
- **브랜치**: task/task-1805-dev3
- **워크트리 경로**: /home/jay/projects/insuwiki/.worktrees/task-1805-dev3
- **머지 의견**: 보안 취약점 수정으로 머지 권장. 마아트 PASS, 로키 write 이슈는 범위 외 별도 태스크로 처리 필요. `firebase deploy --only firestore:rules` 배포는 제이회장님 승인 후 진행.

## 모델 사용 기록
- 팀원: 루(Lugh) / 작업: firestore.rules 수정 / 모델: sonnet / 정당성: -
- 팀원: 모리건(Morrigan) / 작업: 검증 시나리오 리뷰 / 모델: sonnet / 정당성: -
- 횡단: 마아트(Maat) / 작업: 독립 검증 / 모델: sonnet
- 횡단: 로키(Loki) / 작업: 보안 감사 / 모델: sonnet

## 세션 통계
- 총 도구 호출: 4회

### 수정 파일 목록
- /home/jay/projects/insuwiki/.worktrees/task-1805-dev3/firestore.rules: 1회 (Edit)
- /home/jay/workspace/memory/reports/task-1805.md: 1회 (Write)
- /home/jay/workspace/memory/tasks/task-1805.md: 1회 (dispatch)
- bash_cmd: 1회 (Bash)

### 도구 사용 현황
- Bash: 1회
- Edit: 1회
- Write: 1회
- dispatch: 1회

