diff --git a/Cargo.lock b/Cargo.lock index a2f188be3762..ab5cdc1d0c20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5164,6 +5164,7 @@ dependencies = [ "thiserror", "toml", "tracing", + "url", "uv-configuration", "uv-fs", "uv-macros", diff --git a/crates/uv-cli/src/compat.rs b/crates/uv-cli/src/compat.rs index 518e480d585e..8f0147157636 100644 --- a/crates/uv-cli/src/compat.rs +++ b/crates/uv-cli/src/compat.rs @@ -39,9 +39,6 @@ pub struct PipCompileCompatArgs { #[clap(long, hide = true)] client_cert: Option, - #[clap(long, hide = true)] - trusted_host: Option, - #[clap(long, hide = true)] emit_trusted_host: bool, @@ -118,15 +115,9 @@ impl CompatArgs for PipCompileCompatArgs { )); } - if self.trusted_host.is_some() { - return Err(anyhow!( - "pip-compile's `--trusted-host` is unsupported (uv always requires HTTPS)" - )); - } - if self.emit_trusted_host { return Err(anyhow!( - "pip-compile's `--emit-trusted-host` is unsupported (uv always requires HTTPS)" + "pip-compile's `--emit-trusted-host` is unsupported" )); } @@ -209,9 +200,6 @@ pub struct PipSyncCompatArgs { #[clap(short, long, hide = true)] ask: bool, - #[clap(long, hide = true)] - trusted_host: Option, - #[clap(long, hide = true)] python_executable: Option, @@ -265,12 +253,6 @@ impl CompatArgs for PipSyncCompatArgs { )); } - if self.trusted_host.is_some() { - return Err(anyhow!( - "pip-sync's `--trusted-host` is unsupported (uv always requires HTTPS)" - )); - } - if self.config.is_some() { return Err(anyhow!( "pip-sync's `--config` is unsupported (uv does not use a configuration file)" diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 729a93c09831..f121f8cb5cc9 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -6,10 +6,10 @@ use std::str::FromStr; use anyhow::{anyhow, Result}; use clap::builder::styling::Style; use clap::{Args, Parser, Subcommand}; - use distribution_types::{FlatIndexLocation, IndexUrl}; use pep508_rs::Requirement; use pypi_types::VerbatimParsedUrl; +use url::Url; use uv_cache::CacheArgs; use uv_configuration::{ ConfigSettingEntry, IndexStrategy, KeyringProviderType, PackageNameSpecifier, TargetTriple, @@ -678,6 +678,18 @@ fn parse_index_url(input: &str) -> Result, String> { } } +/// Parse a string into an [`Url`], mapping the empty string to `None`. +fn parse_maybe_url(input: &str) -> Result, String> { + if input.is_empty() { + Ok(Maybe::None) + } else { + match Url::parse(input) { + Ok(url) => Ok(Maybe::Some(url)), + Err(err) => Err(err.to_string()), + } + } +} + /// Parse a string into a [`PathBuf`]. The string can represent a file, either as a path or a /// `file://` URL. fn parse_file_path(input: &str) -> Result { @@ -1559,6 +1571,18 @@ pub struct PipUninstallArgs { #[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")] pub keyring_provider: Option, + /// A list of trusted hostnames for SSL connections. + /// + /// WARNING: Hosts included in this list will not be verified against the system's certificate + /// store. + #[arg( + long, + env = "UV_TRUSTED_HOST", + value_delimiter = ' ', + value_parser = parse_maybe_url, + )] + pub trusted_host: Option>>, + /// Use the system Python to uninstall packages. /// /// By default, uv uninstalls from the virtual environment in the current working directory or @@ -1985,6 +2009,18 @@ pub struct VenvArgs { #[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")] pub keyring_provider: Option, + /// A list of trusted hostnames for SSL connections. + /// + /// WARNING: Hosts included in this list will not be verified against the system's certificate + /// store. + #[arg( + long, + env = "UV_TRUSTED_HOST", + value_delimiter = ' ', + value_parser = parse_maybe_url, + )] + pub trusted_host: Option>>, + /// Limit candidate packages to those that were uploaded prior to the given date. /// /// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same @@ -3324,6 +3360,19 @@ pub struct InstallerArgs { )] pub keyring_provider: Option, + /// A list of trusted hostnames for SSL connections. + /// + /// WARNING: Hosts included in this list will not be verified against the system's certificate + /// store. + #[arg( + long, + env = "UV_TRUSTED_HOST", + value_delimiter = ' ', + value_parser = parse_maybe_url, + help_heading = "Index options" + )] + pub trusted_host: Option>>, + /// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs. #[arg( long, @@ -3466,6 +3515,19 @@ pub struct ResolverArgs { )] pub keyring_provider: Option, + /// A list of trusted hostnames for SSL connections. + /// + /// WARNING: Hosts included in this list will not be verified against the system's certificate + /// store. + #[arg( + long, + env = "UV_TRUSTED_HOST", + value_delimiter = ' ', + value_parser = parse_maybe_url, + help_heading = "Index options" + )] + pub trusted_host: Option>>, + /// The strategy to use when selecting between the different compatible versions for a given /// package requirement. /// @@ -3638,6 +3700,19 @@ pub struct ResolverInstallerArgs { )] pub keyring_provider: Option, + /// A list of trusted hostnames for SSL connections. + /// + /// WARNING: Hosts included in this list will not be verified against the system's certificate + /// store. + #[arg( + long, + env = "UV_TRUSTED_HOST", + value_delimiter = ' ', + value_parser = parse_maybe_url, + help_heading = "Index options" + )] + pub trusted_host: Option>>, + /// The strategy to use when selecting between the different compatible versions for a given /// package requirement. /// diff --git a/crates/uv-cli/src/options.rs b/crates/uv-cli/src/options.rs index bbf7133e9626..edf16adb2f22 100644 --- a/crates/uv-cli/src/options.rs +++ b/crates/uv-cli/src/options.rs @@ -38,6 +38,7 @@ impl From for PipOptions { upgrade_package, index_strategy, keyring_provider, + trusted_host, resolution, prerelease, pre, @@ -55,6 +56,12 @@ impl From for PipOptions { upgrade_package: Some(upgrade_package), index_strategy, keyring_provider, + trusted_host: trusted_host.map(|trusted_host| { + trusted_host + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), resolution, prerelease: if pre { Some(PrereleaseMode::Allow) @@ -82,6 +89,7 @@ impl From for PipOptions { reinstall_package, index_strategy, keyring_provider, + trusted_host, config_setting, no_build_isolation, build_isolation, @@ -97,6 +105,12 @@ impl From for PipOptions { reinstall_package: Some(reinstall_package), index_strategy, keyring_provider, + trusted_host: trusted_host.map(|trusted_host| { + trusted_host + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), config_settings: config_setting .map(|config_settings| config_settings.into_iter().collect::()), no_build_isolation: flag(no_build_isolation, build_isolation), @@ -121,6 +135,7 @@ impl From for PipOptions { reinstall_package, index_strategy, keyring_provider, + trusted_host, resolution, prerelease, pre, @@ -142,6 +157,12 @@ impl From for PipOptions { reinstall_package: Some(reinstall_package), index_strategy, keyring_provider, + trusted_host: trusted_host.map(|trusted_host| { + trusted_host + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), resolution, prerelease: if pre { Some(PrereleaseMode::Allow) @@ -194,6 +215,7 @@ pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> R upgrade_package, index_strategy, keyring_provider, + trusted_host, resolution, prerelease, pre, @@ -233,6 +255,12 @@ pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> R upgrade_package: Some(upgrade_package), index_strategy, keyring_provider, + trusted_host: trusted_host.map(|trusted_host| { + trusted_host + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), resolution, prerelease: if pre { Some(PrereleaseMode::Allow) @@ -268,6 +296,7 @@ pub fn resolver_installer_options( reinstall_package, index_strategy, keyring_provider, + trusted_host, resolution, prerelease, pre, @@ -319,6 +348,12 @@ pub fn resolver_installer_options( }, index_strategy, keyring_provider, + trusted_host: trusted_host.map(|trusted_host| { + trusted_host + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), resolution, prerelease: if pre { Some(PrereleaseMode::Allow) diff --git a/crates/uv-client/src/base_client.rs b/crates/uv-client/src/base_client.rs index 81e417749fc1..4c611f44389a 100644 --- a/crates/uv-client/src/base_client.rs +++ b/crates/uv-client/src/base_client.rs @@ -1,10 +1,11 @@ use std::error::Error; use std::fmt::Debug; -use std::ops::Deref; use std::path::Path; use std::{env, iter}; use itertools::Itertools; +use pep508_rs::MarkerEnvironment; +use platform_tags::Platform; use reqwest::{Client, ClientBuilder, Response}; use reqwest_middleware::ClientWithMiddleware; use reqwest_retry::policies::ExponentialBackoff; @@ -13,8 +14,6 @@ use reqwest_retry::{ }; use tracing::debug; use url::Url; -use pep508_rs::MarkerEnvironment; -use platform_tags::Platform; use uv_auth::AuthMiddleware; use uv_configuration::KeyringProviderType; use uv_fs::Simplified; @@ -24,12 +23,13 @@ use uv_warnings::warn_user_once; use crate::linehaul::LineHaul; use crate::middleware::OfflineMiddleware; use crate::tls::read_identity; -use crate::{CachedClient, Connectivity}; +use crate::Connectivity; /// A builder for an [`BaseClient`]. #[derive(Debug, Clone)] pub struct BaseClientBuilder<'a> { keyring: KeyringProviderType, + trusted_host: Vec, native_tls: bool, retries: u32, pub connectivity: Connectivity, @@ -48,6 +48,7 @@ impl BaseClientBuilder<'_> { pub fn new() -> Self { Self { keyring: KeyringProviderType::default(), + trusted_host: vec![], native_tls: false, connectivity: Connectivity::Online, retries: 3, @@ -65,6 +66,12 @@ impl<'a> BaseClientBuilder<'a> { self } + #[must_use] + pub fn trusted_host(mut self, trusted_host: Vec) -> Self { + self.trusted_host = trusted_host; + self + } + #[must_use] pub fn connectivity(mut self, connectivity: Connectivity) -> Self { self.connectivity = connectivity; @@ -109,7 +116,6 @@ impl<'a> BaseClientBuilder<'a> { // Create user agent. let mut user_agent_string = format!("uv/{}", version()); - // Add linehaul metadata. if let Some(markers) = self.markers { let linehaul = LineHaul::new(markers, self.platform); @@ -123,9 +129,9 @@ impl<'a> BaseClientBuilder<'a> { let path_exists = Path::new(&path).exists(); if !path_exists { warn_user_once!( - "Ignoring invalid `SSL_CERT_FILE`. File does not exist: {}.", - path.simplified_display().cyan() - ); + "Ignoring invalid `SSL_CERT_FILE`. File does not exist: {}.", + path.simplified_display().cyan() + ); } path_exists }); @@ -148,30 +154,31 @@ impl<'a> BaseClientBuilder<'a> { debug!("Using request timeout of {timeout}s"); // Create a secure client that validates certificates. - let client = - self.create_client(&user_agent_string, timeout, false, ssl_cert_file_exists); + let client = self.create_client( + &user_agent_string, + timeout, + ssl_cert_file_exists, + Security::Secure, + ); // Create an insecure client that accepts invalid certificates. - let dangerous_client = - self.create_client(&user_agent_string, timeout, true, ssl_cert_file_exists); - + let dangerous_client = self.create_client( + &user_agent_string, + timeout, + ssl_cert_file_exists, + Security::Insecure, + ); // Wrap in any relevant middleware and handle connectivity. - let client = match self.connectivity { - Connectivity::Online => self.apply_middleware(client), - Connectivity::Offline => self.apply_offline_middleware(client), - }; - let dangerous_client = match self.connectivity { - Connectivity::Online => self.apply_middleware(dangerous_client), - Connectivity::Offline => self.apply_offline_middleware(dangerous_client), - }; + let client = self.apply_middleware(client); + let dangerous_client = self.apply_middleware(dangerous_client); BaseClient { connectivity: self.connectivity, client, dangerous_client, timeout, - trusted_host: vec![] + trusted_host: vec![], } } @@ -179,16 +186,21 @@ impl<'a> BaseClientBuilder<'a> { &self, user_agent: &str, timeout: u64, - accept_invalid_certs: bool, ssl_cert_file_exists: bool, + security: Security, ) -> Client { // Configure the builder. let client_builder = ClientBuilder::new() .user_agent(user_agent) .pool_max_idle_per_host(20) .read_timeout(std::time::Duration::from_secs(timeout)) - .tls_built_in_root_certs(false) - .danger_accept_invalid_certs(accept_invalid_certs); + .tls_built_in_root_certs(false); + + // If necessary, accept invalid certificates. + let client_builder = match security { + Security::Secure => client_builder, + Security::Insecure => client_builder.danger_accept_invalid_certs(true), + }; let client_builder = if self.native_tls || ssl_cert_file_exists { client_builder.tls_built_in_native_certs(true) @@ -215,29 +227,29 @@ impl<'a> BaseClientBuilder<'a> { } fn apply_middleware(&self, client: Client) -> ClientWithMiddleware { - let client = reqwest_middleware::ClientBuilder::new(client.clone()); - - // Initialize the retry strategy. - let retry_policy = - ExponentialBackoff::builder().build_with_max_retries(self.retries); - let retry_strategy = RetryTransientMiddleware::new_with_policy_and_strategy( - retry_policy, - UvRetryableStrategy, - ); - let client = client.with(retry_strategy); - - // Initialize the authentication middleware to set headers. - let client = - client.with(AuthMiddleware::new().with_keyring(self.keyring.to_provider())); - - - client.build() - } - - fn apply_offline_middleware(&self, client: Client) -> ClientWithMiddleware { - reqwest_middleware::ClientBuilder::new(client) - .with(OfflineMiddleware) - .build() + match self.connectivity { + Connectivity::Online => { + let client = reqwest_middleware::ClientBuilder::new(client); + + // Initialize the retry strategy. + let retry_policy = + ExponentialBackoff::builder().build_with_max_retries(self.retries); + let retry_strategy = RetryTransientMiddleware::new_with_policy_and_strategy( + retry_policy, + UvRetryableStrategy, + ); + let client = client.with(retry_strategy); + + // Initialize the authentication middleware to set headers. + let client = + client.with(AuthMiddleware::new().with_keyring(self.keyring.to_provider())); + + client.build() + } + Connectivity::Offline => reqwest_middleware::ClientBuilder::new(client) + .with(OfflineMiddleware) + .build(), + } } } @@ -256,6 +268,14 @@ pub struct BaseClient { trusted_host: Vec, } +#[derive(Debug, Clone, Copy)] +enum Security { + /// The client should use secure settings, i.e., valid certificates. + Secure, + /// The client should use insecure settings, i.e., skip certificate validation. + Insecure, +} + impl BaseClient { /// The underlying [`ClientWithMiddleware`] for secure requests. pub fn client(&self) -> ClientWithMiddleware { @@ -265,7 +285,9 @@ impl BaseClient { /// Selects the appropriate client based on the host's trustworthiness. pub fn for_host(&self, url: &Url) -> &ClientWithMiddleware { if self - .trusted_host.iter().any(|trusted| url.host() == trusted.host()) + .trusted_host + .iter() + .any(|trusted| url.host() == trusted.host()) { &self.dangerous_client } else { diff --git a/crates/uv-client/src/registry_client.rs b/crates/uv-client/src/registry_client.rs index 89bc7bf86d72..f647a257f8ff 100644 --- a/crates/uv-client/src/registry_client.rs +++ b/crates/uv-client/src/registry_client.rs @@ -26,7 +26,7 @@ use uv_configuration::IndexStrategy; use uv_configuration::KeyringProviderType; use uv_normalize::PackageName; -use crate::base_client::{BaseClient, BaseClientBuilder}; +use crate::base_client::BaseClientBuilder; use crate::cached_client::CacheControl; use crate::html::SimpleHtml; use crate::remote_metadata::wheel_metadata_from_remote_zip; @@ -72,6 +72,12 @@ impl<'a> RegistryClientBuilder<'a> { self } + #[must_use] + pub fn trusted_host(mut self, trusted_host: Vec) -> Self { + self.base_client_builder = self.base_client_builder.trusted_host(trusted_host); + self + } + #[must_use] pub fn connectivity(mut self, connectivity: Connectivity) -> Self { self.base_client_builder = self.base_client_builder.connectivity(connectivity); @@ -299,7 +305,7 @@ impl RegistryClient { cache_control: CacheControl, ) -> Result, Error> { let simple_request = self - .uncached_client(&url) + .uncached_client(url) .get(url.clone()) .header("Accept-Encoding", "gzip") .header("Accept", MediaType::accepts()) @@ -552,7 +558,7 @@ impl RegistryClient { }; let req = self - .uncached_client(&url) + .uncached_client(url) .head(url.clone()) .header( "accept-encoding", @@ -572,7 +578,7 @@ impl RegistryClient { let read_metadata_range_request = |response: Response| { async { let mut reader = AsyncHttpRangeReader::from_head_response( - self.uncached_client(&url).clone(), + self.uncached_client(url).clone(), response, url.clone(), headers, @@ -620,7 +626,7 @@ impl RegistryClient { // Create a request to stream the file. let req = self - .uncached_client(&url) + .uncached_client(url) .get(url.clone()) .header( // `reqwest` defaults to accepting compressed responses. diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs index c610e71d3327..0c262e82ac61 100644 --- a/crates/uv-distribution/src/source/mod.rs +++ b/crates/uv-distribution/src/source/mod.rs @@ -1118,7 +1118,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { .git() .fetch( resource.git, - client.unmanaged.uncached_client(&resource.url).clone(), + client.unmanaged.uncached_client(resource.url).clone(), self.build_context.cache().bucket(CacheBucket::Git), self.reporter.clone().map(Facade::from), ) @@ -1188,7 +1188,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { .git() .fetch( resource.git, - client.unmanaged.uncached_client(&resource.url).clone(), + client.unmanaged.uncached_client(resource.url).clone(), self.build_context.cache().bucket(CacheBucket::Git), self.reporter.clone().map(Facade::from), ) diff --git a/crates/uv-settings/Cargo.toml b/crates/uv-settings/Cargo.toml index ea893bde5b60..34ddcf62e076 100644 --- a/crates/uv-settings/Cargo.toml +++ b/crates/uv-settings/Cargo.toml @@ -35,6 +35,7 @@ textwrap = { workspace = true } thiserror = { workspace = true } toml = { workspace = true } tracing = { workspace = true } +url = "2.5.2" [package.metadata.cargo-shear] ignored = ["uv-options-metadata", "clap"] diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index 8f580b9d6844..ba67c1503241 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -6,6 +6,7 @@ use distribution_types::{FlatIndexLocation, IndexUrl}; use install_wheel_rs::linker::LinkMode; use pep508_rs::Requirement; use pypi_types::VerbatimParsedUrl; +use url::Url; use uv_configuration::{ ConfigSettings, IndexStrategy, KeyringProviderType, PackageNameSpecifier, TargetTriple, }; @@ -215,6 +216,7 @@ pub struct InstallerOptions { pub find_links: Option>, pub index_strategy: Option, pub keyring_provider: Option, + pub trusted_host: Option>, pub config_settings: Option, pub exclude_newer: Option, pub link_mode: Option, @@ -241,6 +243,7 @@ pub struct ResolverOptions { pub find_links: Option>, pub index_strategy: Option, pub keyring_provider: Option, + pub trusted_host: Option>, pub resolution: Option, pub prerelease: Option, pub config_settings: Option, @@ -352,6 +355,18 @@ pub struct ResolverInstallerOptions { "# )] pub keyring_provider: Option, + /// A list of trusted hostnames for SSL connections. + /// + /// WARNING: Hosts included in this list will not be verified against the system's certificate + /// store. + #[option( + default = "[]", + value_type = "list[str]", + example = r#" + trusted-host = ["localhost:8080"] + "# + )] + pub trusted_host: Option>, /// The strategy to use when selecting between the different compatible versions for a given /// package requirement. /// @@ -723,6 +738,18 @@ pub struct PipOptions { "# )] pub keyring_provider: Option, + /// A list of trusted hostnames for SSL connections. + /// + /// WARNING: Hosts included in this list will not be verified against the system's certificate + /// store. + #[option( + default = "[]", + value_type = "list[str]", + example = r#" + trusted-host = ["localhost:8080"] + "# + )] + pub trusted_host: Option>, /// Don't build source distributions. /// /// When enabled, resolving will not run arbitrary Python code. The cached wheels of @@ -1210,6 +1237,7 @@ impl From for ResolverOptions { find_links: value.find_links, index_strategy: value.index_strategy, keyring_provider: value.keyring_provider, + trusted_host: value.trusted_host, resolution: value.resolution, prerelease: value.prerelease, config_settings: value.config_settings, @@ -1237,6 +1265,7 @@ impl From for InstallerOptions { find_links: value.find_links, index_strategy: value.index_strategy, keyring_provider: value.keyring_provider, + trusted_host: value.trusted_host, config_settings: value.config_settings, exclude_newer: value.exclude_newer, link_mode: value.link_mode, @@ -1269,6 +1298,7 @@ pub struct ToolOptions { pub find_links: Option>, pub index_strategy: Option, pub keyring_provider: Option, + pub trusted_host: Option>, pub resolution: Option, pub prerelease: Option, pub config_settings: Option, @@ -1293,6 +1323,7 @@ impl From for ToolOptions { find_links: value.find_links, index_strategy: value.index_strategy, keyring_provider: value.keyring_provider, + trusted_host: value.trusted_host, resolution: value.resolution, prerelease: value.prerelease, config_settings: value.config_settings, @@ -1319,6 +1350,7 @@ impl From for ResolverInstallerOptions { find_links: value.find_links, index_strategy: value.index_strategy, keyring_provider: value.keyring_provider, + trusted_host: value.trusted_host, resolution: value.resolution, prerelease: value.prerelease, config_settings: value.config_settings, diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index 29c870204bbe..684eba3043d8 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -5,13 +5,13 @@ use std::path::Path; use anstream::{eprint, AutoStream}; use anyhow::{anyhow, Result}; -use itertools::Itertools; -use owo_colors::OwoColorize; -use tracing::debug; - use distribution_types::{IndexLocations, UnresolvedRequirementSpecification, Verbatim}; use install_wheel_rs::linker::LinkMode; +use itertools::Itertools; +use owo_colors::OwoColorize; use pypi_types::Requirement; +use tracing::debug; +use url::Url; use uv_auth::store_credentials_from_url; use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; @@ -74,6 +74,7 @@ pub(crate) async fn pip_compile( index_locations: IndexLocations, index_strategy: IndexStrategy, keyring_provider: KeyringProviderType, + trusted_host: Vec, config_settings: ConfigSettings, connectivity: Connectivity, no_build_isolation: bool, @@ -106,7 +107,8 @@ pub(crate) async fn pip_compile( let client_builder = BaseClientBuilder::new() .connectivity(connectivity) .native_tls(native_tls) - .keyring(keyring_provider); + .keyring(keyring_provider) + .trusted_host(trusted_host); // Read all requirements from the provided sources. let RequirementsSpecification { diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index 0ca873e1d416..e75bfefea405 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -1,14 +1,14 @@ use std::fmt::Write; use anstream::eprint; +use distribution_types::{IndexLocations, Resolution, UnresolvedRequirementSpecification}; +use install_wheel_rs::linker::LinkMode; use itertools::Itertools; use owo_colors::OwoColorize; use pep508_rs::PackageName; -use tracing::{debug, enabled, Level}; - -use distribution_types::{IndexLocations, Resolution, UnresolvedRequirementSpecification}; -use install_wheel_rs::linker::LinkMode; use pypi_types::Requirement; +use tracing::{debug, enabled, Level}; +use url::Url; use uv_auth::store_credentials_from_url; use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; @@ -53,6 +53,7 @@ pub(crate) async fn pip_install( index_locations: IndexLocations, index_strategy: IndexStrategy, keyring_provider: KeyringProviderType, + trusted_host: Vec, reinstall: Reinstall, link_mode: LinkMode, compile: bool, @@ -83,7 +84,8 @@ pub(crate) async fn pip_install( let client_builder = BaseClientBuilder::new() .connectivity(connectivity) .native_tls(native_tls) - .keyring(keyring_provider); + .keyring(keyring_provider) + .trusted_host(trusted_host); // Read all requirements from the provided sources. let RequirementsSpecification { diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index 85f5021876ae..ff9a0ddfcfd7 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -2,12 +2,12 @@ use std::fmt::Write; use anstream::eprint; use anyhow::Result; +use distribution_types::{IndexLocations, Resolution}; +use install_wheel_rs::linker::LinkMode; use owo_colors::OwoColorize; use pep508_rs::PackageName; use tracing::debug; - -use distribution_types::{IndexLocations, Resolution}; -use install_wheel_rs::linker::LinkMode; +use url::Url; use uv_auth::store_credentials_from_url; use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; @@ -48,6 +48,7 @@ pub(crate) async fn pip_sync( index_locations: IndexLocations, index_strategy: IndexStrategy, keyring_provider: KeyringProviderType, + trusted_host: Vec, allow_empty_requirements: bool, connectivity: Connectivity, config_settings: &ConfigSettings, @@ -73,7 +74,8 @@ pub(crate) async fn pip_sync( let client_builder = BaseClientBuilder::new() .connectivity(connectivity) .native_tls(native_tls) - .keyring(keyring_provider); + .keyring(keyring_provider) + .trusted_host(trusted_host); // Initialize a few defaults. let overrides = &[]; diff --git a/crates/uv/src/commands/pip/uninstall.rs b/crates/uv/src/commands/pip/uninstall.rs index 4bfa05a0ac29..63df313b77be 100644 --- a/crates/uv/src/commands/pip/uninstall.rs +++ b/crates/uv/src/commands/pip/uninstall.rs @@ -1,14 +1,14 @@ use std::fmt::Write; use anyhow::Result; +use distribution_types::{InstalledMetadata, Name, UnresolvedRequirement}; use itertools::{Either, Itertools}; use owo_colors::OwoColorize; -use tracing::debug; - -use distribution_types::{InstalledMetadata, Name, UnresolvedRequirement}; use pep508_rs::UnnamedRequirement; use pypi_types::Requirement; use pypi_types::VerbatimParsedUrl; +use tracing::debug; +use url::Url; use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity}; use uv_configuration::KeyringProviderType; @@ -33,6 +33,7 @@ pub(crate) async fn pip_uninstall( connectivity: Connectivity, native_tls: bool, keyring_provider: KeyringProviderType, + trusted_host: Vec, printer: Printer, ) -> Result { let start = std::time::Instant::now(); @@ -40,7 +41,8 @@ pub(crate) async fn pip_uninstall( let client_builder = BaseClientBuilder::new() .connectivity(connectivity) .native_tls(native_tls) - .keyring(keyring_provider); + .keyring(keyring_provider) + .trusted_host(trusted_host); // Read all requirements from the provided sources. let spec = RequirementsSpecification::from_simple_sources(sources, &client_builder).await?; diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index 21b11ac5b674..f36bef1514fa 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -234,6 +234,7 @@ async fn do_lock( index_locations, index_strategy, keyring_provider, + trusted_host, resolution, prerelease, config_setting, @@ -342,6 +343,7 @@ async fn do_lock( .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) .keyring(keyring_provider) + .trusted_host(trusted_host.to_vec()) .markers(interpreter.markers()) .platform(interpreter.platform()) .build(); diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index 4c5258391d31..d6e3578f516c 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -410,6 +410,7 @@ pub(crate) async fn resolve_names( index_locations, index_strategy, keyring_provider, + trusted_host, resolution: _, prerelease: _, config_setting, @@ -436,6 +437,7 @@ pub(crate) async fn resolve_names( .index_urls(index_locations.index_urls()) .index_strategy(*index_strategy) .keyring(*keyring_provider) + .trusted_host(trusted_host.clone()) .markers(interpreter.markers()) .platform(interpreter.platform()) .build(); @@ -510,6 +512,7 @@ pub(crate) async fn resolve_environment<'a>( index_locations, index_strategy, keyring_provider, + trusted_host, resolution, prerelease, config_setting, @@ -549,6 +552,7 @@ pub(crate) async fn resolve_environment<'a>( .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) .keyring(keyring_provider) + .trusted_host(trusted_host.to_vec()) .markers(markers) .platform(interpreter.platform()) .build(); @@ -660,6 +664,7 @@ pub(crate) async fn sync_environment( index_locations, index_strategy, keyring_provider, + trusted_host, config_setting, no_build_isolation, exclude_newer, @@ -689,6 +694,7 @@ pub(crate) async fn sync_environment( .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) .keyring(keyring_provider) + .trusted_host(trusted_host.to_vec()) .markers(markers) .platform(interpreter.platform()) .build(); @@ -800,6 +806,7 @@ pub(crate) async fn update_environment( index_locations, index_strategy, keyring_provider, + trusted_host, resolution, prerelease, config_setting, @@ -869,6 +876,7 @@ pub(crate) async fn update_environment( .index_urls(index_locations.index_urls()) .index_strategy(*index_strategy) .keyring(*keyring_provider) + .trusted_host(trusted_host.clone()) .markers(markers) .platform(interpreter.platform()) .build(); diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index c4b2c766e51e..b591f1ee6902 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -152,6 +152,7 @@ pub(super) async fn do_sync( index_locations, index_strategy, keyring_provider, + trusted_host, config_setting, no_build_isolation, exclude_newer, @@ -221,6 +222,7 @@ pub(super) async fn do_sync( .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) .keyring(keyring_provider) + .trusted_host(trusted_host.to_vec()) .markers(markers) .platform(venv.interpreter().platform()) .build(); diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index 25772e3e1373..f2782eb5d00b 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -5,13 +5,13 @@ use std::vec; use anstream::eprint; use anyhow::Result; -use miette::{Diagnostic, IntoDiagnostic}; -use owo_colors::OwoColorize; -use thiserror::Error; - use distribution_types::IndexLocations; use install_wheel_rs::linker::LinkMode; +use miette::{Diagnostic, IntoDiagnostic}; +use owo_colors::OwoColorize; use pypi_types::Requirement; +use thiserror::Error; +use url::Url; use uv_auth::store_credentials_from_url; use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; @@ -49,6 +49,7 @@ pub(crate) async fn venv( index_locations: &IndexLocations, index_strategy: IndexStrategy, keyring_provider: KeyringProviderType, + trusted_host: Vec, prompt: uv_virtualenv::Prompt, system_site_packages: bool, connectivity: Connectivity, @@ -69,6 +70,7 @@ pub(crate) async fn venv( index_locations, index_strategy, keyring_provider, + trusted_host, prompt, system_site_packages, connectivity, @@ -122,6 +124,7 @@ async fn venv_impl( index_locations: &IndexLocations, index_strategy: IndexStrategy, keyring_provider: KeyringProviderType, + trusted_host: Vec, prompt: uv_virtualenv::Prompt, system_site_packages: bool, connectivity: Connectivity, @@ -251,6 +254,7 @@ async fn venv_impl( .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) .keyring(keyring_provider) + .trusted_host(trusted_host) .markers(interpreter.markers()) .platform(interpreter.platform()) .build(); diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 5a2db246809a..c74fe4d42cb3 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -321,6 +321,7 @@ async fn run(cli: Cli) -> Result { args.settings.index_locations, args.settings.index_strategy, args.settings.keyring_provider, + args.settings.trusted_host, args.settings.config_setting, globals.connectivity, args.settings.no_build_isolation, @@ -387,6 +388,7 @@ async fn run(cli: Cli) -> Result { args.settings.index_locations, args.settings.index_strategy, args.settings.keyring_provider, + args.settings.trusted_host, args.settings.allow_empty_requirements, globals.connectivity, &args.settings.config_setting, @@ -470,6 +472,7 @@ async fn run(cli: Cli) -> Result { args.settings.index_locations, args.settings.index_strategy, args.settings.keyring_provider, + args.settings.trusted_host, args.settings.reinstall, args.settings.link_mode, args.settings.compile_bytecode, @@ -528,6 +531,7 @@ async fn run(cli: Cli) -> Result { globals.connectivity, globals.native_tls, args.settings.keyring_provider, + args.settings.trusted_host, printer, ) .await @@ -688,6 +692,7 @@ async fn run(cli: Cli) -> Result { &args.settings.index_locations, args.settings.index_strategy, args.settings.keyring_provider, + args.settings.trusted_host, uv_virtualenv::Prompt::from_args(prompt), args.system_site_packages, globals.connectivity, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 3284a3ba19b4..710e52c0989b 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -1,13 +1,13 @@ +use distribution_types::IndexLocations; +use install_wheel_rs::linker::LinkMode; +use pep508_rs::{ExtraName, RequirementOrigin}; +use pypi_types::Requirement; use std::env::VarError; use std::num::NonZeroUsize; use std::path::PathBuf; use std::process; use std::str::FromStr; - -use distribution_types::IndexLocations; -use install_wheel_rs::linker::LinkMode; -use pep508_rs::{ExtraName, RequirementOrigin}; -use pypi_types::Requirement; +use url::Url; use uv_cache::{CacheArgs, Refresh}; use uv_cli::{ options::{flag, resolver_installer_options, resolver_options}, @@ -1297,6 +1297,7 @@ impl PipUninstallSettings { requirement, python, keyring_provider, + trusted_host, system, no_system, break_system_packages, @@ -1317,6 +1318,12 @@ impl PipUninstallSettings { target, prefix, keyring_provider, + trusted_host: trusted_host.map(|trusted_host| { + trusted_host + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), ..PipOptions::default() }, filesystem, @@ -1547,6 +1554,7 @@ impl VenvSettings { index_args, index_strategy, keyring_provider, + trusted_host, exclude_newer, link_mode, compat_args: _, @@ -1565,6 +1573,12 @@ impl VenvSettings { system: flag(system, no_system), index_strategy, keyring_provider, + trusted_host: trusted_host.map(|trusted_host| { + trusted_host + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), exclude_newer, link_mode, ..PipOptions::from(index_args) @@ -1584,6 +1598,7 @@ pub(crate) struct InstallerSettingsRef<'a> { pub(crate) index_locations: &'a IndexLocations, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, + pub(crate) trusted_host: &'a [Url], pub(crate) config_setting: &'a ConfigSettings, pub(crate) no_build_isolation: bool, pub(crate) exclude_newer: Option, @@ -1604,6 +1619,7 @@ pub(crate) struct ResolverSettings { pub(crate) index_locations: IndexLocations, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, + pub(crate) trusted_host: Vec, pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, pub(crate) config_setting: ConfigSettings, @@ -1621,6 +1637,7 @@ pub(crate) struct ResolverSettingsRef<'a> { pub(crate) index_locations: &'a IndexLocations, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, + pub(crate) trusted_host: &'a [Url], pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, pub(crate) config_setting: &'a ConfigSettings, @@ -1651,6 +1668,7 @@ impl ResolverSettings { index_locations: &self.index_locations, index_strategy: self.index_strategy, keyring_provider: self.keyring_provider, + trusted_host: &self.trusted_host, resolution: self.resolution, prerelease: self.prerelease, config_setting: &self.config_setting, @@ -1678,6 +1696,7 @@ impl From for ResolverSettings { prerelease: value.prerelease.unwrap_or_default(), index_strategy: value.index_strategy.unwrap_or_default(), keyring_provider: value.keyring_provider.unwrap_or_default(), + trusted_host: value.trusted_host.unwrap_or_default(), config_setting: value.config_settings.unwrap_or_default(), no_build_isolation: value.no_build_isolation.unwrap_or_default(), no_build_isolation_package: value.no_build_isolation_package.unwrap_or_default(), @@ -1706,6 +1725,7 @@ pub(crate) struct ResolverInstallerSettingsRef<'a> { pub(crate) index_locations: &'a IndexLocations, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, + pub(crate) trusted_host: &'a [Url], pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, pub(crate) config_setting: &'a ConfigSettings, @@ -1732,6 +1752,7 @@ pub(crate) struct ResolverInstallerSettings { pub(crate) index_locations: IndexLocations, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, + pub(crate) trusted_host: Vec, pub(crate) resolution: ResolutionMode, pub(crate) prerelease: PrereleaseMode, pub(crate) config_setting: ConfigSettings, @@ -1767,6 +1788,7 @@ impl ResolverInstallerSettings { index_locations: &self.index_locations, index_strategy: self.index_strategy, keyring_provider: self.keyring_provider, + trusted_host: &self.trusted_host, resolution: self.resolution, prerelease: self.prerelease, config_setting: &self.config_setting, @@ -1796,6 +1818,7 @@ impl From for ResolverInstallerSettings { prerelease: value.prerelease.unwrap_or_default(), index_strategy: value.index_strategy.unwrap_or_default(), keyring_provider: value.keyring_provider.unwrap_or_default(), + trusted_host: value.trusted_host.unwrap_or_default(), config_setting: value.config_settings.unwrap_or_default(), no_build_isolation: value.no_build_isolation.unwrap_or_default(), no_build_isolation_package: value.no_build_isolation_package.unwrap_or_default(), @@ -1840,6 +1863,7 @@ pub(crate) struct PipSettings { pub(crate) prefix: Option, pub(crate) index_strategy: IndexStrategy, pub(crate) keyring_provider: KeyringProviderType, + pub(crate) trusted_host: Vec, pub(crate) no_build_isolation: bool, pub(crate) no_build_isolation_package: Vec, pub(crate) build_options: BuildOptions, @@ -1894,6 +1918,7 @@ impl PipSettings { find_links, index_strategy, keyring_provider, + trusted_host, no_build, no_binary, only_binary, @@ -1943,6 +1968,7 @@ impl PipSettings { find_links: top_level_find_links, index_strategy: top_level_index_strategy, keyring_provider: top_level_keyring_provider, + trusted_host: top_level_trusted_host, resolution: top_level_resolution, prerelease: top_level_prerelease, config_settings: top_level_config_settings, @@ -1972,6 +1998,7 @@ impl PipSettings { let find_links = find_links.combine(top_level_find_links); let index_strategy = index_strategy.combine(top_level_index_strategy); let keyring_provider = keyring_provider.combine(top_level_keyring_provider); + let trusted_host = trusted_host.combine(top_level_trusted_host); let resolution = resolution.combine(top_level_resolution); let prerelease = prerelease.combine(top_level_prerelease); let config_settings = config_settings.combine(top_level_config_settings); @@ -2031,6 +2058,7 @@ impl PipSettings { .keyring_provider .combine(keyring_provider) .unwrap_or_default(), + trusted_host: args.trusted_host.combine(trusted_host).unwrap_or_default(), generate_hashes: args .generate_hashes .combine(generate_hashes) @@ -2144,6 +2172,7 @@ impl<'a> From> for ResolverSettingsRef<'a> { index_locations: settings.index_locations, index_strategy: settings.index_strategy, keyring_provider: settings.keyring_provider, + trusted_host: settings.trusted_host, resolution: settings.resolution, prerelease: settings.prerelease, config_setting: settings.config_setting, @@ -2164,6 +2193,7 @@ impl<'a> From> for InstallerSettingsRef<'a> { index_locations: settings.index_locations, index_strategy: settings.index_strategy, keyring_provider: settings.keyring_provider, + trusted_host: settings.trusted_host, config_setting: settings.config_setting, no_build_isolation: settings.no_build_isolation, exclude_newer: settings.exclude_newer, diff --git a/crates/uv/tests/show_settings.rs b/crates/uv/tests/show_settings.rs index 5862c7046b6e..31ade354dcbe 100644 --- a/crates/uv/tests/show_settings.rs +++ b/crates/uv/tests/show_settings.rs @@ -125,6 +125,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -260,6 +261,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -396,6 +398,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -564,6 +567,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -678,6 +682,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -824,6 +829,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1007,6 +1013,7 @@ fn resolve_index_url() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1189,6 +1196,7 @@ fn resolve_index_url() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1349,6 +1357,7 @@ fn resolve_find_links() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1485,6 +1494,7 @@ fn resolve_top_level() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1659,6 +1669,7 @@ fn resolve_top_level() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1816,6 +1827,7 @@ fn resolve_top_level() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -1952,6 +1964,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2071,6 +2084,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2190,6 +2204,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2311,6 +2326,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2440,6 +2456,7 @@ fn resolve_tool() -> anyhow::Result<()> { find_links: None, index_strategy: None, keyring_provider: None, + trusted_host: None, resolution: Some( LowestDirect, ), @@ -2475,6 +2492,7 @@ fn resolve_tool() -> anyhow::Result<()> { }, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], resolution: LowestDirect, prerelease: IfNecessaryOrExplicit, config_setting: ConfigSettings( @@ -2602,6 +2620,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2772,6 +2791,7 @@ fn resolve_both() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -2934,6 +2954,7 @@ fn resolve_config_file() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -3148,6 +3169,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { @@ -3270,6 +3292,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> { prefix: None, index_strategy: FirstIndex, keyring_provider: Disabled, + trusted_host: [], no_build_isolation: false, no_build_isolation_package: [], build_options: BuildOptions { diff --git a/docs/reference/cli.md b/docs/reference/cli.md index f93621a709a8..f139947f23c6 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -308,6 +308,10 @@ uv run [OPTIONS]
  • lowest-direct: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies
  • +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --upgrade, -U

    Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh

    --upgrade-package, -P upgrade-package

    Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package

    @@ -725,6 +729,10 @@ uv add [OPTIONS] >
    --tag tag

    Tag to use when adding a dependency from Git

    +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --upgrade, -U

    Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh

    --upgrade-package, -P upgrade-package

    Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package

    @@ -985,6 +993,10 @@ uv remove [OPTIONS] ...

    If provided, uv will remove the dependency from the script’s inline metadata table, in adhere with PEP 723.

    +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --upgrade, -U

    Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh

    --upgrade-package, -P upgrade-package

    Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package

    @@ -1263,6 +1275,10 @@ uv sync [OPTIONS]
  • lowest-direct: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies
  • +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --upgrade, -U

    Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh

    --upgrade-package, -P upgrade-package

    Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package

    @@ -1491,6 +1507,10 @@ uv lock [OPTIONS]
  • lowest-direct: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies
  • +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --upgrade, -U

    Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh

    --upgrade-package, -P upgrade-package

    Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package

    @@ -1771,6 +1791,10 @@ uv tree [OPTIONS]
  • lowest-direct: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies
  • +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --universal

    Show a platform-independent dependency tree.

    Shows resolved package versions for all Python versions and platforms, rather than filtering to those that are relevant for the current environment.

    @@ -2045,6 +2069,10 @@ uv tool run [OPTIONS] [COMMAND]
  • lowest-direct: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies
  • +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --upgrade, -U

    Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh

    --upgrade-package, -P upgrade-package

    Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package

    @@ -2285,6 +2313,10 @@ uv tool install [OPTIONS]
  • lowest-direct: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies
  • +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --upgrade, -U

    Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh

    --upgrade-package, -P upgrade-package

    Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package

    @@ -2515,6 +2547,10 @@ uv tool upgrade [OPTIONS]
  • lowest-direct: Resolve the lowest compatible version of any direct dependencies, and the highest compatible version of any transitive dependencies
  • +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --upgrade, -U

    Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh

    --upgrade-package, -P upgrade-package

    Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package

    @@ -3904,6 +3940,10 @@ uv pip compile [OPTIONS] ...

    By default, uv uses the virtual environment in the current working directory or any parent directory, falling back to searching for a Python executable in PATH. The --system option instructs uv to avoid using a virtual environment Python and restrict its search to the system path.

    +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --universal

    Perform a universal resolution, attempting to generate a single requirements.txt output file that is compatible with all operating systems, architectures, and Python implementations.

    In universal mode, the current Python version (or user-provided --python-version) will be treated as a lower bound. For example, --universal --python-version 3.7 would produce a universal resolution for Python 3.7 and later.

    @@ -4201,6 +4241,10 @@ uv pip sync [OPTIONS] ...
    --target target

    Install packages into the specified directory, rather than into the virtual or system Python environment. The packages will be installed at the top-level of the directory

    +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --verbose, -v

    Use verbose output.

    You can configure fine-grained logging using the RUST_LOG environment variable. (<https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives>)

    @@ -4550,6 +4594,10 @@ uv pip install [OPTIONS] |--editable
    --target target

    Install packages into the specified directory, rather than into the virtual or system Python environment. The packages will be installed at the top-level of the directory

    +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --upgrade, -U

    Allow package upgrades, ignoring pinned versions in any existing output file. Implies --refresh

    --upgrade-package, -P upgrade-package

    Allow upgrades for a specific package, ignoring pinned versions in any existing output file. Implies --refresh-package

    @@ -4680,6 +4728,10 @@ uv pip uninstall [OPTIONS] >
    --target target

    Uninstall packages from the specified --target directory

    +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --verbose, -v

    Use verbose output.

    You can configure fine-grained logging using the RUST_LOG environment variable. (<https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives>)

    @@ -5386,6 +5438,10 @@ uv venv [OPTIONS] [NAME]

    Unlike pip, when a virtual environment is created with --system-site-packages, uv will not take system site packages into account when running commands like uv pip list or uv pip install. The --system-site-packages flag will provide the virtual environment with access to the system site packages directory at runtime, but will not affect the behavior of uv commands.

    +
    --trusted-host trusted-host

    A list of trusted hostnames for SSL connections.

    + +

    WARNING: Hosts included in this list will not be verified against the system’s certificate store.

    +
    --verbose, -v

    Use verbose output.

    You can configure fine-grained logging using the RUST_LOG environment variable. (<https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives>)

    diff --git a/docs/reference/settings.md b/docs/reference/settings.md index 25f80f258d55..06b22065f1de 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -975,6 +975,34 @@ By default, uv will use the latest compatible version of each package (`highest` --- +#### [`trusted-host`](#trusted-host) {: #trusted-host } + +A list of trusted hostnames for SSL connections. + +WARNING: Hosts included in this list will not be verified against the system's certificate +store. + +**Default value**: `[]` + +**Type**: `list[str]` + +**Example usage**: + +=== "pyproject.toml" + + ```toml + [tool.uv] + trusted-host = ["localhost:8080"] + ``` +=== "uv.toml" + + ```toml + + trusted-host = ["localhost:8080"] + ``` + +--- + #### [`upgrade`](#upgrade) {: #upgrade } Allow package upgrades, ignoring pinned versions in any existing output file. @@ -2439,6 +2467,35 @@ environment. The packages will be installed at the top-level of the directory. --- +#### [`trusted-host`](#pip_trusted-host) {: #pip_trusted-host } + + +A list of trusted hostnames for SSL connections. + +WARNING: Hosts included in this list will not be verified against the system's certificate +store. + +**Default value**: `[]` + +**Type**: `list[str]` + +**Example usage**: + +=== "pyproject.toml" + + ```toml + [tool.uv.pip] + trusted-host = ["localhost:8080"] + ``` +=== "uv.toml" + + ```toml + [pip] + trusted-host = ["localhost:8080"] + ``` + +--- + #### [`universal`](#pip_universal) {: #pip_universal } diff --git a/uv.schema.json b/uv.schema.json index 6fe0959f999b..6fecfe994845 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -351,6 +351,17 @@ "$ref": "#/definitions/Source" } }, + "trusted-host": { + "description": "A list of trusted hostnames for SSL connections.\n\nWARNING: Hosts included in this list will not be verified against the system's certificate store.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "format": "uri" + } + }, "upgrade": { "description": "Allow package upgrades, ignoring pinned versions in any existing output file.", "type": [ @@ -943,6 +954,17 @@ "null" ] }, + "trusted-host": { + "description": "A list of trusted hostnames for SSL connections.\n\nWARNING: Hosts included in this list will not be verified against the system's certificate store.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "format": "uri" + } + }, "universal": { "description": "Perform a universal resolution, attempting to generate a single `requirements.txt` output file that is compatible with all operating systems, architectures, and Python implementations.\n\nIn universal mode, the current Python version (or user-provided `--python-version`) will be treated as a lower bound. For example, `--universal --python-version 3.7` would produce a universal resolution for Python 3.7 and later.", "type": [