Validate WebSocket Origin to secure the dev REPL server#83
Merged
Conversation
WebSocket handshakes aren't bound by the same-origin policy, so any page a developer has open could otherwise connect to the REPL server and, with takeover, drive it. The server now checks the Origin header and accepts only local origins by default; the new :allowed-origins option widens it. It takes a single origin, a collection, a predicate, or :all. Non-browser clients send no Origin and are unaffected, so the Node integration path keeps working. The origin policy is built before the listen port is bound, so a bad :allowed-origins value fails fast instead of leaking a server with no stop handle.
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.
WebSocket handshakes aren't subject to the same-origin policy, so today any page a developer has open in another tab can connect to the REPL server. With multi-client takeover that's worse than it sounds - a drive-by page can become the active client, receive the eval-js stream, and inject frames. This adds an Origin allowlist.
By default only local origins (
localhost,127.0.0.1,[::1], any port, http or https) are accepted. Non-browser clients send no Origin header and are always allowed, so the Node/Deno/Bun path is unaffected (the integration test still passes). Serving from elsewhere - a LAN IP for phone testing, a custom dev domain - is a:allowed-originsaway; it takes a single origin, a collection, a predicate, or:allto opt out.This is a deliberate secure-by-default behavior change: setups that served the app from a non-local origin will now get a 403 until they set
:allowed-origins. Noted in the README and changelog.Came out of an adversarial review pass - the notable catch was building the origin policy before binding the listen port, so a bad
:allowed-originsfails fast instead of leaking an unstoppable server.