#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DangerousUriScheme {
pub scheme: String,
}
impl std::fmt::Display for DangerousUriScheme {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "dangerous URI scheme rejected: {}", self.scheme)
}
}
impl std::error::Error for DangerousUriScheme {}
const BLOCKED_SCHEMES: &[&str] = &["javascript", "data", "vbscript", "file", "blob"];
pub fn sanitize_uri_scheme(uri: &str) -> Result<(), DangerousUriScheme> {
if uri.is_empty() || uri.starts_with('/') || uri.starts_with('#') || uri.starts_with('?') {
return Ok(());
}
if let Some(colon_pos) = uri.find(':') {
let scheme = &uri[..colon_pos];
if scheme.contains('/') {
return Ok(());
}
let scheme_lower = scheme.to_ascii_lowercase();
if BLOCKED_SCHEMES.contains(&scheme_lower.as_str()) {
return Err(DangerousUriScheme {
scheme: scheme_lower,
});
}
}
Ok(())
}