mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Remove sensitive_url and import from crates.io (#8377)
Use the recently published `sensitive_url` and remove it from Lighthouse Co-Authored-By: Mac L <mjladson@pm.me>
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -8449,9 +8449,10 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
|
||||
[[package]]
|
||||
name = "sensitive_url"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb7b0221fa9905eec4163dbf7660b1876cc95663af1deddc3e19ebe49167c58c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ members = [
|
||||
"common/network_utils",
|
||||
"common/oneshot_broadcast",
|
||||
"common/pretty_reqwest_error",
|
||||
"common/sensitive_url",
|
||||
"common/slot_clock",
|
||||
"common/system_health",
|
||||
"common/target_check",
|
||||
@@ -225,7 +224,7 @@ rpds = "0.11"
|
||||
rusqlite = { version = "0.28", features = ["bundled"] }
|
||||
rust_eth_kzg = "0.9"
|
||||
safe_arith = "0.1"
|
||||
sensitive_url = { path = "common/sensitive_url", features = ["serde"] }
|
||||
sensitive_url = { version = "0.1", features = ["serde"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_repr = "0.1"
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
[package]
|
||||
name = "sensitive_url"
|
||||
version = "0.1.0"
|
||||
authors = ["Mac L <mjladson@pm.me>"]
|
||||
edition = { workspace = true }
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
serde = ["dep:serde"]
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true, optional = true }
|
||||
url = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = { workspace = true }
|
||||
@@ -1,239 +0,0 @@
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
/// Errors that can occur when creating or parsing a `SensitiveUrl`.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// The URL cannot be used as a base URL.
|
||||
InvalidUrl(String),
|
||||
/// Failed to parse the URL string.
|
||||
ParseError(url::ParseError),
|
||||
/// Failed to redact sensitive information from the URL.
|
||||
RedactError(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::InvalidUrl(msg) => write!(f, "Invalid URL: {}", msg),
|
||||
Error::ParseError(e) => write!(f, "Parse error: {}", e),
|
||||
Error::RedactError(msg) => write!(f, "Redact error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Error::ParseError(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A URL wrapper that redacts sensitive information in `Display` and `Debug` output.
|
||||
///
|
||||
/// This type stores both the full URL (with credentials, paths, and query parameters)
|
||||
/// and a redacted version (containing only the scheme, host, and port). The redacted
|
||||
/// version is used when displaying or debugging to prevent accidental leakage of
|
||||
/// credentials in logs.
|
||||
///
|
||||
/// Note that `SensitiveUrl` specifically does NOT implement `Deref`, meaning you cannot call
|
||||
/// `Url` methods like `.password()` or `.scheme()` directly on `SensitiveUrl`. You must first
|
||||
/// explicitly call `.expose_full()`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use sensitive_url::SensitiveUrl;
|
||||
///
|
||||
/// let url = SensitiveUrl::parse("https://user:pass@example.com/api?token=secret").unwrap();
|
||||
///
|
||||
/// // Display shows only the redacted version:
|
||||
/// assert_eq!(url.to_string(), "https://example.com/");
|
||||
///
|
||||
/// // But you can still access the full URL when needed:
|
||||
/// let full = url.expose_full();
|
||||
/// assert_eq!(full.to_string(), "https://user:pass@example.com/api?token=secret");
|
||||
/// assert_eq!(full.password(), Some("pass"));
|
||||
/// ```
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SensitiveUrl {
|
||||
full: Url,
|
||||
redacted: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for SensitiveUrl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.redacted.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SensitiveUrl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SensitiveUrl")
|
||||
.field("redacted", &self.redacted)
|
||||
// Maintains traditional `Debug` format but hides the 'full' field.
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for SensitiveUrl {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.full.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> Deserialize<'de> for SensitiveUrl {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
SensitiveUrl::parse(&s)
|
||||
.map_err(|e| de::Error::custom(format!("Failed to deserialize sensitive URL {:?}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SensitiveUrl {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::parse(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl SensitiveUrl {
|
||||
/// Attempts to parse a `&str` into a `SensitiveUrl`.
|
||||
pub fn parse(url: &str) -> Result<Self, Error> {
|
||||
let surl = Url::parse(url).map_err(Error::ParseError)?;
|
||||
SensitiveUrl::new(surl)
|
||||
}
|
||||
|
||||
/// Creates a `SensitiveUrl` from an existing `Url`.
|
||||
pub fn new(full: Url) -> Result<Self, Error> {
|
||||
let mut redacted = full.clone();
|
||||
redacted
|
||||
.path_segments_mut()
|
||||
.map_err(|_| Error::InvalidUrl("URL cannot be a base.".to_string()))?
|
||||
.clear();
|
||||
redacted.set_query(None);
|
||||
|
||||
if redacted.has_authority() {
|
||||
redacted
|
||||
.set_username("")
|
||||
.map_err(|_| Error::RedactError("Unable to redact username.".to_string()))?;
|
||||
redacted
|
||||
.set_password(None)
|
||||
.map_err(|_| Error::RedactError("Unable to redact password.".to_string()))?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
full,
|
||||
redacted: redacted.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a reference to the full, unredacted URL.
|
||||
pub fn expose_full(&self) -> &Url {
|
||||
&self.full
|
||||
}
|
||||
|
||||
/// Returns the redacted URL as a `&str`.
|
||||
pub fn redacted(&self) -> &str {
|
||||
&self.redacted
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn redact_remote_url() {
|
||||
let full = "https://user:pass@example.com/example?somequery";
|
||||
let surl = SensitiveUrl::parse(full).unwrap();
|
||||
assert_eq!(surl.to_string(), "https://example.com/");
|
||||
assert_eq!(surl.expose_full().to_string(), full);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redact_localhost_url() {
|
||||
let full = "http://user:pass@localhost:5052/";
|
||||
let surl = SensitiveUrl::parse(full).unwrap();
|
||||
assert_eq!(surl.to_string(), "http://localhost:5052/");
|
||||
assert_eq!(surl.expose_full().to_string(), full);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_credentials() {
|
||||
let full = "https://example.com/path";
|
||||
let surl = SensitiveUrl::parse(full).unwrap();
|
||||
assert_eq!(surl.to_string(), "https://example.com/");
|
||||
assert_eq!(surl.expose_full().to_string(), full);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_display() {
|
||||
let full = "https://user:pass@example.com/api?token=secret";
|
||||
let surl = SensitiveUrl::parse(full).unwrap();
|
||||
|
||||
let display = surl.to_string();
|
||||
assert_eq!(display, "https://example.com/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let full = "https://user:pass@example.com/api?token=secret";
|
||||
let surl = SensitiveUrl::parse(full).unwrap();
|
||||
|
||||
let debug = format!("{:?}", surl);
|
||||
|
||||
assert_eq!(
|
||||
debug,
|
||||
"SensitiveUrl { redacted: \"https://example.com/\", .. }"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialize() {
|
||||
let full = "https://user:pass@example.com/api?token=secret";
|
||||
let surl = SensitiveUrl::parse(full).unwrap();
|
||||
|
||||
let json = serde_json::to_string(&surl).unwrap();
|
||||
assert_eq!(json, format!("\"{}\"", full));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize() {
|
||||
let full = "https://user:pass@example.com/api?token=secret";
|
||||
let json = format!("\"{}\"", full);
|
||||
|
||||
let surl: SensitiveUrl = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(surl.expose_full().as_str(), full);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip() {
|
||||
let full = "https://user:pass@example.com/api?token=secret";
|
||||
let original = SensitiveUrl::parse(full).unwrap();
|
||||
|
||||
let json = serde_json::to_string(&original).unwrap();
|
||||
let deserialized: SensitiveUrl = serde_json::from_str(&json).unwrap();
|
||||
|
||||
assert_eq!(deserialized.expose_full(), original.expose_full());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ logging = { workspace = true }
|
||||
node_test_rig = { path = "../node_test_rig" }
|
||||
parking_lot = { workspace = true }
|
||||
rayon = { workspace = true }
|
||||
sensitive_url = { path = "../../common/sensitive_url" }
|
||||
sensitive_url = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
Reference in New Issue
Block a user