Prepare sensitive_url for crates.io (#8223)

Another good candidate for publishing separately from Lighthouse is `sensitive_url` as it's a general utility crate and not related to Ethereum. This PR prepares it to be spun out into its own crate.


  I've made the `full` field on `SensitiveUrl` private and instead provided an explicit getter called `.expose_full()`. It's a bit ugly for the diff but I prefer the explicit nature of the getter.
I've also added some extra tests and doc strings along with feature gating `Serialize` and `Deserialize` implementations behind the `serde` feature.


Co-Authored-By: Mac L <mjladson@pm.me>
This commit is contained in:
Mac L
2025-11-05 11:46:32 +04:00
committed by GitHub
parent 7b1cbca264
commit 3066f0bef2
16 changed files with 225 additions and 93 deletions

1
Cargo.lock generated
View File

@@ -8485,6 +8485,7 @@ name = "sensitive_url"
version = "0.1.0"
dependencies = [
"serde",
"serde_json",
"url",
]

View File

@@ -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"

View File

@@ -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<E>,
) -> Result<FullPayloadContents<E>, 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<E>,
) -> 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<E>,
) -> Result<ForkVersionedResponse<FullPayloadContents<E>>, 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<E>,
) -> 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<Option<ForkVersionedResponse<SignedBuilderBid<E>>>, 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<E: EthSpec>(&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()))?

View File

@@ -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);

View File

@@ -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<str> 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<Url, 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()))?
@@ -2613,7 +2611,7 @@ impl BeaconNodeHttpClient {
ids: &[u64],
epoch: Epoch,
) -> Result<GenericResponse<Vec<LivenessResponseData>>, 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()))?

View File

@@ -173,7 +173,7 @@ pub struct DepositLog {
impl BeaconNodeHttpClient {
/// `GET lighthouse/health`
pub async fn get_lighthouse_health(&self) -> Result<GenericResponse<Health>, 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<GenericResponse<SyncState>, 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<CustodyInfo, 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()))?
@@ -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<GenericResponse<ProtoArray>, 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<GenericResponse<GlobalValidatorInclusionData>, 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<GenericResponse<Option<ValidatorInclusionData>>, 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<String, 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()))?
@@ -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<Vec<BlockReward>, 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<Vec<BlockPackingEfficiency>, 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<Vec<AttestationPerformance>, 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()))?

View File

@@ -283,7 +283,7 @@ impl ValidatorClientHttpClient {
/// `GET lighthouse/version`
pub async fn get_lighthouse_version(&self) -> Result<GenericResponse<VersionData>, 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<GenericResponse<Health>, 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<T: Serialize + DeserializeOwned>(
&self,
) -> Result<GenericResponse<T>, 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<GenericResponse<Vec<ValidatorData>>, 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<Option<GenericResponse<ValidatorData>>, 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<ValidatorRequest>,
) -> Result<GenericResponse<PostValidatorsResponseData>, 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<GenericResponse<Vec<CreatedValidator>>, 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<GenericResponse<ValidatorData>, 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<bool>,
graffiti: Option<GraffitiString>,
) -> 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<ExportKeystoresResponse, 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()))?
@@ -462,7 +462,7 @@ impl ValidatorClientHttpClient {
}
fn make_keystores_url(&self) -> Result<Url, Error> {
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<Url, Error> {
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<Url, Error> {
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<Url, Error> {
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<Url, Error> {
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<AuthResponse, Error> {
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<Epoch>,
) -> Result<GenericResponse<SignedVoluntaryExit>, 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()))?

View File

@@ -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
}
}

View File

@@ -5,6 +5,12 @@ 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 }
serde = { workspace = true, optional = true }
url = { workspace = true }
[dev-dependencies]
serde_json = { workspace = true }

View File

@@ -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),
}
}
}
// Wrapper around Url which provides a custom `Display` implementation to protect user secrets.
#[derive(Clone, PartialEq)]
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 {
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<str> 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -50,6 +91,7 @@ impl Serialize for SensitiveUrl {
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for SensitiveUrl {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
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, Self::Err> {
Self::parse(s)
@@ -70,26 +112,28 @@ impl FromStr for SensitiveUrl {
}
impl SensitiveUrl {
pub fn parse(url: &str) -> Result<Self, SensitiveError> {
let surl = Url::parse(url).map_err(SensitiveError::ParseError)?;
/// 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)
}
pub fn new(full: Url) -> Result<Self, SensitiveError> {
/// 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(|_| 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());
}
}
}

View File

@@ -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!(

View File

@@ -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/");

View File

@@ -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);

View File

@@ -656,7 +656,7 @@ impl<T: SlotClock> BeaconNodeFallback<T> {
R: Future<Output = Result<O, Err>>,
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<T: SlotClock> BeaconNodeFallback<T> {
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)))
}
}

View File

@@ -191,8 +191,7 @@ async fn run<E: EthSpec>(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()

View File

@@ -134,8 +134,7 @@ async fn run<E: EthSpec>(config: ListConfig) -> Result<Vec<SingleKeystoreRespons
if let Some(ref beacon_url) = beacon_url {
for validator in &validators_to_display {
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)),
);