|
| 1 | +# 160 — 2차 Patch 검증 결과 |
| 2 | + |
| 3 | +## 검증 일시 |
| 4 | + |
| 5 | +2026-06-21 03:30 UTC |
| 6 | + |
| 7 | +## 검증 환경 |
| 8 | + |
| 9 | +- bun v1.3.14 |
| 10 | +- macOS arm64 |
| 11 | +- tsc --noEmit: clean |
| 12 | +- bun test: 116 pass / 0 fail / 379 expect() |
| 13 | + |
| 14 | +## Finding별 검증 |
| 15 | + |
| 16 | +### F1: OpenCode Go URL 정규화 (Blocker) — ✅ RESOLVED |
| 17 | + |
| 18 | +**Commit**: `ef389f6` |
| 19 | +**변경**: `src/adapters/anthropic.ts:178` — baseUrl 끝 `/v1` 제거 후 URL 구성 |
| 20 | +**테스트**: `tests/url-normalization.test.ts` — 5 cases (opencode-go, trailing slash, standard anthropic, mid-URL, false positive) |
| 21 | + |
| 22 | +``` |
| 23 | +Before: https://opencode.ai/zen/go/v1/v1/messages (404) |
| 24 | +After: https://opencode.ai/zen/go/v1/messages (correct) |
| 25 | +``` |
| 26 | + |
| 27 | +### F2: WS incomplete event type (High) — ✅ RESOLVED |
| 28 | + |
| 29 | +**Commit**: `945f4b2` |
| 30 | +**변경**: `src/ws-bridge.ts:226` — template literal `response.${finalStatus}` |
| 31 | +**테스트**: `tests/ws-endpoint.test.ts` — 2 cases (incomplete→response.incomplete, failed→response.failed) |
| 32 | + |
| 33 | +``` |
| 34 | +Before: status "incomplete" → event type "response.completed" (Codex treats as success) |
| 35 | +After: status "incomplete" → event type "response.incomplete" (Codex treats as error) |
| 36 | +``` |
| 37 | + |
| 38 | +### F3: localhost CSRF (Medium) — ✅ RESOLVED |
| 39 | + |
| 40 | +**Commit**: `8107f2d` |
| 41 | +**변경**: `src/server.ts` — `isLocalOrigin()` + handleManagementAPI 상단 POST/PUT/DELETE guard |
| 42 | +**테스트**: CSRF 보호는 서버 integration test 필요 (unit test 범위 밖) — Origin 검증 로직은 코드 리뷰로 확인 |
| 43 | + |
| 44 | +``` |
| 45 | +Before: POST /api/stop with any Origin → 200 (proxy stops) |
| 46 | +After: POST /api/stop with foreign Origin → 403 (blocked) |
| 47 | +``` |
| 48 | + |
| 49 | +### F4: heartbeat stall timeout (Medium) — ✅ RESOLVED |
| 50 | + |
| 51 | +**Commit**: `a66a6c0` + `a490882` |
| 52 | +**변경**: `src/bridge.ts`, `src/types.ts`, `src/server.ts` — `stallTimeoutSec` in options object, default 90s, min 1s |
| 53 | +**테스트**: `tests/bridge-lifecycle.test.ts` — stallTimeoutSec=1 triggers response.incomplete after deadline |
| 54 | + |
| 55 | +``` |
| 56 | +Before: hardcoded 150 ticks × 2s = 5 min, no config |
| 57 | +After: configurable stallTimeoutSec (default 90s, min 1s), options object (no call site breakage) |
| 58 | +``` |
| 59 | + |
| 60 | +### F5: Windows update shim repair (Medium) — ✅ RESOLVED |
| 61 | + |
| 62 | +**Commit**: `63cac58` |
| 63 | +**변경**: `src/update.ts` (async), `src/cli.ts` (await) — installCodexShim() 호출 |
| 64 | +**테스트**: Platform-specific (Windows 필요) — macOS에서 코드 경로 확인, 실행은 win32 조건 분기 |
| 65 | + |
| 66 | +``` |
| 67 | +Before: ocx update → npm/bun install → done (shim may be overwritten) |
| 68 | +After: ocx update → npm/bun install → installCodexShim() on Windows → done |
| 69 | +``` |
| 70 | + |
| 71 | +## 감사 audit 피드백 반영 |
| 72 | + |
| 73 | +| Audit Issue | 조치 | |
| 74 | +|---|---| |
| 75 | +| Phase 130: handleManagementAPI에 listenPort 없음 | `_corsOrigin` 모듈 변수 활용 | |
| 76 | +| Phase 130: POST만 보호, PUT/DELETE 누락 | POST/PUT/DELETE 모두 커버 | |
| 77 | +| Phase 140: bridgeToResponsesSSE call site 파손 | positional param 대신 options 객체에 추가 | |
| 78 | +| Phase 150: runUpdate 동기함수에 await import | async로 변환, cli.ts await 추가 | |
| 79 | +| Phase 150: ensureCodexShim 존재하지 않음 | 실제 export인 installCodexShim 사용 | |
| 80 | + |
| 81 | +## 커밋 목록 |
| 82 | + |
| 83 | +| Commit | 내용 | |
| 84 | +|---|---| |
| 85 | +| `643b668` | docs: v2.1.2 patch plan | |
| 86 | +| `ef389f6` | fix: URL normalization | |
| 87 | +| `945f4b2` | fix: WS incomplete event | |
| 88 | +| `8107f2d` | fix: CSRF protection | |
| 89 | +| `a66a6c0` | feat: stall timeout config | |
| 90 | +| `63cac58` | fix: update shim repair | |
| 91 | +| `a490882` | test: patch-specific tests | |
| 92 | + |
| 93 | +## 남은 항목 (v2.1.2 scope 밖) |
| 94 | + |
| 95 | +- H5: native WebSocket response header 보존 — 실환경 capture 필요 |
| 96 | +- ANTHROPIC_WIRE_MODELS 모델 목록 drift 모니터링 |
| 97 | +- structure/ 문서 업데이트 (transport 섹션의 WS incomplete 설명) |
0 commit comments