Skip to content

Commit af264ef

Browse files
fix(uri): redact credentials from endpoint debug output
1 parent 9e7f407 commit af264ef

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

‎src/sinks/loki/tests.rs‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@ fn generate_config() {
2424
test_util::test_generate_config::<LokiConfig>();
2525
}
2626

27+
#[test]
28+
fn config_debug_redacts_endpoint_credentials() {
29+
let (config, _cx) = load_sink::<LokiConfig>(
30+
r#"
31+
endpoint = "http://user:secret@example.com"
32+
labels = {test_name = "placeholder"}
33+
encoding.codec = "json"
34+
"#,
35+
)
36+
.unwrap();
37+
38+
let debug = format!("{config:?}");
39+
assert!(!debug.contains("secret"));
40+
assert!(debug.contains("http://<redacted>@example.com"));
41+
}
42+
2743
#[tokio::test]
2844
async fn interpolate_labels() {
2945
let (config, cx) = load_sink::<LokiConfig>(

‎src/sinks/util/uri.rs‎

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
#![expect(
2+
clippy::let_underscore_must_use,
3+
reason = "derivative's Debug derive with format_with expands to a must_use let binding"
4+
)]
5+
16
use std::{fmt, str::FromStr};
27

8+
use derivative::Derivative;
39
use http::uri::{Authority, PathAndQuery, Scheme, Uri};
410
use percent_encoding::percent_decode_str;
511
use snafu::{ResultExt, Snafu};
@@ -239,9 +245,29 @@ pub enum HttpEndpointError {
239245
/// still an absolute `http(s)` URL.
240246
#[configurable_component]
241247
#[configurable(title = "An absolute HTTP(S) URL.", description = "")]
242-
#[derive(Debug, Clone, PartialEq, Eq)]
248+
#[derive(Derivative, Clone, PartialEq, Eq)]
249+
#[derivative(Debug)]
243250
#[serde(try_from = "String", into = "String")]
244-
pub struct HttpEndpoint(Uri);
251+
pub struct HttpEndpoint(#[derivative(Debug(format_with = "fmt_http_endpoint_uri"))] Uri);
252+
253+
fn fmt_http_endpoint_uri(uri: &Uri, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254+
if let Some(scheme) = uri.scheme_str() {
255+
write!(f, "{scheme}://")?;
256+
}
257+
if let Some(authority) = uri.authority() {
258+
if authority.as_str().contains('@') {
259+
f.write_str("<redacted>@")?;
260+
}
261+
f.write_str(authority.host())?;
262+
if let Some(port) = authority.port() {
263+
write!(f, ":{}", port.as_str())?;
264+
}
265+
}
266+
if let Some(path_and_query) = uri.path_and_query() {
267+
f.write_str(path_and_query.as_str())?;
268+
}
269+
Ok(())
270+
}
245271

246272
impl TryFrom<String> for HttpEndpoint {
247273
type Error = HttpEndpointError;
@@ -539,6 +565,16 @@ mod tests {
539565
}
540566
}
541567

568+
#[test]
569+
fn http_endpoint_debug_redacts_userinfo() {
570+
let endpoint =
571+
HttpEndpoint::parse("http://user:secret@example.com:8080/path?query=1").unwrap();
572+
assert_eq!(
573+
format!("{endpoint:?}"),
574+
"HttpEndpoint(http://<redacted>@example.com:8080/path?query=1)"
575+
);
576+
}
577+
542578
#[test]
543579
fn http_endpoint_defaults_missing_scheme_to_https() {
544580
for endpoint in [

0 commit comments

Comments
 (0)