Harden dev server: loopback bind, drop wildcard CORS, hide feedback bookkeeping#5
Open
companygardener wants to merge 2 commits into
Open
Conversation
Default to loopback bind, drop wildcard CORS, and stop serving the feedback/ bookkeeping files over HTTP. Together these close the three LAN/CSRF/data-exposure issues filed against this repo. - Bind to 127.0.0.1 by default; expose a --bind flag with a printed warning when bound elsewhere. - Remove Access-Control-Allow-Origin: * and the permissive do_OPTIONS. Replace with a same-origin POST check that compares Origin to Host. - Block GET on /feedback/* except history.json (the only file the in-page library polls). The check normalizes the path first (posixpath.normpath + unquote) so /./feedback/inbox.jsonl, /x/../feedback/inbox.jsonl, /feedback//inbox.jsonl, and /%66eedback/... do not bypass it. - Cap POST bodies at 1 MiB; reject non-object JSON bodies with 400. - Fix log_message TypeError when log_error passes an int as args[0]. Closes paraschopra#2, paraschopra#3, paraschopra#4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Hardens server.py against cross-origin and cross-device abuse by adding same-origin POST checks, body-size caps, type validation on JSON bodies, GET-blocking of agent-side files under /feedback/, and a default-loopback bind with an opt-in --bind flag. CORS allow-all and the OPTIONS preflight handler are removed in favor of same-origin-only access.
Changes:
- Restrict
/feedback/*GETs tohistory.json; reject cross-origin POSTs viaOrigin/Hostcomparison; enforce a 1 MiB body cap and require JSON objects. - Bind to
127.0.0.1by default; add--bindwith a LAN-exposure warning; drop CORS wildcard + OPTIONS. - Document the new bind/origin behavior in
SKILL.md.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| lib/server.py | Adds origin check, body cap, path-normalized GET filter, JSON-object validation, --bind flag, log_message fix. |
| SKILL.md | Documents loopback default and same-origin POST rejection. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Two bypasses of the GET filter, both reported by the Copilot reviewer:
1. macOS APFS and Windows NTFS are case-insensitive, so
/FEEDBACK/inbox.jsonl normalized to "/FEEDBACK/inbox.jsonl" failed
the startswith("/feedback/") check, and translate_path then resolved
it to the real file. Fix: lowercase the normalized path before the
prefix comparison.
2. posixpath.normpath("/feedback/") returns "/feedback" (no trailing
slash), which doesn't startswith("/feedback/"). The request fell
through to SimpleHTTPRequestHandler, which renders an auto directory
listing leaking inbox.jsonl / lastseen.json filenames. Fix: add an
explicit equality check for the bare "/feedback" path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
alex-pezarro-portswigger
added a commit
to alex-pezarro-portswigger/make-pages-interactive
that referenced
this pull request
May 27, 2026
…-dev-server Harden dev server: loopback bind, drop wildcard CORS, hide feedback bookkeeping
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
Closes #2, #3, #4 — three concrete network-surface issues in
lib/server.py.--bindto127.0.0.1, expose the flag for users who explicitly want LAN access, print a warning when bound non-loopback.Access-Control-Allow-Origin: *and the permissivedo_OPTIONS. Reject POSTs whoseOriginheader is set but doesn't matchHost. Same-origin browser requests, the agent, and curl all keep working as before.do_GET, block any path under/feedback/excepthistory.json. Normalize the path (posixpath.normpath(unquote(...))) first — without that,/./feedback/inbox.jsonl,/x/../feedback/inbox.jsonl,/feedback//inbox.jsonl, and/%66eedback/inbox.jsonlall reach the file throughSimpleHTTPRequestHandler.translate_path.Also folded in a couple of small robustness fixes that surfaced while doing this:
data["..."] = ...).TypeErrorinlog_messagewhenlog_errorcalls it with anintasargs[0].The
--bindwarning, the same-origin check, and the new SKILL.md notes are intended to make the security model legible to anyone reading the skill.Test plan
Manually verified against the patched server with
curl --path-as-is:GET /feedback/inbox.jsonl→ 403GET /./feedback/inbox.jsonl→ 403 (was 200 with naive exact-string check)GET /x/../feedback/inbox.jsonl→ 403GET /feedback//inbox.jsonl→ 403GET /%66eedback/inbox.jsonl→ 403GET /feedback/lastseen.json(and bypass variants) → 403GET /feedback/history.json→ 200 (the in-page library still polls it)POST /feedbackwithOrigin: http://evil.example→ 403POST /feedbackwith matchingOrigin→ 200POST /feedbackwith noOrigin(agent / curl) → 200POST /feedback→ 413127.0.0.1by default;--bind 0.0.0.0prints the warningKnown follow-up
The same-origin check compares
OrigintoHostwithout validatingHostagainst an allowlist, so a determined DNS-rebinding attacker who can match the dev port could still pass it. The realistic blast radius reduces to writing into the agent's reading context — out of scope for this PR — but aHost-allowlist hardening would be a small, reasonable follow-up.🤖 Generated with Claude Code