Skip to content

Harden dev server: loopback bind, drop wildcard CORS, hide feedback bookkeeping#5

Open
companygardener wants to merge 2 commits into
paraschopra:mainfrom
companygardener:security/harden-dev-server
Open

Harden dev server: loopback bind, drop wildcard CORS, hide feedback bookkeeping#5
companygardener wants to merge 2 commits into
paraschopra:mainfrom
companygardener:security/harden-dev-server

Conversation

@companygardener

Copy link
Copy Markdown

Summary

Closes #2, #3, #4 — three concrete network-surface issues in lib/server.py.

Also folded in a couple of small robustness fixes that surfaced while doing this:

  • Cap POST bodies at 1 MiB and reject non-object JSON with 400 (instead of crashing later on data["..."] = ...).
  • Fix a TypeError in log_message when log_error calls it with an int as args[0].

The --bind warning, 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 → 403
  • GET /./feedback/inbox.jsonl → 403 (was 200 with naive exact-string check)
  • GET /x/../feedback/inbox.jsonl → 403
  • GET /feedback//inbox.jsonl → 403
  • GET /%66eedback/inbox.jsonl → 403
  • GET /feedback/lastseen.json (and bypass variants) → 403
  • GET /feedback/history.json → 200 (the in-page library still polls it)
  • POST /feedback with Origin: http://evil.example → 403
  • POST /feedback with matching Origin → 200
  • POST /feedback with no Origin (agent / curl) → 200
  • 2 MiB POST /feedback → 413
  • Boots cleanly on 127.0.0.1 by default; --bind 0.0.0.0 prints the warning

Known follow-up

The same-origin check compares Origin to Host without validating Host against 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 a Host-allowlist hardening would be a small, reasonable follow-up.

🤖 Generated with Claude Code

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>
Copilot AI review requested due to automatic review settings May 27, 2026 05:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 to history.json; reject cross-origin POSTs via Origin/Host comparison; enforce a 1 MiB body cap and require JSON objects.
  • Bind to 127.0.0.1 by default; add --bind with 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.

Comment thread lib/server.py Outdated
Comment thread lib/server.py Outdated
Comment thread lib/server.py
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
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.

Security: dev server binds to all interfaces by default (LAN exposure)

2 participants