diff --git a/Cargo.lock b/Cargo.lock index 676f674b1bb..fad3ad2ffc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8485,6 +8485,7 @@ name = "sensitive_url" version = "0.1.0" dependencies = [ "serde", + "serde_json", "url", ] diff --git a/Cargo.toml b/Cargo.toml index 1dfc753b8c2..d09b0fcd80c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -225,7 +225,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" } +sensitive_url = { path = "common/sensitive_url", features = ["serde"] } serde = { version = "1", features = ["derive"] } serde_json = "1" serde_repr = "0.1" diff --git a/beacon_node/builder_client/src/lib.rs b/beacon_node/builder_client/src/lib.rs index 6b993542f35..b486e77083a 100644 --- a/beacon_node/builder_client/src/lib.rs +++ b/beacon_node/builder_client/src/lib.rs @@ -270,7 +270,7 @@ impl BuilderHttpClient { &self, validator: &[SignedValidatorRegistrationData], ) -> Result<(), Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -289,7 +289,7 @@ impl BuilderHttpClient { &self, blinded_block: &SignedBlindedBeaconBlock, ) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); let body = blinded_block.as_ssz_bytes(); @@ -337,7 +337,7 @@ impl BuilderHttpClient { &self, blinded_block: &SignedBlindedBeaconBlock, ) -> Result<(), Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); let body = blinded_block.as_ssz_bytes(); @@ -387,7 +387,7 @@ impl BuilderHttpClient { &self, blinded_block: &SignedBlindedBeaconBlock, ) -> Result>, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -430,7 +430,7 @@ impl BuilderHttpClient { &self, blinded_block: &SignedBlindedBeaconBlock, ) -> Result<(), Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -480,7 +480,7 @@ impl BuilderHttpClient { parent_hash: ExecutionBlockHash, pubkey: &PublicKeyBytes, ) -> Result>>, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -521,7 +521,7 @@ impl BuilderHttpClient { /// `GET /eth/v1/builder/status` pub async fn get_builder_status(&self) -> Result<(), Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? diff --git a/beacon_node/execution_layer/src/engine_api/http.rs b/beacon_node/execution_layer/src/engine_api/http.rs index a8dbed34cee..8f7564ace6b 100644 --- a/beacon_node/execution_layer/src/engine_api/http.rs +++ b/beacon_node/execution_layer/src/engine_api/http.rs @@ -652,7 +652,7 @@ impl HttpJsonRpc { let mut request = self .client - .post(self.url.full.clone()) + .post(self.url.expose_full().clone()) .timeout(timeout) .header(CONTENT_TYPE, "application/json") .json(&body); diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 2641a4c02e0..e8e6663d46a 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -30,7 +30,7 @@ use reqwest::{ }; pub use reqwest::{StatusCode, Url}; use reqwest_eventsource::{Event, EventSource}; -pub use sensitive_url::{SensitiveError, SensitiveUrl}; +pub use sensitive_url::SensitiveUrl; use serde::{Serialize, de::DeserializeOwned}; use ssz::Encode; use std::fmt; @@ -152,12 +152,6 @@ impl fmt::Display for BeaconNodeHttpClient { } } -impl AsRef for BeaconNodeHttpClient { - fn as_ref(&self) -> &str { - self.server.as_ref() - } -} - impl BeaconNodeHttpClient { pub fn new(server: SensitiveUrl, timeouts: Timeouts) -> Self { Self { @@ -178,10 +172,14 @@ impl BeaconNodeHttpClient { timeouts, } } + // Returns a reference to the `SensitiveUrl` of the server. + pub fn server(&self) -> &SensitiveUrl { + &self.server + } /// Return the path with the standard `/eth/vX` prefix applied. fn eth_path(&self, version: EndpointVersion) -> Result { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -2613,7 +2611,7 @@ impl BeaconNodeHttpClient { ids: &[u64], epoch: Epoch, ) -> Result>, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? diff --git a/common/eth2/src/lighthouse.rs b/common/eth2/src/lighthouse.rs index 4f9a049e44e..993c263cbfb 100644 --- a/common/eth2/src/lighthouse.rs +++ b/common/eth2/src/lighthouse.rs @@ -173,7 +173,7 @@ pub struct DepositLog { impl BeaconNodeHttpClient { /// `GET lighthouse/health` pub async fn get_lighthouse_health(&self) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -185,7 +185,7 @@ impl BeaconNodeHttpClient { /// `GET lighthouse/syncing` pub async fn get_lighthouse_syncing(&self) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -197,7 +197,7 @@ impl BeaconNodeHttpClient { /// `GET lighthouse/custody/info` pub async fn get_lighthouse_custody_info(&self) -> Result { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -210,7 +210,7 @@ impl BeaconNodeHttpClient { /// `POST lighthouse/custody/backfill` pub async fn post_lighthouse_custody_backfill(&self) -> Result<(), Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -231,7 +231,7 @@ impl BeaconNodeHttpClient { /// `GET lighthouse/proto_array` pub async fn get_lighthouse_proto_array(&self) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -246,7 +246,7 @@ impl BeaconNodeHttpClient { &self, epoch: Epoch, ) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -264,7 +264,7 @@ impl BeaconNodeHttpClient { epoch: Epoch, validator_id: ValidatorId, ) -> Result>, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -278,7 +278,7 @@ impl BeaconNodeHttpClient { /// `POST lighthouse/database/reconstruct` pub async fn post_lighthouse_database_reconstruct(&self) -> Result { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -291,7 +291,7 @@ impl BeaconNodeHttpClient { /// `POST lighthouse/add_peer` pub async fn post_lighthouse_add_peer(&self, req: AdminPeer) -> Result<(), Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -303,7 +303,7 @@ impl BeaconNodeHttpClient { /// `POST lighthouse/remove_peer` pub async fn post_lighthouse_remove_peer(&self, req: AdminPeer) -> Result<(), Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -323,7 +323,7 @@ impl BeaconNodeHttpClient { start_slot: Slot, end_slot: Slot, ) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -344,7 +344,7 @@ impl BeaconNodeHttpClient { start_epoch: Epoch, end_epoch: Epoch, ) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -366,7 +366,7 @@ impl BeaconNodeHttpClient { end_epoch: Epoch, target: String, ) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? diff --git a/common/eth2/src/lighthouse_vc/http_client.rs b/common/eth2/src/lighthouse_vc/http_client.rs index c4fddb97d7a..8c9d3397a8c 100644 --- a/common/eth2/src/lighthouse_vc/http_client.rs +++ b/common/eth2/src/lighthouse_vc/http_client.rs @@ -283,7 +283,7 @@ impl ValidatorClientHttpClient { /// `GET lighthouse/version` pub async fn get_lighthouse_version(&self) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -295,7 +295,7 @@ impl ValidatorClientHttpClient { /// `GET lighthouse/health` pub async fn get_lighthouse_health(&self) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -309,7 +309,7 @@ impl ValidatorClientHttpClient { pub async fn get_lighthouse_spec( &self, ) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -323,7 +323,7 @@ impl ValidatorClientHttpClient { pub async fn get_lighthouse_validators( &self, ) -> Result>, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -338,7 +338,7 @@ impl ValidatorClientHttpClient { &self, validator_pubkey: &PublicKeyBytes, ) -> Result>, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -354,7 +354,7 @@ impl ValidatorClientHttpClient { &self, validators: Vec, ) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -369,7 +369,7 @@ impl ValidatorClientHttpClient { &self, request: &CreateValidatorsMnemonicRequest, ) -> Result>, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -385,7 +385,7 @@ impl ValidatorClientHttpClient { &self, request: &KeystoreValidatorsPostRequest, ) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -401,7 +401,7 @@ impl ValidatorClientHttpClient { &self, request: &[Web3SignerValidatorRequest], ) -> Result<(), Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -424,7 +424,7 @@ impl ValidatorClientHttpClient { prefer_builder_proposals: Option, graffiti: Option, ) -> Result<(), Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -451,7 +451,7 @@ impl ValidatorClientHttpClient { &self, req: &DeleteKeystoresRequest, ) -> Result { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? @@ -462,7 +462,7 @@ impl ValidatorClientHttpClient { } fn make_keystores_url(&self) -> Result { - let mut url = self.server.full.clone(); + let mut url = self.server.expose_full().clone(); url.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("eth") @@ -472,7 +472,7 @@ impl ValidatorClientHttpClient { } fn make_remotekeys_url(&self) -> Result { - let mut url = self.server.full.clone(); + let mut url = self.server.expose_full().clone(); url.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("eth") @@ -482,7 +482,7 @@ impl ValidatorClientHttpClient { } fn make_fee_recipient_url(&self, pubkey: &PublicKeyBytes) -> Result { - let mut url = self.server.full.clone(); + let mut url = self.server.expose_full().clone(); url.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("eth") @@ -494,7 +494,7 @@ impl ValidatorClientHttpClient { } fn make_graffiti_url(&self, pubkey: &PublicKeyBytes) -> Result { - let mut url = self.server.full.clone(); + let mut url = self.server.expose_full().clone(); url.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("eth") @@ -506,7 +506,7 @@ impl ValidatorClientHttpClient { } fn make_gas_limit_url(&self, pubkey: &PublicKeyBytes) -> Result { - let mut url = self.server.full.clone(); + let mut url = self.server.expose_full().clone(); url.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("eth") @@ -519,7 +519,7 @@ impl ValidatorClientHttpClient { /// `GET lighthouse/auth` pub async fn get_auth(&self) -> Result { - let mut url = self.server.full.clone(); + let mut url = self.server.expose_full().clone(); url.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("lighthouse") @@ -635,7 +635,7 @@ impl ValidatorClientHttpClient { pubkey: &PublicKeyBytes, epoch: Option, ) -> Result, Error> { - let mut path = self.server.full.clone(); + let mut path = self.server.expose_full().clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? diff --git a/common/monitoring_api/src/lib.rs b/common/monitoring_api/src/lib.rs index 465618c9a82..03b93f2faae 100644 --- a/common/monitoring_api/src/lib.rs +++ b/common/monitoring_api/src/lib.rs @@ -195,7 +195,7 @@ impl MonitoringHttpClient { endpoint = %self.monitoring_endpoint, "Sending metrics to remote endpoint" ); - self.post(self.monitoring_endpoint.full.clone(), &metrics) + self.post(self.monitoring_endpoint.expose_full().clone(), &metrics) .await } } diff --git a/common/sensitive_url/Cargo.toml b/common/sensitive_url/Cargo.toml index ff562097225..3793cc51398 100644 --- a/common/sensitive_url/Cargo.toml +++ b/common/sensitive_url/Cargo.toml @@ -5,6 +5,12 @@ authors = ["Mac L "] 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 } +serde = { workspace = true, optional = true } url = { workspace = true } + +[dev-dependencies] +serde_json = { workspace = true } diff --git a/common/sensitive_url/src/lib.rs b/common/sensitive_url/src/lib.rs index 64ad070a1fd..3f9240268dd 100644 --- a/common/sensitive_url/src/lib.rs +++ b/common/sensitive_url/src/lib.rs @@ -1,26 +1,69 @@ +#[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 SensitiveError { +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 SensitiveError { +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + 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, + } } } -// Wrapper around Url which provides a custom `Display` implementation to protect user secrets. -#[derive(Clone, PartialEq)] +/// 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 { - pub full: Url, - pub redacted: String, + full: Url, + redacted: String, } impl fmt::Display for SensitiveUrl { @@ -31,16 +74,14 @@ impl fmt::Display for SensitiveUrl { impl fmt::Debug for SensitiveUrl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.redacted.fmt(f) - } -} - -impl AsRef for SensitiveUrl { - fn as_ref(&self) -> &str { - self.redacted.as_str() + 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(&self, serializer: S) -> Result where @@ -50,6 +91,7 @@ impl Serialize for SensitiveUrl { } } +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for SensitiveUrl { fn deserialize(deserializer: D) -> Result where @@ -62,7 +104,7 @@ impl<'de> Deserialize<'de> for SensitiveUrl { } impl FromStr for SensitiveUrl { - type Err = SensitiveError; + type Err = Error; fn from_str(s: &str) -> Result { Self::parse(s) @@ -70,26 +112,28 @@ impl FromStr for SensitiveUrl { } impl SensitiveUrl { - pub fn parse(url: &str) -> Result { - let surl = Url::parse(url).map_err(SensitiveError::ParseError)?; + /// Attempts to parse a `&str` into a `SensitiveUrl`. + pub fn parse(url: &str) -> Result { + let surl = Url::parse(url).map_err(Error::ParseError)?; SensitiveUrl::new(surl) } - pub fn new(full: Url) -> Result { + /// Creates a `SensitiveUrl` from an existing `Url`. + pub fn new(full: Url) -> Result { let mut redacted = full.clone(); redacted .path_segments_mut() - .map_err(|_| SensitiveError::InvalidUrl("URL cannot be a base.".to_string()))? + .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(|_| { - SensitiveError::RedactError("Unable to redact username.".to_string()) - })?; - redacted.set_password(None).map_err(|_| { - SensitiveError::RedactError("Unable to redact password.".to_string()) - })?; + 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 { @@ -97,6 +141,16 @@ impl SensitiveUrl { 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)] @@ -105,16 +159,81 @@ mod tests { #[test] fn redact_remote_url() { - let full = "https://project:secret@example.com/example?somequery"; + 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.full.to_string(), full); + assert_eq!(surl.expose_full().to_string(), full); } + #[test] fn redact_localhost_url() { - let full = "http://localhost:5052/"; + let full = "http://user:pass@localhost:5052/"; let surl = SensitiveUrl::parse(full).unwrap(); assert_eq!(surl.to_string(), "http://localhost:5052/"); - assert_eq!(surl.full.to_string(), full); + 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()); + } } } diff --git a/lighthouse/tests/beacon_node.rs b/lighthouse/tests/beacon_node.rs index 8342b021738..207324ea33f 100644 --- a/lighthouse/tests/beacon_node.rs +++ b/lighthouse/tests/beacon_node.rs @@ -481,7 +481,12 @@ fn run_execution_jwt_secret_key_is_persisted() { .with_config(|config| { let config = config.execution_layer.as_ref().unwrap(); assert_eq!( - config.execution_endpoint.as_ref().unwrap().full.to_string(), + config + .execution_endpoint + .as_ref() + .unwrap() + .expose_full() + .to_string(), "http://localhost:8551/" ); let mut file_jwt_secret_key = String::new(); @@ -532,7 +537,12 @@ fn bellatrix_jwt_secrets_flag() { .with_config(|config| { let config = config.execution_layer.as_ref().unwrap(); assert_eq!( - config.execution_endpoint.as_ref().unwrap().full.to_string(), + config + .execution_endpoint + .as_ref() + .unwrap() + .expose_full() + .to_string(), "http://localhost:8551/" ); assert_eq!( diff --git a/lighthouse/tests/validator_client.rs b/lighthouse/tests/validator_client.rs index 398c6fbd6b2..ee3e910b369 100644 --- a/lighthouse/tests/validator_client.rs +++ b/lighthouse/tests/validator_client.rs @@ -109,12 +109,12 @@ fn beacon_nodes_flag() { .run() .with_config(|config| { assert_eq!( - config.beacon_nodes[0].full.to_string(), + config.beacon_nodes[0].expose_full().to_string(), "http://localhost:1001/" ); assert_eq!(config.beacon_nodes[0].to_string(), "http://localhost:1001/"); assert_eq!( - config.beacon_nodes[1].full.to_string(), + config.beacon_nodes[1].expose_full().to_string(), "https://project:secret@infura.io/" ); assert_eq!(config.beacon_nodes[1].to_string(), "https://infura.io/"); diff --git a/testing/execution_engine_integration/src/test_rig.rs b/testing/execution_engine_integration/src/test_rig.rs index 05ec0a2f191..9e45a788704 100644 --- a/testing/execution_engine_integration/src/test_rig.rs +++ b/testing/execution_engine_integration/src/test_rig.rs @@ -64,7 +64,7 @@ async fn import_and_unlock(http_url: SensitiveUrl, priv_keys: &[&str], password: let client = Client::builder().build().unwrap(); let request = client - .post(http_url.full.clone()) + .post(http_url.expose_full().clone()) .header(CONTENT_TYPE, "application/json") .json(&body); @@ -90,7 +90,7 @@ async fn import_and_unlock(http_url: SensitiveUrl, priv_keys: &[&str], password: ); let request = client - .post(http_url.full.clone()) + .post(http_url.expose_full().clone()) .header(CONTENT_TYPE, "application/json") .json(&body); diff --git a/validator_client/beacon_node_fallback/src/lib.rs b/validator_client/beacon_node_fallback/src/lib.rs index a3f60d2de04..0f13d8c8b7b 100644 --- a/validator_client/beacon_node_fallback/src/lib.rs +++ b/validator_client/beacon_node_fallback/src/lib.rs @@ -656,7 +656,7 @@ impl BeaconNodeFallback { R: Future>, Err: Debug, { - inc_counter_vec(&ENDPOINT_REQUESTS, &[candidate.as_ref()]); + inc_counter_vec(&ENDPOINT_REQUESTS, &[candidate.server().redacted()]); // There exists a race condition where `func` may be called when the candidate is // actually not ready. We deem this an acceptable inefficiency. @@ -668,7 +668,7 @@ impl BeaconNodeFallback { error = ?e, "Request to beacon node failed" ); - inc_counter_vec(&ENDPOINT_ERRORS, &[candidate.as_ref()]); + inc_counter_vec(&ENDPOINT_ERRORS, &[candidate.server().redacted()]); Err((candidate.to_string(), Error::RequestFailed(e))) } } diff --git a/validator_manager/src/exit_validators.rs b/validator_manager/src/exit_validators.rs index a6bbf05fb4a..4a398793ce1 100644 --- a/validator_manager/src/exit_validators.rs +++ b/validator_manager/src/exit_validators.rs @@ -191,8 +191,7 @@ async fn run(config: ExitConfig) -> Result<(), String> { // Only publish the voluntary exit if the --beacon-node flag is present if let Some(ref beacon_url) = beacon_url { let beacon_node = BeaconNodeHttpClient::new( - SensitiveUrl::parse(beacon_url.as_ref()) - .map_err(|e| format!("Failed to parse beacon http server: {:?}", e))?, + beacon_url.clone(), Timeouts::set_all(Duration::from_secs(12)), ); @@ -399,7 +398,7 @@ mod test { }) .collect(); - let beacon_url = SensitiveUrl::parse(self.beacon_node.client.as_ref()).unwrap(); + let beacon_url = self.beacon_node.client.server().clone(); let validators_to_exit = index_of_validators_to_exit .iter() diff --git a/validator_manager/src/list_validators.rs b/validator_manager/src/list_validators.rs index b064982adf4..082894a995d 100644 --- a/validator_manager/src/list_validators.rs +++ b/validator_manager/src/list_validators.rs @@ -134,8 +134,7 @@ async fn run(config: ListConfig) -> Result