mirror of
https://github.com/block/buzz.git
synced 2026-10-31 14:31:02 +00:00
fix(relay): reject any unparseable BUZZ_ADMIN_HOST authority at config parse
The bare-IPv6 bracket guard names the honest `::1` shape but skips
unclosed-bracket typos like `[::1` and `[::1:3000` — they start with
`[`, pass the guard, then interpolate into an unparseable
`http://[::1` NIP-11 advertisement and NIP-98 u-tag URL. Same defect
class as the bare-IPv6 case, just a typo shape.
Add a catch-all after the bracket guard: url::Url::parse("http://{host}")
must succeed, else reject with an error naming the host. This is a
validity gate only — the host is still stored verbatim, not normalized.
It kills every malformed authority in one guard, including shapes not
enumerated. url is already a buzz-relay dep.
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
@@ -1082,6 +1082,22 @@ impl Config {
|
||||
)));
|
||||
}
|
||||
|
||||
// Catch-all authority gate: every accepted host is interpolated
|
||||
// verbatim into the NIP-11 advertisement and NIP-98 `u`-tag URLs,
|
||||
// so it must form a parseable authority. The bracket guard above
|
||||
// names the honest bare-IPv6 shape; this rejects any remaining
|
||||
// malformed authority (e.g. the unclosed-bracket typos `[::1`
|
||||
// and `[::1:3000`) that would otherwise produce an unparseable
|
||||
// `http://{host}`. This is a validity gate only — the host is
|
||||
// still stored verbatim, not normalized.
|
||||
if url::Url::parse(&format!("http://{host}")).is_err() {
|
||||
return Err(ConfigError::InvalidValue(format!(
|
||||
"BUZZ_ADMIN_HOST={host} is not a valid URL authority; \
|
||||
it must be a host with an optional port, e.g. \
|
||||
relay.example.com:8443 or [::1]:3000"
|
||||
)));
|
||||
}
|
||||
|
||||
// Parse BUZZ_ADMIN_AUTH. Accepted values: "token" (default when
|
||||
// unset), "disabled", "nip98". Any other non-empty value is a
|
||||
// startup error (typo-proofing).
|
||||
@@ -1446,6 +1462,29 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_host_malformed_authority_fails_closed() {
|
||||
// Shapes that slip the bare-IPv6 bracket guard (they start with `[`)
|
||||
// but still cannot form a valid URL authority — the unclosed-bracket
|
||||
// typos. Without the catch-all parse gate these would produce an
|
||||
// unparseable `http://[::1` advertisement and NIP-98 `u`-tag URL.
|
||||
let _guard = ENV_MUTEX.lock().unwrap();
|
||||
for host in ["[::1", "[::1:3000", "[not-closed"] {
|
||||
let result = config_with_admin_env(&[
|
||||
("BUZZ_ADMIN_HOST", Some(host)),
|
||||
("BUZZ_ADMIN_TOKEN", Some(VALID_ADMIN_TOKEN)),
|
||||
]);
|
||||
assert!(
|
||||
matches!(
|
||||
result,
|
||||
Err(ConfigError::InvalidValue(ref message))
|
||||
if message.contains("BUZZ_ADMIN_HOST") && message.contains("valid URL authority")
|
||||
),
|
||||
"malformed authority {host:?} must be rejected: {result:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_host_bracketed_ipv6_literal_is_accepted() {
|
||||
let _guard = ENV_MUTEX.lock().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user