pub fn matches_allowlist(host: &str, allowlist: &[String]) -> bool {
if host.is_empty() {
return false;
}
let host_lc = host.to_ascii_lowercase();
for entry in allowlist {
let entry_lc = entry.to_ascii_lowercase();
if let Some(suffix) = entry_lc.strip_prefix("*.") {
if host_lc.len() > suffix.len() + 1
&& host_lc.ends_with(suffix)
&& host_lc.as_bytes()[host_lc.len() - suffix.len() - 1] == b'.'
{
return true;
}
} else if host_lc == entry_lc {
return true;
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn literal_match_exact() {
assert!(matches_allowlist(
"api.example.com",
&["api.example.com".into()]
));
}
#[test]
fn literal_does_not_match_subdomain() {
assert!(!matches_allowlist(
"x.api.example.com",
&["api.example.com".into()]
));
}
#[test]
fn wildcard_matches_one_label() {
assert!(matches_allowlist(
"foo.cdn.example.com",
&["*.cdn.example.com".into()]
));
}
#[test]
fn wildcard_matches_multiple_labels() {
assert!(matches_allowlist(
"deep.foo.cdn.example.com",
&["*.cdn.example.com".into()]
));
}
#[test]
fn wildcard_does_not_match_apex() {
assert!(!matches_allowlist(
"cdn.example.com",
&["*.cdn.example.com".into()]
));
}
#[test]
fn wildcard_does_not_match_sibling_with_dash() {
assert!(!matches_allowlist(
"evil-cdn.example.com",
&["*.cdn.example.com".into()]
));
}
#[test]
fn empty_host_returns_false() {
assert!(!matches_allowlist(
"",
&["api.example.com".into(), "*.cdn.example.com".into()]
));
}
#[test]
fn empty_allowlist_returns_false() {
assert!(!matches_allowlist("api.example.com", &[]));
}
#[test]
fn case_insensitive_on_host() {
assert!(matches_allowlist(
"API.Example.COM",
&["api.example.com".into()]
));
}
#[test]
fn case_insensitive_on_entry() {
assert!(matches_allowlist(
"api.example.com",
&["API.EXAMPLE.COM".into()]
));
}
#[test]
fn multiple_entries_or_semantics() {
let allow: Vec<String> = vec!["api.example.com".into(), "*.cdn.example.com".into()];
assert!(matches_allowlist("api.example.com", &allow));
assert!(matches_allowlist("img.cdn.example.com", &allow));
assert!(!matches_allowlist("blocked.example.com", &allow));
}
}