diff --git a/crates/uv-configuration/src/trusted_host.rs b/crates/uv-configuration/src/trusted_host.rs index 910fe6991de04..0d63dd5cd37a3 100644 --- a/crates/uv-configuration/src/trusted_host.rs +++ b/crates/uv-configuration/src/trusted_host.rs @@ -1,8 +1,7 @@ -use serde::{Deserialize, Serialize}; use url::Url; /// A trusted host, which could be a host or a host-port pair. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct TrustedHost { scheme: Option, host: String, @@ -32,6 +31,26 @@ impl TrustedHost { } } +impl<'de> serde::de::Deserialize<'de> for TrustedHost { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + s.parse().map_err(serde::de::Error::custom) + } +} + +impl serde::Serialize for TrustedHost { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let s = self.to_string(); + serializer.serialize_str(&s) + } +} + #[derive(Debug, thiserror::Error)] pub enum TrustedHostError { #[error("missing host for `--trusted-host`: `{0}`")] @@ -73,6 +92,22 @@ impl std::str::FromStr for TrustedHost { } } +impl std::fmt::Display for TrustedHost { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if let Some(scheme) = &self.scheme { + write!(f, "{}://{}", scheme, self.host)?; + } else { + write!(f, "{}", self.host)?; + } + + if let Some(port) = self.port { + write!(f, ":{}", port)?; + } + + Ok(()) + } +} + #[cfg(feature = "schemars")] impl schemars::JsonSchema for TrustedHost { fn schema_name() -> String { diff --git a/crates/uv/tests/show_settings.rs b/crates/uv/tests/show_settings.rs index d6c4fd8aecc42..7e9d8efa9e128 100644 --- a/crates/uv/tests/show_settings.rs +++ b/crates/uv/tests/show_settings.rs @@ -3407,3 +3407,143 @@ fn resolve_skip_empty() -> anyhow::Result<()> { Ok(()) } + +/// Deserialize an insecure host. +#[test] +#[cfg_attr( + windows, + ignore = "Configuration tests are not yet supported on Windows" +)] +fn allow_insecure_host() -> anyhow::Result<()> { + let context = TestContext::new("3.12"); + + let config = context.temp_dir.child("uv.toml"); + config.write_str(indoc::indoc! {r#" + allow-insecure-host = ["google.com"] + "#})?; + + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("anyio>3.0.0")?; + + uv_snapshot!(context.filters(), add_shared_args(context.pip_compile()) + .arg("--show-settings") + .arg("requirements.in"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + GlobalSettings { + quiet: false, + verbose: 0, + color: Auto, + native_tls: false, + concurrency: Concurrency { + downloads: 50, + builds: 16, + installs: 8, + }, + connectivity: Online, + show_settings: true, + preview: Disabled, + python_preference: OnlySystem, + python_downloads: Automatic, + no_progress: false, + } + CacheSettings { + no_cache: false, + cache_dir: Some( + "[CACHE_DIR]/", + ), + } + PipCompileSettings { + src_file: [ + "requirements.in", + ], + constraint: [], + override: [], + build_constraint: [], + constraints_from_workspace: [], + overrides_from_workspace: [], + environments: SupportedEnvironments( + [], + ), + refresh: None( + Timestamp( + SystemTime { + tv_sec: [TIME], + tv_nsec: [TIME], + }, + ), + ), + settings: PipSettings { + index_locations: IndexLocations { + index: None, + extra_index: [], + flat_index: [], + no_index: false, + }, + python: None, + system: false, + extras: None, + break_system_packages: false, + target: None, + prefix: None, + index_strategy: FirstIndex, + keyring_provider: Disabled, + allow_insecure_host: [ + TrustedHost { + scheme: None, + host: "google.com", + port: None, + }, + ], + no_build_isolation: false, + no_build_isolation_package: [], + build_options: BuildOptions { + no_binary: None, + no_build: None, + }, + allow_empty_requirements: false, + strict: false, + dependency_mode: Transitive, + resolution: Highest, + prerelease: IfNecessaryOrExplicit, + output_file: None, + no_strip_extras: false, + no_strip_markers: false, + no_annotate: false, + no_header: false, + custom_compile_command: None, + generate_hashes: false, + config_setting: ConfigSettings( + {}, + ), + python_version: None, + python_platform: None, + universal: false, + exclude_newer: Some( + ExcludeNewer( + 2024-03-25T00:00:00Z, + ), + ), + no_emit_package: [], + emit_index_url: false, + emit_find_links: false, + emit_build_options: false, + emit_marker_expression: false, + emit_index_annotation: false, + annotation_style: Split, + link_mode: Clone, + compile_bytecode: false, + sources: Enabled, + hash_checking: None, + upgrade: None, + reinstall: None, + }, + } + + ----- stderr ----- + "### + ); + + Ok(()) +}