use ::url::{
ParseError,
Url,
form_urlencoded,
};
use crate::{
FieldSanitizer,
NameMatchMode,
SensitivityLevel,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UrlSanitizer {
field_sanitizer: FieldSanitizer,
}
impl UrlSanitizer {
pub const fn new(field_sanitizer: FieldSanitizer) -> Self {
Self { field_sanitizer }
}
pub const fn field_sanitizer(&self) -> &FieldSanitizer {
&self.field_sanitizer
}
pub fn field_sanitizer_mut(&mut self) -> &mut FieldSanitizer {
&mut self.field_sanitizer
}
pub fn sanitize_url(&self, url: &Url, match_mode: NameMatchMode) -> String {
let mut sanitized = url.clone();
if !sanitized.username().is_empty() {
let username = mask_url_component(&self.field_sanitizer, sanitized.username());
let _ = sanitized.set_username(&username);
}
if let Some(password) = sanitized.password() {
let password = mask_url_component(&self.field_sanitizer, password);
let _ = sanitized.set_password(Some(&password));
}
if let Some(fragment) = sanitized.fragment() {
let fragment = mask_url_component(&self.field_sanitizer, fragment);
sanitized.set_fragment(Some(&fragment));
}
let Some(_) = sanitized.query() else {
return sanitized.to_string();
};
let mut serializer = form_urlencoded::Serializer::new(String::new());
for (key, value) in url.query_pairs() {
let sanitized_value =
self.field_sanitizer
.sanitize_value(key.as_ref(), value.as_ref(), match_mode);
serializer.append_pair(key.as_ref(), sanitized_value.as_ref());
}
sanitized.set_query(Some(&serializer.finish()));
sanitized.to_string()
}
pub fn sanitize_url_str(
&self,
url: &str,
match_mode: NameMatchMode,
) -> Result<String, ParseError> {
Url::parse(url).map(|url| self.sanitize_url(&url, match_mode))
}
}
impl Default for UrlSanitizer {
fn default() -> Self {
Self::new(FieldSanitizer::default())
}
}
fn mask_url_component(sanitizer: &FieldSanitizer, value: &str) -> String {
sanitizer
.policy()
.mask_policies
.for_level(SensitivityLevel::High)
.mask(value)
.into_owned()
}