# [InsuRo 개인정보 P0-1] Drive 공개제거→서버 프록시 · Phase 1 (백엔드 코어)

## allowed_resources

```yaml
allowed_resources:
  paths:
    - "server/**"
    - "supabase/migrations/**"
    - "server/tests/**"
  forbidden_paths:
    - ".env"
    - ".env.*"
    - ".github/**"
  commands:
    - "pytest"
    - "python3 -m py_compile"
    - "python3 -c"
    - "curl"
  merge_policy: "tiered"
  ttl_hours: 72
```

## 프로젝트 / 필독 설계문서 (정독 필수)
- 프로젝트: `/home/jay/projects/InsuRo` (FastAPI `server/` + React `src/`)
- ★ 착수 전 3문서 **전부 정독**:
  - `docs/plans/drive-proxy/plan.md` (계획서 — 3대 결정)
  - `docs/plans/drive-proxy/context.md` (맥락 — 현재 코드 file:line)
  - `docs/plans/drive-proxy/checklist.md` (체크리스트)
- 배경: 증권 PDF 등 업로드 파일이 Google Drive `anyone/reader` 공개 → 링크 유출 리스크. 공개 제거 + 서버 프록시로 접근통제. **Drive 유지(대량저장 비용 절감), Supabase Storage 이관 안 함.**

## Phase 1 범위 (백엔드 코어만 — 프론트/소급회수는 후속 Phase)
1. **`drive_file_id` 정규화 컬럼 신설 + 백필** 마이그레이션: `newsletters`·`premium_data`·`policy_analyses`(+`source_file_ref` jsonb). 기존 저장 `file_url`(`https://drive.google.com/file/d/{id}/view`)에서 `re.search(r"/file/d/([^/]+)")`로 id 추출·백필. IDOR 인가의 기반.
2. **file_id 추출 파서 유틸** — 삭제·재분석·프록시가 **공유하는 단일 함수**로.
3. **`POST /api/insuro/file-ticket`** (Bearer/`verify_jwt`) — 등급별 소유권 인가 후 **1회용 ticket 서버저장**(만료 5분) → `ticket_id` 반환.
   - 등급: 공개(fcpa_config) / 관리자(소식지·보험료, `require_admin`) / FA소유(증권 policy_analyses, `payload.sub == user_id`). ※FCPA 가이드 공개등급 여부는 **회장 확인 대기 → 우선 공개등급으로 두되 result.json에 확인요망 명시**.
   - `drive_file_id` ↔ 레코드 조회로 소유 검증(IDOR 차단).
4. **`GET /api/insuro/file/{ticket_id}`** — ticket 조회·**1회 소비(무효화, replay 방지)** → `run_in_executor`로 Drive `get_media` → **`StreamingResponse` 청크**(전체 메모리 적재 금지, OOM 방지).
   - 헤더: 정확한 `Content-Type` + `Content-Disposition: attachment` + `X-Content-Type-Options: nosniff` + `Referrer-Policy: no-referrer`. 타임아웃 상한.
5. **`parse_premium_file`(`server/main.py:5859-5865`) file_id 기반 전환**: 현 `httpx.get(/view URL)`(HTML 오수신 버그) → `drive_file_id`로 소유자 `get_media` 다운로드. legacy `/view` URL 입력도 파서로 id 추출(하위호환).
6. **JWT ES256 전용 전환**(`server/main.py` verify_jwt `algorithms=["ES256","HS256"]` → `["ES256"]`). ★Codex 확인: 이 코드는 Supabase **JWKS 기반 검증**(`PyJWKClient`)이라 안전(HS256 secret 미사용). 테스트 fixture만 갱신.
7. **예외 `pass` 삼킴 제거** → Drive/DB 실패 시 감사로그. 삭제 실패 시 고아파일 재시도 큐(최소 로그).
8. **`_validate_file_url` 화이트리스트 존치**(SSRF 방어).

## Phase 1 배포 특성 (안전)
- Phase 1은 **엔드포인트 추가 + 컬럼 추가 + 내부 전환**만. **신규 업로드 `anyone` 생성은 아직 유지**(구 `/view` 병행) → 기존 동작 무변경, 중간 파손 0. (신규 anyone 제거·소급회수·프론트 교체는 Phase 2/3에서 프록시 완성 후 함께 — 회장 결정 (b): 공백 없이 한 번에.)
- restore 태그 `restore-260808-pre-p0-1` 먼저.

## 조사·보고 항목 (result.json)
- OAuth refresh token 실제 scope(`get_drive_service`) — `drive.file`로 축소 가능한지 조사.
- FCPA 가이드 공개등급 확정 필요(회장 확인).

## 검증 기준
1. 유닛: file_id 파서 / ticket 발급·1회소비·만료 / IDOR(타 FA 403) / ES256(HS256 토큰 거부) / SSRF 화이트리스트.
2. 로컬 스모크: file-ticket 발급 → file/{ticket} 스트리밍 200 + 헤더 확인. `parse_premium_file` legacy `/view` 입력으로도 정상.
3. 회귀 0(기존 pytest, ES256 전환으로 깨지는 테스트 fixture 갱신). 마이그레이션 백필 검증(기존 레코드 drive_file_id 채워짐).

## 완료 보고 8항목
수정 파일/함수 diff · 마이그레이션(컬럼+백필) · file-ticket/프록시 엔드포인트 · IDOR 인가 로직 · ES256 전환 · parse_premium 전환 · 회귀/스모크 결과 · OAuth scope·FCPA등급 조사결과 · PR/머지 상태.

## 주의
- 프로덕션. Phase 1은 무중단 성격이나 배포 전 restore 태그. finish-task 후 원격 head 확인.
- InsuRo repo=`Jeon-Jonghyuk/InsuRo`(또는 JonghyukJeon/InsuRo — git remote 확인). PR ruleset 데드락 이력 주의 → 머지 막히면 로컬커밋+ANU 보고.