#[must_use]
pub(crate) fn sanitize_text_for_display(text: &str) -> String {
text.chars().flat_map(char::escape_default).collect()
}
#[must_use]
pub fn sanitize_uri_for_display(uri: &str) -> String {
let uri = strip_fragment_and_query(uri);
let uri = strip_userinfo(uri);
sanitize_text_for_display(&uri)
}
fn strip_fragment_and_query(uri: &str) -> &str {
uri.find(['?', '#']).map_or(uri, |index| &uri[..index])
}
fn strip_userinfo(uri: &str) -> String {
let Some(scheme_end) = uri.find("://") else {
return uri.to_owned();
};
let authority_start = scheme_end + 3;
let authority_end = uri[authority_start..]
.find('/')
.map_or(uri.len(), |relative_end| authority_start + relative_end);
let authority = &uri[authority_start..authority_end];
let Some(userinfo_end) = authority.rfind('@') else {
return uri.to_owned();
};
format!(
"{}{}{}",
&uri[..authority_start],
&authority[userinfo_end + 1..],
&uri[authority_end..]
)
}
#[cfg(test)]
mod tests {
use super::sanitize_uri_for_display;
#[test]
fn uri_display_redacts_query_fragment_and_userinfo() {
let display =
sanitize_uri_for_display("s3://user:password@example.com/table?token=secret#debug");
assert_eq!(display, "s3://example.com/table");
}
#[test]
fn uri_display_redacts_fragment_without_query() {
let display = sanitize_uri_for_display("https://example.com/table#access_token=secret");
assert_eq!(display, "https://example.com/table");
}
#[test]
fn uri_display_preserves_at_signs_outside_authority() {
let display = sanitize_uri_for_display("s3://bucket/path@name/table?token=secret");
assert_eq!(display, "s3://bucket/path@name/table");
}
#[test]
fn uri_display_escapes_control_characters() {
let display = sanitize_uri_for_display("s3://bucket/table\nname");
assert_eq!(display, r"s3://bucket/table\nname");
}
}