diff --git a/Cargo.lock b/Cargo.lock index be8838d..1aab2b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3199,6 +3199,7 @@ dependencies = [ "tokio", "toml", "tower-service", + "url", "wasm-bindgen", "webp", "worker", diff --git a/Cargo.toml b/Cargo.toml index eee089f..51f706e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ cf-worker = ["dep:worker", "dep:worker-macros"] cf-worker-paid = ["cf-worker", "resvg/raster-images", "resvg/text", "image/ico", "panic-console-error"] panic-console-error = ["dep:console_error_panic_hook"] apparmor = ["dep:siphasher", "dep:libc"] -reqwest = ["dep:reqwest"] +reqwest = ["dep:reqwest", "dep:url"] svg-text = ["resvg/text", "dep:fontdb"] tokio = ["dep:tokio", "axum/tokio"] env_logger = ["dep:env_logger"] @@ -77,6 +77,7 @@ libc = { version = "0.2.162", optional = true } axum-server = { version = "0.7.1", optional = true } fontdb = { version = "0.23", optional = true } webp = { version = "0.3.0", optional = true } +url = { version = "2", optional = true } [patch.crates-io] # licensing and webp dependencies diff --git a/src/fetch/mod.rs b/src/fetch/mod.rs index d2784ae..3bb624d 100644 --- a/src/fetch/mod.rs +++ b/src/fetch/mod.rs @@ -147,6 +147,7 @@ pub mod reqwest { use futures::TryStreamExt; use reqwest::dns::Resolve; use std::{net::SocketAddrV4, sync::Arc, time::Duration}; + use url::Host; /// A Safe DNS resolver that only resolves to global addresses unless the requester itself is local. pub struct SafeResolver(AddrFamilyConfig); @@ -308,8 +309,14 @@ pub mod reqwest { } let url_parsed = Url::parse(url).map_err(|_| ErrorResponse::bad_url())?; - secure &= url_parsed.scheme().eq_ignore_ascii_case("https"); + if !url_parsed.host().map_or(false, |h| match h { + Host::Domain(_) => true, + _ => false, + }) { + return Err(ErrorResponse::non_dns_name()); + } + secure &= url_parsed.scheme().eq_ignore_ascii_case("https"); if self.https_only && !secure { return Err(ErrorResponse::insecure_request()); } diff --git a/src/lib.rs b/src/lib.rs index 5418b59..96246a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -424,6 +424,14 @@ impl Display for ErrorResponse { impl std::error::Error for ErrorResponse {} impl ErrorResponse { + #[cfg(not(feature = "cf-worker"))] + /// URL must be a DNS name + pub const fn non_dns_name() -> Self { + Self { + status: StatusCode::BAD_REQUEST, + message: Cow::Borrowed("URL must be a DNS name"), + } + } /// Entropy source exhausted #[must_use] pub const fn entropy_exhausted() -> Self {