use axum::http::HeaderMap;
pub fn origin_allowed(headers: &HeaderMap, extras: &[String]) -> bool {
let origin = match headers.get("origin").and_then(|v| v.to_str().ok()) {
Some(s) => s,
None => return true,
};
if is_default_loopback_origin(origin) {
return true;
}
extras.iter().any(|p| origin == p)
}
pub fn is_default_loopback_origin(origin: &str) -> bool {
origin.starts_with("http://127.0.0.1")
|| origin.starts_with("http://localhost")
|| origin.starts_with("https://127.0.0.1")
|| origin.starts_with("https://localhost")
|| origin == "tauri://localhost"
|| origin.starts_with("tauri://")
}
#[cfg(test)]
mod tests {
use super::*;
use axum::http::HeaderValue;
fn headers_with_origin(origin: &str) -> HeaderMap {
let mut h = HeaderMap::new();
h.insert("origin", HeaderValue::from_str(origin).unwrap());
h
}
#[test]
fn default_allows_loopback_http() {
assert!(origin_allowed(
&headers_with_origin("http://127.0.0.1:5173"),
&[]
));
assert!(origin_allowed(
&headers_with_origin("http://localhost:1420"),
&[]
));
}
#[test]
fn default_allows_loopback_https_and_tauri() {
assert!(origin_allowed(
&headers_with_origin("https://127.0.0.1"),
&[]
));
assert!(origin_allowed(
&headers_with_origin("https://localhost:8443"),
&[]
));
assert!(origin_allowed(
&headers_with_origin("tauri://localhost"),
&[]
));
assert!(origin_allowed(&headers_with_origin("tauri://custom"), &[]));
}
#[test]
fn default_rejects_remote_origin() {
assert!(!origin_allowed(
&headers_with_origin("https://evil.example"),
&[]
));
assert!(!origin_allowed(
&headers_with_origin("http://attacker.test:8080"),
&[]
));
}
#[test]
fn extras_admits_exact_match_only() {
let extras = vec!["https://celia.health".to_string()];
assert!(origin_allowed(
&headers_with_origin("https://celia.health"),
&extras
));
assert!(!origin_allowed(
&headers_with_origin("https://celia.health.evil.example"),
&extras
));
assert!(origin_allowed(
&headers_with_origin("http://127.0.0.1:5173"),
&extras
));
}
#[test]
fn missing_origin_header_is_allowed() {
let headers = HeaderMap::new();
assert!(origin_allowed(&headers, &[]));
}
}