From 3ace1e83a65bf92a29126288df615e93bd1809eb Mon Sep 17 00:00:00 2001 From: npub12wpjffj7q5qjsky5jvk4ldwlxmse5xll3d8gytk4wqd0c5y7jvwspg37n6 <538324a65e0501285894932d5fb5df36e19a1bff8b4e822ed5701afc509e931d@buzz.block.builderlab.xyz> Date: Fri, 31 Jul 2026 10:20:57 -0700 Subject: [PATCH] revert: preserve canonical HTTPS push delivery Co-authored-by: Tom Brow Signed-off-by: Tom Brow --- crates/buzz-relay/src/config.rs | 115 ++------------------------------ 1 file changed, 6 insertions(+), 109 deletions(-) diff --git a/crates/buzz-relay/src/config.rs b/crates/buzz-relay/src/config.rs index f203e060e..85a0ca2ef 100644 --- a/crates/buzz-relay/src/config.rs +++ b/crates/buzz-relay/src/config.rs @@ -354,53 +354,14 @@ fn parse_operator_api_origin(raw: &str) -> Result { } const DEFAULT_PUSH_GATEWAY_DELIVERY_URL: &str = "https://push.buzz.xyz/v1/deliveries/apns"; -const DEV_PUSH_GATEWAY_LOOPBACK_HTTP_ENV: &str = "BUZZ_PUSH_DEV_LOOPBACK_HTTP"; -fn parse_dev_push_gateway_loopback_http(raw: Option<&str>) -> Result { - match raw { - None | Some("0") => Ok(false), - Some("1") if cfg!(debug_assertions) => Ok(true), - Some("1") => Err(ConfigError::InvalidValue(format!( - "{DEV_PUSH_GATEWAY_LOOPBACK_HTTP_ENV}=1 is unavailable outside Debug builds" - ))), - Some(_) => Err(ConfigError::InvalidValue(format!( - "{DEV_PUSH_GATEWAY_LOOPBACK_HTTP_ENV} must be exactly 0 or 1" - ))), - } -} - -fn is_debug_loopback_push_gateway_url(url: &url::Url, allow_loopback_http: bool) -> bool { - #[cfg(debug_assertions)] - { - if !allow_loopback_http || url.scheme() != "http" { - return false; - } - - match url.host() { - Some(url::Host::Domain(host)) => host == "localhost", - Some(url::Host::Ipv4(address)) => address == std::net::Ipv4Addr::LOCALHOST, - Some(url::Host::Ipv6(address)) => address == std::net::Ipv6Addr::LOCALHOST, - None => false, - } - } - - #[cfg(not(debug_assertions))] - { - let _ = (url, allow_loopback_http); - false - } -} - -fn parse_push_gateway_delivery_url( - raw: &str, - allow_loopback_http: bool, -) -> Result { +fn parse_push_gateway_delivery_url(raw: &str) -> Result { let url = url::Url::parse(raw.trim()).map_err(|e| { ConfigError::InvalidValue(format!( "BUZZ_PUSH_GATEWAY_DELIVERY_URL is not a valid URL: {e}" )) })?; - if (url.scheme() != "https" && !is_debug_loopback_push_gateway_url(&url, allow_loopback_http)) + if url.scheme() != "https" || url.host().is_none() || !url.username().is_empty() || url.password().is_some() @@ -409,7 +370,7 @@ fn parse_push_gateway_delivery_url( || url.fragment().is_some() { return Err(ConfigError::InvalidValue( - "BUZZ_PUSH_GATEWAY_DELIVERY_URL must be an exact HTTPS /v1/deliveries/apns URL without credentials, query, or fragment (explicitly gated Debug builds also allow exact loopback HTTP URLs)" + "BUZZ_PUSH_GATEWAY_DELIVERY_URL must be an exact HTTPS /v1/deliveries/apns URL without credentials, query, or fragment" .to_string(), )); } @@ -848,31 +809,13 @@ impl Config { "BUZZ_PUSH_EXECUTOR_KEY_ID must contain 1..=64 bytes".to_string(), )); } - let dev_push_gateway_loopback_http = parse_dev_push_gateway_loopback_http( - std::env::var(DEV_PUSH_GATEWAY_LOOPBACK_HTTP_ENV) - .ok() - .as_deref(), - )?; let push_gateway_delivery_url = match std::env::var("BUZZ_PUSH_GATEWAY_DELIVERY_URL") { Ok(raw) if raw.trim().is_empty() => None, - Ok(raw) => Some(parse_push_gateway_delivery_url( - &raw, - dev_push_gateway_loopback_http, - )?), + Ok(raw) => Some(parse_push_gateway_delivery_url(&raw)?), Err(_) => Some(parse_push_gateway_delivery_url( DEFAULT_PUSH_GATEWAY_DELIVERY_URL, - dev_push_gateway_loopback_http, )?), }; - if dev_push_gateway_loopback_http - && push_gateway_delivery_url - .as_ref() - .is_some_and(|url| url.scheme() == "http") - { - warn!( - "DEVELOPMENT ONLY: relay push delivery permits plaintext HTTP to an exact loopback gateway" - ); - } let push_gateway_timeout_millis = match std::env::var("BUZZ_PUSH_GATEWAY_TIMEOUT_MS") { Ok(raw) => raw .parse::() @@ -1500,64 +1443,18 @@ mod tests { #[test] fn push_gateway_url_is_exact_and_fail_closed() { - assert!( - parse_push_gateway_delivery_url("https://push.example/v1/deliveries/apns", false) - .is_ok() - ); + assert!(parse_push_gateway_delivery_url("https://push.example/v1/deliveries/apns").is_ok()); for invalid in [ "http://push.example/v1/deliveries/apns", - "http://localhost.example/v1/deliveries/apns", - "http://localhost@push.example/v1/deliveries/apns", - "http://127.0.0.2/v1/deliveries/apns", - "http://192.168.1.10/v1/deliveries/apns", "https://push.example/v1/deliveries/apns/", "https://push.example/v1/deliveries/apns?token=x", "https://user@push.example/v1/deliveries/apns", ] { assert!( - parse_push_gateway_delivery_url(invalid, true).is_err(), + parse_push_gateway_delivery_url(invalid).is_err(), "{invalid}" ); } - assert!( - parse_push_gateway_delivery_url("http://localhost:8080/v1/deliveries/apns", false,) - .is_err(), - "loopback HTTP must default off" - ); - } - - #[test] - fn push_gateway_http_loopback_requires_debug_gate() { - for url in [ - "http://localhost:8080/v1/deliveries/apns", - "http://127.0.0.1:8080/v1/deliveries/apns", - "http://[::1]:8080/v1/deliveries/apns", - ] { - let result = parse_push_gateway_delivery_url(url, true); - if cfg!(debug_assertions) { - assert!(result.is_ok(), "{url}: {result:?}"); - } else { - assert!(result.is_err(), "{url}"); - } - } - } - - #[test] - fn push_gateway_http_loopback_flag_is_strict_and_debug_only() { - assert!(!parse_dev_push_gateway_loopback_http(None).unwrap()); - assert!(!parse_dev_push_gateway_loopback_http(Some("0")).unwrap()); - for invalid in ["", "true", "on", "01", " 1"] { - assert!( - parse_dev_push_gateway_loopback_http(Some(invalid)).is_err(), - "{invalid:?}" - ); - } - let enabled = parse_dev_push_gateway_loopback_http(Some("1")); - if cfg!(debug_assertions) { - assert!(enabled.unwrap()); - } else { - assert!(enabled.is_err()); - } } #[test]