From 170c5356199c45db46fd00945618fcfc13e58fd3 Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Sun, 24 May 2026 19:32:01 +0300 Subject: [PATCH] starknet_transaction_prover: redact URL credentials in logs and add startup banner Adds `redact_url_host` which collapses a URL to `scheme://host[:port]`, dropping userinfo, path, and query. The CLI-override logs for `rpc_node_url` and `blocking_check_url` and a new startup banner all route through it so credentials embedded in those URLs cannot reach a log sink. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../starknet_transaction_prover/src/main.rs | 15 +++++++++ .../starknet_transaction_prover/src/server.rs | 1 + .../src/server/config.rs | 19 ++++++++++-- .../src/server/log_redact.rs | 22 +++++++++++++ .../src/server/log_redact_test.rs | 31 +++++++++++++++++++ 5 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 crates/starknet_transaction_prover/src/server/log_redact.rs create mode 100644 crates/starknet_transaction_prover/src/server/log_redact_test.rs diff --git a/crates/starknet_transaction_prover/src/main.rs b/crates/starknet_transaction_prover/src/main.rs index eb20c9860e5..4dacb4c05d3 100644 --- a/crates/starknet_transaction_prover/src/main.rs +++ b/crates/starknet_transaction_prover/src/main.rs @@ -21,6 +21,7 @@ async fn main() -> anyhow::Result<()> { TransportMode, }; use starknet_transaction_prover::server::cors::{build_cors_layer, cors_mode}; + use starknet_transaction_prover::server::log_redact::redact_url_host; use starknet_transaction_prover::server::rpc_api::ProvingRpcServer; use starknet_transaction_prover::server::rpc_impl::ProvingRpcServerImpl; use starknet_transaction_prover::server::{ @@ -47,6 +48,20 @@ async fn main() -> anyhow::Result<()> { let config = ServiceConfig::from_args(args)?; + // Startup banner — version + chain id + redacted RPC host only. No URLs + // with userinfo, no fee token address, no TLS paths, no tx data. + info!( + version = env!("CARGO_PKG_VERSION"), + git_sha = option_env!("GIT_SHA").unwrap_or("unknown"), + chain_id = %config.prover_config.chain_id, + rpc_node_host = %redact_url_host(&config.prover_config.rpc_node_url), + validate_zero_fee_fields = config.prover_config.validate_zero_fee_fields, + blocking_check_enabled = config.prover_config.blocking_check_url.is_some(), + blocking_check_fail_open = config.prover_config.blocking_check_fail_open, + ohttp_enabled = config.ohttp_enabled, + "Starting Starknet transaction prover." + ); + // Build and start the JSON-RPC server. let rpc_impl = ProvingRpcServerImpl::from_config(&config); let addr = SocketAddr::new(config.ip, config.port); diff --git a/crates/starknet_transaction_prover/src/server.rs b/crates/starknet_transaction_prover/src/server.rs index 577e63cb368..4732102e68d 100644 --- a/crates/starknet_transaction_prover/src/server.rs +++ b/crates/starknet_transaction_prover/src/server.rs @@ -30,6 +30,7 @@ pub mod config; pub mod cors; pub mod errors; pub mod health; +pub mod log_redact; #[cfg(test)] pub mod mock_rpc; pub mod rpc_api; diff --git a/crates/starknet_transaction_prover/src/server/config.rs b/crates/starknet_transaction_prover/src/server/config.rs index 13ce4f6b86f..403c97a750d 100644 --- a/crates/starknet_transaction_prover/src/server/config.rs +++ b/crates/starknet_transaction_prover/src/server/config.rs @@ -17,6 +17,7 @@ use crate::running::runner::RunnerConfig; use crate::running::storage_proofs::StorageProofConfig; use crate::running::virtual_block_executor::RpcVirtualBlockExecutorConfig; use crate::server::cors::normalize_cors_allow_origins; +use crate::server::log_redact::redact_url_host; #[cfg(test)] #[path = "config_test.rs"] @@ -178,7 +179,15 @@ impl ServiceConfig { // Override with CLI arguments if provided. if let Some(rpc_url) = args.rpc_url { if rpc_url != config.rpc_node_url { - info!("CLI override: rpc_node_url: {} -> {}", config.rpc_node_url, rpc_url); + // `rpc_node_url` defaults to an empty string when no config + // file is provided; treat empty as unset so the log doesn't + // claim `` for the previous value. + let previous = if config.rpc_node_url.is_empty() { + "".to_string() + } else { + redact_url_host(&config.rpc_node_url) + }; + info!("CLI override: rpc_node_url: {} -> {}", previous, redact_url_host(&rpc_url),); config.rpc_node_url = rpc_url; } } @@ -312,8 +321,12 @@ impl ServiceConfig { if let Some(url) = args.blocking_check_url { if Some(&url) != config.blocking_check_url.as_ref() { info!( - "CLI override: blocking_check_url: {:?} -> {:?}", - config.blocking_check_url, url + "CLI override: blocking_check_url: {} -> {}", + config + .blocking_check_url + .as_deref() + .map_or("".to_string(), redact_url_host), + redact_url_host(&url), ); config.blocking_check_url = Some(url); } diff --git a/crates/starknet_transaction_prover/src/server/log_redact.rs b/crates/starknet_transaction_prover/src/server/log_redact.rs new file mode 100644 index 00000000000..9ca1712ae4e --- /dev/null +++ b/crates/starknet_transaction_prover/src/server/log_redact.rs @@ -0,0 +1,22 @@ +//! Helpers for sanitizing values that appear in log lines. + +#[cfg(test)] +#[path = "log_redact_test.rs"] +mod log_redact_test; + +/// Returns `scheme://host[:port]` for a URL, dropping userinfo, path, query, +/// and fragment. Used to log upstream endpoints without echoing credentials +/// embedded in the URL. Falls back to `""` on parse failure so +/// the raw URL is never echoed. +pub fn redact_url_host(url: &str) -> String { + match url::Url::parse(url) { + Ok(parsed) => { + let host = parsed.host_str().unwrap_or(""); + match parsed.port() { + Some(port) => format!("{}://{}:{}", parsed.scheme(), host, port), + None => format!("{}://{}", parsed.scheme(), host), + } + } + Err(_) => "".to_string(), + } +} diff --git a/crates/starknet_transaction_prover/src/server/log_redact_test.rs b/crates/starknet_transaction_prover/src/server/log_redact_test.rs new file mode 100644 index 00000000000..65c6afac195 --- /dev/null +++ b/crates/starknet_transaction_prover/src/server/log_redact_test.rs @@ -0,0 +1,31 @@ +use crate::server::log_redact::redact_url_host; + +#[test] +fn strips_userinfo_path_and_query() { + assert_eq!( + redact_url_host("https://user:pass@rpc.example.com:8443/v1?token=abc"), + "https://rpc.example.com:8443" + ); +} + +#[test] +fn keeps_default_port_implicit() { + assert_eq!(redact_url_host("https://rpc.example.com/"), "https://rpc.example.com"); +} + +#[test] +fn returns_placeholder_for_invalid_url() { + assert_eq!(redact_url_host("not a url"), ""); +} + +#[test] +fn returns_placeholder_for_empty_string() { + // Pinned so callers know empty input lands in the invalid-url path and + // can guard with `` at the call site when that's misleading. + assert_eq!(redact_url_host(""), ""); +} + +#[test] +fn drops_fragment() { + assert_eq!(redact_url_host("https://rpc.example.com/#secret"), "https://rpc.example.com"); +}