Conversation
chore: merge release v0.5.5 to develop
`src/lib/ws-server.ts` の upgrade ハンドラに `/proxy/<prefix>/...` 分岐を追加し、
対応する external_apps レコードが enabled かつ websocket_enabled の場合は
upstream へ raw TCP で WebSocket をパススルーするようにした。これにより
Streamlit/Shiny 等 WebSocket を使う External App が CommandMate プロキシ越しでも
完全に起動できるようになる。
主な変更:
- src/lib/ws-server.ts: handleProxyUpgrade() を DI 構成で実装し、`/proxy/<prefix>` を
upstream 解決→TCP 接続→双方向パイプ
- SSRF 防御: PROXY_ALLOWED_HOSTS = {'localhost', '127.0.0.1'} allow-list
- src/lib/proxy/handler.ts: 旧 proxyWebSocket() を @deprecated 化 (実際には
Route Handler に upgrade は到達しないため dead code)
- src/app/proxy/[...path]/route.ts: HEAD メソッドを追加 (ヘルスチェック向け)
- next.config.js: skipTrailingSlashRedirect: true を設定し
`/proxy/<prefix>/` の 308 を抑止
- tests/unit/ws-server-proxy-upgrade.test.ts (11 ケース) と
tests/unit/proxy/route.test.ts (HEAD ケース) を追加
品質: lint / tsc / 6342 unit / 179 regression すべて pass。
新規コードパスのカバレッジ ~92%。
Closes #671
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(#671): proxy WebSocket upgrades to External Apps
Node's fetch transparently decompresses the upstream body (Node 18+ undici default). Forwarding the upstream Content-Encoding: gzip along with the already-decompressed body causes browsers to fail with ERR_CONTENT_DECODING_FAILED, resulting in a blank page (observed on Streamlit-based External Apps, e.g. Photon via /proxy/photon/). Add content-encoding and content-length to HOP_BY_HOP_RESPONSE_HEADERS so they are filtered out alongside the existing hop-by-hop entries. The Node response layer re-adds correct framing (chunked or computed length) for the decoded body, and the browser parses correctly. Regressions via curl were hidden because curl omits Accept-Encoding by default; the upstream then skips compression and the bug does not manifest. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Apply minor refactoring to the Issue #673 PDF viewer implementation: - src/config/pdf-extensions.ts: Reuse normalizeExtension from image-extensions.ts (same pattern as video-extensions.ts), removing 7 lines of duplicated normalization logic. - src/app/api/worktrees/[id]/files/[...path]/route.ts: Remove WHAT-level comments in the PDF branch (e.g. "Read file as binary", "magic bytes failure") whose intent is self-evident from the identifiers and control flow. - src/components/worktree/PdfPreview.tsx: Remove one WHAT-level comment explaining state-reset lines that are already obvious. Quality Metrics: - Lint: pass - TypeScript: pass - Unit tests: 6381 passing (unchanged) Refs #673 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Integrate the remaining PDF viewer pieces with the API/component/config modules already landed in e7ea044. PDF files in the Files tab now render via an iframe backed by a Blob URL, matching the image/video preview pattern while honoring PDF-specific security constraints. - src/types/models.ts: Add isPdf?: boolean to FileContent for the new routing signal from the file fetch API. - src/components/worktree/FilePanelContent.tsx: Route isPdf responses to a dynamically-imported PdfPreview between the video and HTML branches. - src/hooks/useFileContentPolling.ts: Disable auto-polling for PDF tabs to avoid refetching 20MB payloads on every tick (S3-003). - next.config.js: Extend frame-src with blob: so Blob URL iframes can load (Issue #490 DR4-007 retracted for #673 PDF preview). - tests/helpers/pdf-fixtures.ts: Shared helpers for minimal/broken PDF buffers used by the new pdf-extensions, API and PdfPreview tests. - tests/unit/config/pdf-extensions.test.ts, tests/unit/api/files-pdf.test.ts, tests/unit/components/PdfPreview.test.tsx, tests/unit/components/FilePanelContent.test.tsx: Cover validation, API branches (PDF_SIZE_EXCEEDED / INVALID_MAGIC_BYTES / ENOENT), iframe attributes, Blob URL lifecycle and routing. - CLAUDE.md: Index the new pdf-extensions.ts module. Refs #673 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ptance) Persist the PM automation artifacts produced during /pm-auto-issue2dev 673 so that the review findings, design decisions, and acceptance verification remain auditable. - issue-review/: Stage 1 (normal) and Stage 3 (impact) review results with 15 + 14 findings applied to the Issue body (Stages 5-8 Codex delegation skipped per session policy). - work-plan.md: Scoped work plan that defers PoC, method B streaming, i18n and mobile-specific work to follow-up issues. - pm-auto-dev/iteration-1/: TDD context + result JSON, acceptance verification (15/15 passed), refactor notes. - hypothesis-verification.md and summary-report.md for traceability. Refs #673 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Issue #673 iframe-based PDF preview was blocked across the board: - Desktop Chrome showed "このページは Chrome によってブロックされています" because CSP `frame-ancestors 'none'` + `X-Frame-Options: DENY` plus the `sandbox="allow-scripts"` iframe collided with Chrome's built-in PDF viewer MIME-handler navigation, and `connect-src` rejected the PdfPreview `fetch(dataUri)`. - Mobile Chrome additionally lacks an in-page PDF viewer entirely and shows "このコンテンツはブロックされました" for any iframe-rendered PDF. Adjust the stack end-to-end: - next.config.js: allow `data:` in `connect-src`, downgrade `X-Frame-Options` to `SAMEORIGIN`, and loosen CSP `frame-ancestors` to `'self'` so same-origin blob iframes are permitted while external clickjacking remains blocked. - PdfPreview: drop the `sandbox` attribute (any value conflicts with Chrome's PDF MIME-handler) and introduce a `variant` prop — `iframe` for desktop, `download` for mobile where tapping opens the PDF in the OS/browser PDF handler or downloads it. - FileViewer (mobile): route PDFs through the new `download` variant. - Tests updated for the new sandbox semantics.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
frame-ancestors 'none', missingdata:inconnect-src),X-Frame-Options: DENY, andsandbox="allow-scripts"colliding with Chrome's built-in PDF viewer MIME-handler navigationChanges
CSP / framing (
next.config.js)data:toconnect-srcsoPdfPreview'sfetch(dataUri)passesX-Frame-Options: DENY→SAMEORIGIN(allow same-origin blob iframes)frame-ancestors 'none'→'self'(keep external clickjacking protection, allow our own blob iframe)PdfPreview (
src/components/worktree/PdfPreview.tsx+src/config/pdf-extensions.ts)sandboxattribute entirely — every value (evenallow-scripts allow-same-origin) collides with Chrome's PDF MIME-handler navigation. Clickjacking protection is preserved at the host-page level (X-Frame-Options+frame-ancestors)variantprop:iframe(default, desktop) vsdownload(mobile) with PDFを新しいタブで開く / ダウンロード buttons that point at the Blob URLMobile viewer (
src/components/worktree/FileViewer.tsx)content.isPdfbranch that rendersPdfPreviewindownloadvariant, so mobile taps hand the PDF off to the OS PDF handler instead of hitting the iframe blockTests
tests/unit/config/pdf-extensions.test.ts:PDF_IFRAME_SANDBOXis nowundefinedtests/unit/components/PdfPreview.test.tsx: iframe no longer has asandboxattributeVerification
http://localhost:3000/worktrees/mywebdata-main: PDF renders with thumbnails, toolbar, zoom (verified via Playwright MCP after each fix)Test plan
.pdfin the Files tab and confirm the iframe renders with thumbnail / toolbarnpm run test:unitpasses (PdfPreview / pdf-extensions tests updated)🤖 Generated with Claude Code