157 lines
4.4 KiB
Rust
157 lines
4.4 KiB
Rust
use crate::domain::AllowedContext;
|
|
|
|
pub fn domain_allowed(context: &AllowedContext, candidate: &str) -> bool {
|
|
let candidate = normalize_domain(candidate);
|
|
if candidate.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
context.domains.iter().any(|allowed| {
|
|
let allowed = normalize_domain(allowed);
|
|
!allowed.is_empty()
|
|
&& (candidate == allowed
|
|
|| candidate
|
|
.strip_suffix(&allowed)
|
|
.is_some_and(|prefix| prefix.ends_with('.')))
|
|
})
|
|
}
|
|
|
|
pub fn window_class_allowed(context: &AllowedContext, candidate: &str) -> bool {
|
|
let candidate = normalize_casefolded(candidate);
|
|
if candidate.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
context
|
|
.window_classes
|
|
.iter()
|
|
.any(|allowed| normalize_casefolded(allowed) == candidate)
|
|
}
|
|
|
|
pub fn window_title_allowed(context: &AllowedContext, candidate: &str) -> bool {
|
|
let candidate = normalize_casefolded(candidate);
|
|
if candidate.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
context.window_title_substrings.iter().any(|allowed| {
|
|
let allowed = normalize_casefolded(allowed);
|
|
!allowed.is_empty() && candidate.contains(&allowed)
|
|
})
|
|
}
|
|
|
|
pub fn command_allowed(context: &AllowedContext, candidate: &str) -> bool {
|
|
let candidate = executable_basename(candidate);
|
|
if candidate.is_empty() {
|
|
return false;
|
|
}
|
|
|
|
context
|
|
.commands
|
|
.iter()
|
|
.any(|allowed| executable_basename(allowed) == candidate)
|
|
}
|
|
|
|
fn normalize_domain(value: &str) -> String {
|
|
value.trim().trim_end_matches('.').to_lowercase()
|
|
}
|
|
|
|
fn normalize_casefolded(value: &str) -> String {
|
|
value.trim().to_lowercase()
|
|
}
|
|
|
|
fn executable_basename(command: &str) -> String {
|
|
let executable = command.split_whitespace().next().unwrap_or_default();
|
|
executable
|
|
.trim()
|
|
.rsplit(['/', '\\'])
|
|
.next()
|
|
.unwrap_or_default()
|
|
.to_lowercase()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::domain::AllowedContext;
|
|
|
|
#[test]
|
|
fn domains_match_exact_or_subdomain() {
|
|
let context = AllowedContext {
|
|
domains: vec!["github.com".to_string()],
|
|
..AllowedContext::default()
|
|
};
|
|
|
|
assert!(domain_allowed(&context, "github.com"));
|
|
assert!(domain_allowed(&context, "docs.github.com"));
|
|
assert!(!domain_allowed(&context, "evilgithub.com"));
|
|
}
|
|
|
|
#[test]
|
|
fn window_classes_match_case_insensitively_after_normalization() {
|
|
let context = AllowedContext {
|
|
window_classes: vec!["code".to_string()],
|
|
..AllowedContext::default()
|
|
};
|
|
|
|
assert!(window_class_allowed(&context, "Code"));
|
|
assert!(!window_class_allowed(&context, "firefox"));
|
|
}
|
|
|
|
#[test]
|
|
fn titles_match_by_substring() {
|
|
let context = AllowedContext {
|
|
window_title_substrings: vec!["antidrift".to_string()],
|
|
..AllowedContext::default()
|
|
};
|
|
|
|
assert!(window_title_allowed(&context, "Commitment OS - AntiDrift"));
|
|
assert!(!window_title_allowed(&context, "random video"));
|
|
}
|
|
|
|
#[test]
|
|
fn domains_ignore_whitespace_case_and_trailing_dots() {
|
|
let context = AllowedContext {
|
|
domains: vec![" GitHub.COM. ".to_string()],
|
|
..AllowedContext::default()
|
|
};
|
|
|
|
assert!(domain_allowed(&context, " DOCS.GITHUB.COM. "));
|
|
}
|
|
|
|
#[test]
|
|
fn window_class_matching_trims_allowed_and_candidate_values() {
|
|
let context = AllowedContext {
|
|
window_classes: vec![" code ".to_string()],
|
|
..AllowedContext::default()
|
|
};
|
|
|
|
assert!(window_class_allowed(&context, " Code "));
|
|
}
|
|
|
|
#[test]
|
|
fn title_matching_trims_allowed_substrings() {
|
|
let context = AllowedContext {
|
|
window_title_substrings: vec![" antidrift ".to_string()],
|
|
..AllowedContext::default()
|
|
};
|
|
|
|
assert!(window_title_allowed(&context, "Commitment OS - AntiDrift"));
|
|
}
|
|
|
|
#[test]
|
|
fn commands_match_executable_basename() {
|
|
let context = AllowedContext {
|
|
commands: vec!["cargo".to_string()],
|
|
..AllowedContext::default()
|
|
};
|
|
|
|
assert!(command_allowed(
|
|
&context,
|
|
"/home/felixm/.cargo/bin/cargo test"
|
|
));
|
|
assert!(command_allowed(&context, "cargo"));
|
|
assert!(!command_allowed(&context, "/usr/bin/git status"));
|
|
}
|
|
}
|