Repository navigation
fix(ui): reject cross-site and DNS-rebinding requests to the clip ui API - #65
Merged
Merged
Conversation
The local API answered any request that reached 127.0.0.1:5573. A web page the user visits could send a text/plain or Content-Type-less POST (no CORS preflight), which the JSON-parsing handlers accepted, and a DNS-rebinding page could read and write as if it were same-origin. `/api/*` now rejects requests whose Host name isn't 127.0.0.1 or localhost, and runs Hono's built-in `csrf()` so cross-site writes are refused. The Vite dev proxy keeps working: it forwards the browser's own localhost Host and Origin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
🎉 This PR is included in version 0.4.2 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
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.
What
clip ui's local API (/api/*) now turns away requests that didn't come from the UI itself:Hostname isn't127.0.0.1orlocalhostgets a 403. Any port is allowed, so the Vite dev proxy (which forwards the browser'slocalhost:5173) keeps working.csrf()middleware rejects cross-site writes. It coverstext/plain, form and Content-Type-less POSTs, the ones a browser sends without a CORS preflight.Both are registered on
/api/*instartUiServer(src/ui/server.ts). Static UI files are unchanged.Why
The server binds
127.0.0.1on a predictable port (5573) and had no request middleware. The handlers parse bodies withc.req.json(), which accepts any Content-Type. So:clip uiis running could sendfetch('http://127.0.0.1:5573/api/…', { method: 'POST', mode: 'no-cors', body: '{…}' })and drive mutating endpoints (tools, timeline verbs, export, snapshot/undo, import);AGENTS.md non-negotiable #3 confines
clip uicallers to the workspace. That only holds if the caller is the user's own UI.Not covered here: the snapshot-label path validation flagged on skills.sh. That's a separate change.
How to test
The new test starts the real server and sends raw HTTP with chosen
Host/Originheaders. Onmain, the 4 attack cases all returned 200. With this change they return 403, and the 4 legitimate cases (same-origin UI write, Vite proxy write, no-Origin JSON client,localhostread) return 200.Checks
pnpm test: pass, 783 passed, 24 skipped (46 files passed, 7 skipped)pnpm type-check: passpnpm lint: pass, 0 errors. The 7 warnings are the same ones already onmain, none in changed files.pnpm build(tsup + Vite UI): passvitedev (VITE_API_PORT→ the test server). All UI API calls returned 200 and a same-origin snapshot POST returned 200. Ano-corscross-origin POST fromhttp://localhost:5591tohttp://127.0.0.1:5591was refused (403) and created no snapshot.codex review --base origin/main, gpt-6.1-sol, read-only): no findings. "No actionable regressions were identified."/verifywrapper needed a one-time trust grant for repo commands. Instead of granting it, I ran the same four commands above directly on this commit.Release impact
fix(ui)→ patch release on merge (angular preset).