Refuse the endpoint spellings WHATWG URL repairs and System.Uri does not

`isLoopbackEndpoint` handed the raw endpoint to `URL` and read the host it got
back. `URL` is a repairing parser and `System.Uri` is not, so seven spellings
were loopback here and remote in C#: a trailing dot on the host, which `URL`
drops; `ws:127.0.0.1`, `ws:/127.0.0.1` and `ws:///127.0.0.1`, whose authority
`URL` infers; a backslash path separator, which `URL` reads as a separator;
fullwidth digits and letters in the host, which `URL` folds to ASCII; and a tab
in the host, which `URL` deletes. Each of those is a local-development bypass:
a route the C# consumer would have demanded a certificate for opened unsigned
here.

`refusedByCSharpUriShape` refuses exactly those, on the raw string, before
`URL` sees it. Leading and trailing C0 controls and spaces around the whole
endpoint are stripped first, because both sides accept them. The spellings
Hands probed earlier and C# accepts -- `127.1`, hex, decimal,
`[::FFFF:7F00:1]`, userinfo, `localhost:8443` -- are untouched.

The doc comment claimed equivalence with `System.Uri.IsLoopback`. It is not
equivalent and now says so: two divergences remain, both unreachable from a
browser CultMesh route, and both named. An IPv6 zone id throws in `URL` and is
a host-local interface name no browser socket can dial; `file:` and `mailto:`
have no `URL` host and are refused by `openSocket`, which takes `ws:` and
`wss:` only.

The test is Soul's endpoint list with the C# answer as the expectation: 82 of
the 88 it probed, the six unreachable ones named in the doc comment instead.

Mutations: the trailing-dot refusal dropped, the scheme-and-authority shape
inverted, the backslash refusal dropped, and the non-ASCII host refusal
dropped -- each killed by the table.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-16 17:22:06 +02:00
commit 803ee12a7a
3 changed files with 172 additions and 22 deletions

View file

@ -188,6 +188,26 @@ const mutations = [
old: 'host === "::ffff:7f00:1" ||',
new: "",
},
{
rule: "a trailing dot on the host is refused, as System.Uri refuses it",
old: ' || host.endsWith(".");',
new: ";",
},
{
rule: "a scheme not followed by // and an authority is refused",
old: " if (!shape) return true;",
new: " if (!shape) return false;",
},
{
rule: "a backslash in the endpoint is refused",
old: ' if (raw.includes("\\\\")) return true;\n',
new: "",
},
{
rule: "a non-ASCII host is refused, so URL cannot fold a fullwidth form to ASCII",
old: " return /[^\\u0021-\\u007e]/.test(host)",
new: " return false",
},
{
rule: "protected scheme: any scheme containing quic",
old: 'scheme.includes("quic")',