From f595499af9580f0450d12654e3f2824507114e7a Mon Sep 17 00:00:00 2001 From: Alfie Fresta Date: Tue, 12 May 2026 19:45:16 +0100 Subject: [PATCH] fix(deps): bump publicsuffix to 2.3 to drop vulnerable idna 0.2.3 publicsuffix 1.5 transitively pulls idna 0.2.3, which is affected by CVE-2024-12224 (GHSA-h97m-ww89-6jmq). publicsuffix 2.3 depends on idna 1.0+, which is patched. Closes Dependabot alert #25. The 2.x API replaces List::from_str / parse_domain / Domain::root with str::parse and the Psl trait's suffix() / domain() methods. The new algorithm also applies an implicit wildcard so unlisted TLDs (like "localhost") report themselves as a suffix; filter on Suffix::is_known() to preserve v1 semantics and keep bare "localhost" valid as an rp.id. --- Cargo.lock | 65 ++++++----------------------- libwebauthn/Cargo.toml | 2 +- libwebauthn/src/ops/webauthn/psl.rs | 30 +++++++++---- 3 files changed, 36 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2fc9f1..5c00153 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1559,17 +1559,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.1.0" @@ -1802,7 +1791,7 @@ dependencies = [ "hidapi", "hkdf", "hmac 0.12.1", - "idna 1.1.0", + "idna", "maplit", "mockall", "nfc1", @@ -1954,12 +1943,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "memchr" version = "2.8.0" @@ -2468,14 +2451,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + [[package]] name = "publicsuffix" -version = "1.5.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b4ce31ff0a27d93c8de1849cf58162283752f065a90d508f1105fa6c9a213f" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" dependencies = [ - "idna 0.2.3", - "url", + "idna", + "psl-types", ] [[package]] @@ -3315,21 +3304,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.52.3" @@ -3629,27 +3603,12 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-xid" version = "0.2.6" @@ -3679,7 +3638,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", - "idna 1.1.0", + "idna", "percent-encoding", "serde", ] diff --git a/libwebauthn/Cargo.toml b/libwebauthn/Cargo.toml index f56bc85..6e90cf1 100644 --- a/libwebauthn/Cargo.toml +++ b/libwebauthn/Cargo.toml @@ -32,7 +32,7 @@ base64-url = "3.0.0" dbus = "0.9.5" tracing = "0.1.29" idna = "1.0.3" -publicsuffix = { version = "1.5", default-features = false } +publicsuffix = "2.3" url = "2.5" maplit = "1.0.2" sha2 = "0.10.2" diff --git a/libwebauthn/src/ops/webauthn/psl.rs b/libwebauthn/src/ops/webauthn/psl.rs index 47967ad..1be13a3 100644 --- a/libwebauthn/src/ops/webauthn/psl.rs +++ b/libwebauthn/src/ops/webauthn/psl.rs @@ -14,6 +14,8 @@ use std::path::{Path, PathBuf}; +use publicsuffix::{List, Psl}; + /// Public Suffix List lookup interface. /// /// Implementations decide where the PSL data lives (system file, embedded @@ -42,7 +44,7 @@ pub const SYSTEM_PSL_PATH: &str = "/usr/share/publicsuffix/public_suffix_list.da /// `PublicSuffixList` implementation backed by a Public Suffix List `.dat` /// file loaded from disk at construction time. pub struct DatFilePublicSuffixList { - list: publicsuffix::List, + list: List, source: PathBuf, } @@ -59,8 +61,9 @@ impl DatFilePublicSuffixList { pub fn from_path(path: impl AsRef) -> Result { let path = path.as_ref(); let data = std::fs::read_to_string(path)?; - let list = publicsuffix::List::from_str(&data) - .map_err(|e| DatFileLoadError::Parse(e.to_string()))?; + let list: List = data + .parse() + .map_err(|e: publicsuffix::Error| DatFileLoadError::Parse(e.to_string()))?; Ok(Self { list, source: path.to_path_buf(), @@ -74,14 +77,27 @@ impl DatFilePublicSuffixList { } impl PublicSuffixList for DatFilePublicSuffixList { + // `is_known()` filter drops `publicsuffix`'s implicit-wildcard match for + // unlisted TLDs (e.g. `localhost`), so bare `localhost` stays a valid rp.id. fn registrable_domain(&self, host: &str) -> Option { - let domain = self.list.parse_domain(host).ok()?; - domain.root().map(|s| s.to_string()) + let suffix = self.list.suffix(host.as_bytes())?; + if !suffix.is_known() { + return None; + } + let domain = self.list.domain(host.as_bytes())?; + std::str::from_utf8(domain.as_bytes()) + .ok() + .map(String::from) } fn public_suffix(&self, host: &str) -> Option { - let domain = self.list.parse_domain(host).ok()?; - domain.suffix().map(|s| s.to_string()) + let suffix = self.list.suffix(host.as_bytes())?; + if !suffix.is_known() { + return None; + } + std::str::from_utf8(suffix.as_bytes()) + .ok() + .map(String::from) } }