fix(dash): guard the localhost dashboard writes against CSRF and DNS-rebinding#107
Merged
Merged
Conversation
…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
marked this pull request as ready for review
July 21, 2026 05:04
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 & why
Addresses the deep review's LOW finding on the
forge dashwrite surface — theunauthenticated 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:
fetch()them cross-origin (the browserattaches an
Origin);127.0.0.1; the request thencarries the attacker's
Host, not the loopback one, so binding to loopback alonedoesn't stop it.
Writes now require the
Hostheader to name the loopback interface and, when a browserOriginis present, thatOriginto be the loopback origin. A mismatch is refused with403before any write is attempted. Native clients (curl, the CLI) send noOriginandstill work; a deliberate non-loopback
--hostbind is the documented opt-out (unchanged).Tests
New regression assertions in
test/dash.test.js: a cross-siteOriginand a forgedHost(via the low-level
http.request, sincefetchforbids overridingHost) are bothrejected with
403, while the existing loopback writes keep working.Checklist
npm testpasses (1072 pass / 0 fail / 2 gated skips)npm run checkpasses (Biome — 0 errors)CHANGELOG.mdupdated under## [Unreleased](Security)docs/GUIDE.md— theforge dashwrite guard)Risk & rollback
API or to legitimate loopback writes.
Extra checks
npm run typecheckpassesnode src/cli.js docs checkgreen🤖 Generated with Claude Code
https://claude.ai/code/session_01C7htTiesXKLvAUtv6ET2jz
Generated by Claude Code