Skip to content

fix(server): bind to loopback and reject cross-origin writes - #5

Merged
fiddur merged 1 commit into
developfrom
bind-loopback
Aug 21, 2026
Merged

fix(server): bind to loopback and reject cross-origin writes#5
fiddur merged 1 commit into
developfrom
bind-loopback

Conversation

@fiddur

@fiddur fiddur commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

First of the security fixes. server.listen(currentPort) passed no host, so the server bound the
wildcard interface — every /api route, meaning the diff, the repository's files and the review
comments, was readable by anything routable to the machine, with no authentication. DIFFITY_HOST
does not help: it only changes the hostname in the printed URL.

Every response also carried Access-Control-Allow-Origin: *, which authorises any page open in the
same browser to read a private diff, and a cross-origin POST with Content-Type: text/plain is a
CORS simple request, so /api/revert-file, /api/revert-hunk, /api/threads and
/api/github/push-comments were reachable without a preflight.

  • DIFFITY_BIND selects the interface, default 127.0.0.1, with a warning printed when widened
  • the CORS headers are gone; the UI is same-origin and calls /api with relative paths, so it never needed them
  • writes require Sec-Fetch-Site: same-origin or an absent/loopback Origin — unforgeable cross-site, and navigations are GET so they are unaffected
  • X-Content-Type-Options: nosniff on every response
  • README documents both variables and the absence of authentication

Verified: ss -ltn shows 127.0.0.1:5391 where it previously showed the wildcard; a cross-site
POST to /api/revert-file returns 403; a same-origin POST still reaches its route; no
Access-Control-* header is sent. 163 tests pass.

Still outstanding from the audit, in follow-ups: shell interpolation in the git helpers (a PR
containing a filename with $(…) executes on open), percent-encoded path traversal in
/api/tree/raw/, and rehype-raw without a sanitizer.

Stacked on #4.

🤖 Generated with Claude Code

https://claude.ai/code/session_018PkYQzbsnMihHesafWvXKs

The server passed no host to listen(), so it bound the wildcard interface and every
/api route — the diff, the repository's files, the review comments — was readable by
anything routable to the machine. It also sent Access-Control-Allow-Origin: * on every
response, so any page open in the same browser could read a private diff, and could POST
to /api/revert-file, /api/revert-hunk, /api/threads and /api/github/push-comments without
a preflight.

DIFFITY_BIND selects the interface and defaults to 127.0.0.1, with a warning when it is
widened. DIFFITY_HOST keeps its existing meaning: the hostname in the printed URL only.

The UI is served from the same origin and calls /api with relative paths, so it needs no
CORS headers at all. Writes now additionally require Sec-Fetch-Site: same-origin (or an
absent/loopback Origin), which a cross-site page cannot forge. Navigations are GET and
unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018PkYQzbsnMihHesafWvXKs
@fiddur
fiddur changed the base branch from upstream-ui to develop August 21, 2026 13:34
@fiddur
fiddur marked this pull request as ready for review August 21, 2026 13:34
@fiddur
fiddur merged commit d44b361 into develop Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant