diff --git a/conduwuit-example.toml b/conduwuit-example.toml index ef8fc23d..0baba84f 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -240,7 +240,7 @@ allow_device_name_federation = false url_preview_domain_contains_allowlist = [] # Vector list of explicit domains allowed to send requests to for URL previews. Defaults to none. -# Note: This is an *explicit* match, not a ccontains match. Putting "google.com" will match "https://google.com", "http://google.com", but not "https://mymaliciousdomainexamplegoogle.com" +# Note: This is an *explicit* match, not a contains match. Putting "google.com" will match "https://google.com", "http://google.com", but not "https://mymaliciousdomainexamplegoogle.com" # Setting this to "*" will allow all URL previews. Please note that this opens up significant attack surface to your server, you are expected to be aware of the risks by doing so. url_preview_domain_explicit_allowlist = [] @@ -249,6 +249,11 @@ url_preview_domain_explicit_allowlist = [] # Setting this to "*" will allow all URL previews. Please note that this opens up significant attack surface to your server, you are expected to be aware of the risks by doing so. url_preview_url_contains_allowlist = [] +# Vector list of explicit domains not allowed to send requests to for URL previews. Defaults to none. +# Note: This is an *explicit* match, not a contains match. Putting "google.com" will match "https://google.com", "http://google.com", but not "https://mymaliciousdomainexamplegoogle.com" +# The denylist is checked first before allowlist. Setting this to "*" will not do anything. +url_preview_domain_explicit_denylist = [] + # Maximum amount of bytes allowed in a URL preview body size when spidering. Defaults to 384KB (384_000 bytes) url_preview_max_spider_size = 384_000 diff --git a/src/api/client_server/media.rs b/src/api/client_server/media.rs index 52c6c4b2..40e2b093 100644 --- a/src/api/client_server/media.rs +++ b/src/api/client_server/media.rs @@ -800,6 +800,7 @@ fn url_preview_allowed(url_str: &str) -> bool { let allowlist_domain_contains = services().globals.url_preview_domain_contains_allowlist(); let allowlist_domain_explicit = services().globals.url_preview_domain_explicit_allowlist(); + let denylist_domain_explicit = services().globals.url_preview_domain_explicit_denylist(); let allowlist_url_contains = services().globals.url_preview_url_contains_allowlist(); if allowlist_domain_contains.contains(&"*".to_owned()) @@ -811,8 +812,16 @@ fn url_preview_allowed(url_str: &str) -> bool { } if !host.is_empty() { + if denylist_domain_explicit.contains(&host) { + debug!( + "Host {} is not allowed by url_preview_domain_explicit_denylist (check 1/4)", + &host + ); + return false; + } + if allowlist_domain_explicit.contains(&host) { - debug!("Host {} is allowed by url_preview_domain_explicit_allowlist (check 1/3)", &host); + debug!("Host {} is allowed by url_preview_domain_explicit_allowlist (check 2/4)", &host); return true; } @@ -820,7 +829,7 @@ fn url_preview_allowed(url_str: &str) -> bool { .iter() .any(|domain_s| domain_s.contains(&host.clone())) { - debug!("Host {} is allowed by url_preview_domain_contains_allowlist (check 2/3)", &host); + debug!("Host {} is allowed by url_preview_domain_contains_allowlist (check 3/4)", &host); return true; } @@ -828,7 +837,7 @@ fn url_preview_allowed(url_str: &str) -> bool { .iter() .any(|url_s| url.to_string().contains(&url_s.to_string())) { - debug!("URL {} is allowed by url_preview_url_contains_allowlist (check 3/3)", &host); + debug!("URL {} is allowed by url_preview_url_contains_allowlist (check 4/4)", &host); return true; } @@ -838,9 +847,17 @@ fn url_preview_allowed(url_str: &str) -> bool { match host.split_once('.') { None => return false, Some((_, root_domain)) => { + if denylist_domain_explicit.contains(&root_domain.to_owned()) { + debug!( + "Root domain {} is not allowed by url_preview_domain_explicit_denylist (check 1/3)", + &root_domain + ); + return true; + } + if allowlist_domain_explicit.contains(&root_domain.to_owned()) { debug!( - "Root domain {} is allowed by url_preview_domain_explicit_allowlist (check 1/3)", + "Root domain {} is allowed by url_preview_domain_explicit_allowlist (check 2/3)", &root_domain ); return true; @@ -851,7 +868,7 @@ fn url_preview_allowed(url_str: &str) -> bool { .any(|domain_s| domain_s.contains(&root_domain.to_owned())) { debug!( - "Root domain {} is allowed by url_preview_domain_contains_allowlist (check 2/3)", + "Root domain {} is allowed by url_preview_domain_contains_allowlist (check 3/3)", &root_domain ); return true; diff --git a/src/config/mod.rs b/src/config/mod.rs index b60f745a..0a616be1 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -278,6 +278,8 @@ pub struct Config { #[serde(default = "Vec::new")] pub url_preview_domain_explicit_allowlist: Vec, #[serde(default = "Vec::new")] + pub url_preview_domain_explicit_denylist: Vec, + #[serde(default = "Vec::new")] pub url_preview_url_contains_allowlist: Vec, #[serde(default = "default_url_preview_max_spider_size")] pub url_preview_max_spider_size: usize, @@ -709,6 +711,10 @@ impl fmt::Display for Config { "URL preview domain explicit allowlist", &self.url_preview_domain_explicit_allowlist.join(", "), ), + ( + "URL preview domain explicit denylist", + &self.url_preview_domain_explicit_denylist.join(", "), + ), ( "URL preview URL contains allowlist", &self.url_preview_url_contains_allowlist.join(", "), diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index 78714d24..307aaff3 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -277,6 +277,10 @@ impl Service<'_> { &self.config.url_preview_domain_explicit_allowlist } + pub fn url_preview_domain_explicit_denylist(&self) -> &Vec { + &self.config.url_preview_domain_explicit_denylist + } + pub fn url_preview_url_contains_allowlist(&self) -> &Vec { &self.config.url_preview_url_contains_allowlist } pub fn url_preview_max_spider_size(&self) -> usize { self.config.url_preview_max_spider_size }