fix(net): treat empty SSL_CERT_FILE/SSL_CERT_DIR as unset in the stealth client

The stealth (wreq) client gated its custom-trust-store build on
`var_os(...).is_some()`, which is true for a set-but-empty variable. Supplying
a store to wreq's `tls_cert_store` REPLACES the bundled webpki roots, so an
empty `SSL_CERT_FILE` (a common shell accident, e.g. `export SSL_CERT_FILE=$UNSET`)
built a near-empty default-paths store and made every HTTPS request fail with
CERTIFICATE_VERIFY_FAILED -- while the reqwest client, which already filters
empty values in configured_root_paths(), kept working. Same input, opposite
behavior.

Extract a shared `custom_cert_store_requested` predicate in client.rs that
applies the same `!is_empty()` filter, and call it from the stealth client's
guard. The predicate takes the values as arguments so it is unit-tested under
default features (the stealth feature pulls in boringssl and is not built in
the normal test job).

Fixes #551
This commit is contained in:
Andrey Mnatsakanov committed 2026-08-04 11:08:28 +02:00
1 parent f0541f40a4
commit 86e6d5ce15
2 files changed
+51 -3

No files matched your search

+4 -3
View File
@@ -77,9 +77,10 @@ impl StealthHttpClient {
// `tls/conn/ext.rs`), it does not add to them. Applying it unconditionally would break
// every ordinary site whenever the bundle is incomplete. With neither variable set,
// behaviour is byte-for-byte what it was before.
if std::env::var_os("SSL_CERT_FILE").is_some()
|| std::env::var_os("SSL_CERT_DIR").is_some()
{
if crate::client::custom_cert_store_requested(
std::env::var_os("SSL_CERT_FILE").as_deref(),
std::env::var_os("SSL_CERT_DIR").as_deref(),
) {
match wreq::tls::trust::CertStore::builder().set_default_paths().build() {
Ok(store) => builder = builder.tls_cert_store(store),
Err(error) => tracing::warn!(