Skip to content

fix(dash): guard the localhost dashboard writes against CSRF and DNS-rebinding#107

Merged
CodeWithJuber merged 1 commit into
masterfrom
claude/loop-goal-worktree-issues-il3uk9
Jul 21, 2026
Merged

fix(dash): guard the localhost dashboard writes against CSRF and DNS-rebinding#107
CodeWithJuber merged 1 commit into
masterfrom
claude/loop-goal-worktree-issues-il3uk9

Conversation

@CodeWithJuber

Copy link
Copy Markdown
Owner

What & why

Addresses the deep review's LOW finding on the forge dash write surface — the
unauthenticated localhost dashboard's two human-driven writes (POST /api/ratify,
POST /api/retract) had no CSRF or DNS-rebinding protection.

Unguarded, those writes were reachable by:

  • CSRF — any web page the user visits could fetch() them cross-origin (the browser
    attaches an Origin);
  • DNS-rebinding — an attacker domain that rebinds to 127.0.0.1; the request then
    carries the attacker's Host, not the loopback one, so binding to loopback alone
    doesn't stop it.

Writes now require the Host header to name the loopback interface and, when a browser
Origin is present, that Origin to be the loopback origin. A mismatch is refused with
403 before any write is attempted. Native clients (curl, the CLI) send no Origin and
still work; a deliberate non-loopback --host bind is the documented opt-out (unchanged).

Tests

New regression assertions in test/dash.test.js: a cross-site Origin and a forged Host
(via the low-level http.request, since fetch forbids overriding Host) are both
rejected with 403, while the existing loopback writes keep working.

Checklist

  • npm test passes (1072 pass / 0 fail / 2 gated skips)
  • npm run check passes (Biome — 0 errors)
  • New behavior has a test
  • Conventional commit message
  • CHANGELOG.md updated under ## [Unreleased] (Security)
  • No new runtime dependency
  • Docs updated (docs/GUIDE.md — the forge dash write guard)

Risk & rollback

  • Risk level: low — adds a pre-write check on two localhost routes; no change to the read
    API or to legitimate loopback writes.
  • Rollback plan: revert the single commit.

Extra checks

  • npm run typecheck passes
  • node src/cli.js docs check green
  • Authorization/ownership checked (this IS the authorization fix)

🤖 Generated with Claude Code

https://claude.ai/code/session_01C7htTiesXKLvAUtv6ET2jz


Generated by Claude Code

…rebinding

The `forge dash` server is an unauthenticated localhost convenience server with two
human-driven writes (POST /api/ratify, POST /api/retract). Unguarded, those were
reachable by any web page the user visits (a cross-site fetch — the browser attaches
an Origin) and by a DNS-rebinding attack (an attacker domain rebinds to 127.0.0.1 —
the request then carries the attacker's Host, not the loopback one).

Writes now require the Host header to name the loopback interface AND, when a browser
Origin is present, that Origin to be the loopback origin; a mismatch is refused with
403 before any write is attempted. Native clients (curl, the CLI) send no Origin and
still work; a deliberate non-loopback --host bind is the documented opt-out. Covered by
new regression tests (a cross-site Origin and a forged Host are both rejected).

Addresses the deep review's LOW finding on the dashboard write surface. Full suite
1072 pass; biome/tsc/docs-check green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7htTiesXKLvAUtv6ET2jz
@CodeWithJuber
CodeWithJuber marked this pull request as ready for review July 21, 2026 05:04
@CodeWithJuber
CodeWithJuber merged commit 7f48879 into master Jul 21, 2026
11 checks passed
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.

2 participants