Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 28 additions & 26 deletions crates/uv-client/src/base_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,33 +480,35 @@ impl<'a> BaseClientBuilder<'a> {
// Checks for the presence of `SSL_CERT_FILE`.
// Certificate loading support is delegated to `rustls-native-certs`.
// See https://github.com/rustls/rustls-native-certs/blob/813790a297ad4399efe70a8e5264ca1b420acbec/src/lib.rs#L118-L125
let ssl_cert_file_exists = env::var_os(EnvVars::SSL_CERT_FILE).is_some_and(|path| {
let path = Path::new(&path);
match path.metadata() {
Ok(metadata) if metadata.is_file() => true,
Ok(_) => {
warn_user_once!(
"Ignoring invalid `SSL_CERT_FILE`. Path is not a file: {}.",
path.simplified_display().cyan()
);
false
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
warn_user_once!(
"Ignoring invalid `SSL_CERT_FILE`. Path does not exist: {}.",
path.simplified_display().cyan()
);
false
}
Err(err) => {
warn_user_once!(
"Ignoring invalid `SSL_CERT_FILE`. Path is not accessible: {} ({err}).",
path.simplified_display().cyan()
);
false
let ssl_cert_file_exists = env::var_os(EnvVars::SSL_CERT_FILE)
.filter(|v| !v.is_empty())
.is_some_and(|path| {
let path = Path::new(&path);
match path.metadata() {
Ok(metadata) if metadata.is_file() => true,
Ok(_) => {
warn_user_once!(
"Ignoring invalid `SSL_CERT_FILE`. Path is not a file: {}.",
path.simplified_display().cyan()
);
false
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
warn_user_once!(
"Ignoring invalid `SSL_CERT_FILE`. Path does not exist: {}.",
path.simplified_display().cyan()
);
false
}
Err(err) => {
warn_user_once!(
"Ignoring invalid `SSL_CERT_FILE`. Path is not accessible: {} ({err}).",
path.simplified_display().cyan()
);
false
}
}
}
});
});

// Checks for the presence of `SSL_CERT_DIR`.
// Certificate loading support is delegated to `rustls-native-certs`.
Expand Down
57 changes: 57 additions & 0 deletions crates/uv-client/tests/it/ssl_certs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,63 @@ async fn ssl_env_vars() -> Result<()> {
),
)?;

// ** Set SSL_CERT_FILE to empty value
// ** Then verify it's treated like unset (i.e. we still fail against a self-signed cert)

unsafe {
std::env::set_var(EnvVars::SSL_CERT_FILE, "");
}
let (server_task, addr) = start_https_user_agent_server(&standalone_server_cert).await?;
let url = DisplaySafeUrl::from_str(&format!("https://{addr}"))?;
let cache = Cache::temp()?.init().await?;
let client =
RegistryClientBuilder::new(BaseClientBuilder::default().no_retry_delay(true), cache)
.build();
let res = client
.cached_client()
.uncached()
.for_host(&url)
.get(Url::from(url))
.send()
.await;
unsafe {
std::env::remove_var(EnvVars::SSL_CERT_FILE);
}

// Validate the client error
let Some(reqwest_middleware::Error::Middleware(middleware_error)) = res.err() else {
panic!("expected middleware error");
};
let reqwest_error = middleware_error
.chain()
.find_map(|err| {
err.downcast_ref::<reqwest_middleware::Error>().map(|err| {
if let reqwest_middleware::Error::Reqwest(inner) = err {
inner
} else {
panic!("expected reqwest error")
}
})
})
.expect("expected reqwest error");
assert!(reqwest_error.is_connect());

// Validate the server error
let server_res = server_task.await?;
let expected_err = if let Err(anyhow_err) = server_res
&& let Some(io_err) = anyhow_err.downcast_ref::<std::io::Error>()
&& let Some(wrapped_err) = io_err.get_ref()
&& let Some(tls_err) = wrapped_err.downcast_ref::<rustls::Error>()
&& matches!(
tls_err,
rustls::Error::AlertReceived(AlertDescription::UnknownCA)
) {
true
} else {
false
};
assert!(expected_err);

// ** Set SSL_CERT_FILE to non-existent location
// ** Then verify our request fails to establish a connection

Expand Down
10 changes: 9 additions & 1 deletion crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,10 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
// If found, this file is combined with the user configuration file.
// 3. The nearest configuration file (`uv.toml` or `pyproject.toml`) in the directory tree,
// starting from the current directory.
let config_file = cli.top_level.config_file.clone();

let workspace_cache = WorkspaceCache::default();
let filesystem = if let Some(config_file) = cli.top_level.config_file.as_ref() {
let filesystem = if let Some(config_file) = config_file.as_ref() {
if config_file
.file_name()
.is_some_and(|file_name| file_name == "pyproject.toml")
Expand Down Expand Up @@ -362,6 +364,12 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
)?;

debug!("uv {}", uv_cli::version::uv_self_version());
if let Some(config_file) = config_file.as_ref() {
debug!(
"Using `--config-file` / `UV_CONFIG_FILE` at `{}`, ignoring discovered project, user, and system configuration files",
config_file.user_display()
);
}
if globals.preview.all_enabled() {
debug!("All preview features are enabled");
} else if globals.preview.any_enabled() {
Expand Down
25 changes: 25 additions & 0 deletions crates/uv/tests/it/cache_clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,31 @@ fn clean_all() -> Result<()> {
Ok(())
}

#[test]
fn clean_all_with_config_file_logs_override() -> Result<()> {
let context = uv_test::test_context!("3.12").with_filtered_counts();

let config = context.temp_dir.child("uv.toml");
config.write_str("")?;

uv_snapshot!(context.filters(), context.clean()
.arg("--verbose")
.arg("--config-file")
.arg(config.path()), @"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
DEBUG uv [VERSION] ([COMMIT] DATE)
DEBUG Using `--config-file` / `UV_CONFIG_FILE` at `uv.toml`, ignoring discovered project, user, and system configuration files
Clearing cache at: [CACHE_DIR]/
Removed [N] files ([SIZE])
");

Ok(())
}

#[tokio::test]
async fn clean_force() -> Result<()> {
let context = uv_test::test_context!("3.12").with_filtered_counts();
Expand Down