From e47dcd3f74e45420e863407e67a5382dc387ea07 Mon Sep 17 00:00:00 2001 From: scanash00 Date: Thu, 13 Aug 2026 16:48:46 -0800 Subject: [PATCH] extra fixes --- Cargo.lock | 1 + crates/tranquil-oauth/Cargo.toml | 3 ++- crates/tranquil-oauth/src/client.rs | 39 ++++++++++++++++++++++++++--- crates/tranquil-pds/Cargo.toml | 1 + 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e8f6f7f..7e59498 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5923,6 +5923,7 @@ dependencies = [ "pin-project-lite", "quinn", "rustls 0.23.37", + "rustls-native-certs", "rustls-pki-types", "serde", "serde_json", diff --git a/crates/tranquil-oauth/Cargo.toml b/crates/tranquil-oauth/Cargo.toml index 5194281..5f6102f 100644 --- a/crates/tranquil-oauth/Cargo.toml +++ b/crates/tranquil-oauth/Cargo.toml @@ -25,4 +25,5 @@ tracing = { workspace = true } uuid = { workspace = true } [features] -native-tls-roots = [] +native-tls-roots = ["reqwest/rustls-tls-native-roots"] +test-http-loopback = [] diff --git a/crates/tranquil-oauth/src/client.rs b/crates/tranquil-oauth/src/client.rs index eb05a0c..116426a 100644 --- a/crates/tranquil-oauth/src/client.rs +++ b/crates/tranquil-oauth/src/client.rs @@ -78,7 +78,17 @@ struct CachedJwks { async fn validate_outbound_url(url: &str, field: &str) -> Result<(), OAuthError> { let parsed = reqwest::Url::parse(url) .map_err(|_| OAuthError::InvalidClient(format!("{field} must be a valid URL")))?; - if parsed.scheme() != "https" { + #[cfg(feature = "test-http-loopback")] + let is_dev_loopback = parsed.scheme() == "http" + && parsed.host_str().is_some_and(|host| { + host == "localhost" + || host + .parse::() + .is_ok_and(|address| address.is_loopback()) + }); + #[cfg(not(feature = "test-http-loopback"))] + let is_dev_loopback = false; + if parsed.scheme() != "https" && !is_dev_loopback { return Err(OAuthError::InvalidClient(format!("{field} must use https"))); } if !parsed.username().is_empty() || parsed.password().is_some() { @@ -89,6 +99,9 @@ async fn validate_outbound_url(url: &str, field: &str) -> Result<(), OAuthError> let host = parsed .host_str() .ok_or_else(|| OAuthError::InvalidClient(format!("{field} must include a host")))?; + if is_dev_loopback { + return Ok(()); + } if let Ok(ip) = host.parse::() { return is_public_ip(&ip) .then_some(()) @@ -163,8 +176,6 @@ impl ClientMetadataCache { env!("CARGO_PKG_VERSION"), " (ATProto; +https://tangled.org/tranquil.farm/tranquil-pds)" )); - #[cfg(feature = "native-tls-roots")] - let builder = builder.danger_accept_invalid_certs(true); builder.build().unwrap_or_else(|_| Client::new()) }, cache_ttl_secs, @@ -778,12 +789,32 @@ fn verify_eddsa( #[cfg(test)] mod tests { - use super::client_assertion_audience_matches; + use super::{client_assertion_audience_matches, validate_outbound_url}; use serde_json::json; const ISSUER: &str = "https://margin.cafe"; const TOKEN_ENDPOINT: &str = "https://margin.cafe/oauth/token"; + #[cfg(not(feature = "test-http-loopback"))] + #[tokio::test] + async fn outbound_url_rejects_http_loopback_by_default() { + assert!( + validate_outbound_url("http://127.0.0.1:3000/client-metadata.json", "client_id") + .await + .is_err() + ); + } + + #[cfg(feature = "test-http-loopback")] + #[tokio::test] + async fn outbound_url_allows_http_loopback_for_tests() { + assert!( + validate_outbound_url("http://127.0.0.1:3000/client-metadata.json", "client_id") + .await + .is_ok() + ); + } + #[test] fn client_assertion_audience_accepts_authorization_server_issuer() { let audience = json!(ISSUER); diff --git a/crates/tranquil-pds/Cargo.toml b/crates/tranquil-pds/Cargo.toml index 0cb3eb8..763d788 100644 --- a/crates/tranquil-pds/Cargo.toml +++ b/crates/tranquil-pds/Cargo.toml @@ -96,6 +96,7 @@ testcontainers-modules = { workspace = true } tranquil-ripple = { workspace = true } tranquil-sync = { workspace = true } tranquil-api = { workspace = true } +tranquil-oauth = { workspace = true, features = ["test-http-loopback"] } tranquil-oauth-server = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter"] } wiremock = { workspace = true } -- 2.51.2