diff --git a/Cargo.lock b/Cargo.lock index 213fa1c1ad8..857abaa059a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8990,6 +8990,7 @@ dependencies = [ "tokio", "tokio-tungstenite 0.27.0", "tokio-tungstenite-wasm", + "toml 0.8.23", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", diff --git a/Cargo.toml b/Cargo.toml index 37d8cb11878..bd33414a623 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -215,7 +215,10 @@ base64 = "0.21.2" bigdecimal = "0.4.7" bitflags = "2.3.3" blake3 = "1.5.1" -brotli = "3.5" +# `ffi-api` (on by default) exports `Brotli*` C symbols from any `cdylib` that +# links this crate, which clashes with host processes that also load libcrypto. +# Only the Rust API is used, so keep the defaults off. See #6430. +brotli = { version = "3.5", default-features = false, features = ["std"] } bytemuck = { version = "1.16.2", features = ["must_cast"] } bytes = "1.10.1" bytestring = { version = "1.2.0", features = ["serde"] } diff --git a/sdks/rust/Cargo.toml b/sdks/rust/Cargo.toml index 7f1f24f6ff7..056d9800e8b 100644 --- a/sdks/rust/Cargo.toml +++ b/sdks/rust/Cargo.toml @@ -66,6 +66,7 @@ native-tls = "=0.2.14" [dev-dependencies] futures-channel.workspace = true shlex = "1.3.0" +toml.workspace = true # for tests spacetimedb-testing = { path = "../../crates/testing" } diff --git a/sdks/rust/tests/brotli_ffi_api.rs b/sdks/rust/tests/brotli_ffi_api.rs new file mode 100644 index 00000000000..cd0f973ba8a --- /dev/null +++ b/sdks/rust/tests/brotli_ffi_api.rs @@ -0,0 +1,37 @@ +//! Guards the `brotli` dependency policy described in #6430. +//! +//! `brotli`'s default features include `ffi-api`, which exports the `Brotli*` +//! C symbols from any `cdylib` that links the SDK. A host process that also +//! loads a libcrypto built with Brotli support then binds those imports to the +//! extension, keeps it mapped after unload, and breaks hot reloading. The SDK +//! only uses the Rust API, so `ffi-api` must stay off. + +use toml::Value; + +const WORKSPACE_MANIFEST: &str = include_str!("../../../Cargo.toml"); +const SDK_MANIFEST: &str = include_str!("../Cargo.toml"); + +#[test] +fn brotli_does_not_enable_ffi_api() { + let workspace: Value = toml::from_str(WORKSPACE_MANIFEST).expect("workspace manifest is not valid TOML"); + let sdk: Value = toml::from_str(SDK_MANIFEST).expect("SDK manifest is not valid TOML"); + + assert_eq!( + sdk["dependencies"]["brotli"]["workspace"].as_bool(), + Some(true), + "the SDK must inherit `brotli` from the workspace so the `ffi-api` policy cannot be \ + overridden locally" + ); + + let brotli = &workspace["workspace"]["dependencies"]["brotli"]; + assert_eq!( + brotli["default-features"].as_bool(), + Some(false), + "`brotli`'s default features include `ffi-api`, which exports `Brotli*` C symbols from \ + `cdylib` clients (#6430)" + ); + let enables_ffi_api = brotli["features"] + .as_array() + .is_some_and(|features| features.iter().any(|feature| feature.as_str() == Some("ffi-api"))); + assert!(!enables_ffi_api, "`brotli` must not enable `ffi-api` (#6430)"); +}