Skip to content

Commit 1f06693

Browse files
authored
[0.19] Add IP check for webmention (#6444)
* [0.19] Add IP check for webmention * ci * clippy
1 parent 05b59e4 commit 1f06693

2 files changed

Lines changed: 35 additions & 21 deletions

File tree

crates/api_common/src/request.rs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use serde::{Deserialize, Serialize};
3737
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3838
use tokio::net::lookup_host;
3939
use tracing::{info, warn};
40-
use url::Url;
40+
use url::{Host, Url};
4141
use urlencoding::encode;
4242
use webpage::HTML;
4343

@@ -64,24 +64,7 @@ pub async fn fetch_link_metadata(
6464
return Err(LemmyErrorType::InvalidUrl.into());
6565
}
6666

67-
// Resolve the domain and throw an error if it points to any internal IP,
68-
// using logic from nightly IpAddr::is_global.
69-
if !cfg!(debug_assertions) {
70-
// TODO: Replace with IpAddr::is_global() once stabilized
71-
// https://doc.rust-lang.org/std/net/enum.IpAddr.html#method.is_global
72-
let domain = url.domain().ok_or(LemmyErrorType::UrlWithoutDomain)?;
73-
let invalid_ip =
74-
lookup_host((domain.to_owned(), 80))
75-
.await?
76-
.any(|addr| match addr.ip().to_canonical() {
77-
IpAddr::V4(addr) => v4_is_invalid(addr),
78-
IpAddr::V6(addr) => v6_is_invalid(addr),
79-
});
80-
if invalid_ip {
81-
return Err(LemmyErrorType::InvalidUrl.into());
82-
}
83-
}
84-
67+
validate_link_ip(url).await?;
8568
info!("Fetching site metadata for url: {}", url);
8669
// We only fetch the first MB of data in order to not waste bandwidth especially for large
8770
// binary files. This high limit is particularly needed for youtube, which includes a lot of
@@ -161,6 +144,37 @@ pub async fn fetch_link_metadata(
161144
})
162145
}
163146

147+
/// Resolve the domain and throw an error if it points to any internal IP.
148+
pub async fn validate_link_ip(url: &Url) -> LemmyResult<()> {
149+
if cfg!(debug_assertions) {
150+
return Ok(());
151+
}
152+
// Resolve the domain and throw an error if it points to any internal IP,
153+
// using logic from nightly IpAddr::is_global.
154+
155+
// TODO: Replace with IpAddr::is_global() once stabilized
156+
// https://doc.rust-lang.org/std/net/enum.IpAddr.html#method.is_global
157+
let mut ip = vec![];
158+
match url.host().ok_or(LemmyErrorType::UrlWithoutDomain)? {
159+
Host::Domain(domain) => ip.extend(
160+
lookup_host((domain.to_owned(), 80))
161+
.await?
162+
.map(|s| s.ip().to_canonical()),
163+
),
164+
Host::Ipv4(ipv4) => ip.push(ipv4.into()),
165+
Host::Ipv6(ipv6) => ip.push(ipv6.into()),
166+
};
167+
168+
let invalid_ip = ip.into_iter().any(|addr| match addr {
169+
IpAddr::V4(addr) => v4_is_invalid(addr),
170+
IpAddr::V6(addr) => v6_is_invalid(addr),
171+
});
172+
if invalid_ip {
173+
return Err(LemmyErrorType::InvalidUrl.into());
174+
}
175+
Ok(())
176+
}
177+
164178
fn v4_is_invalid(v4: Ipv4Addr) -> bool {
165179
v4.is_private()
166180
|| v4.is_loopback()

crates/api_crud/src/post/create.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use lemmy_api_common::{
44
build_response::build_post_response,
55
context::LemmyContext,
66
post::{CreatePost, PostResponse},
7-
request::generate_post_link_metadata,
7+
request::{generate_post_link_metadata, validate_link_ip},
88
send_activity::SendActivityData,
99
utils::{
1010
check_community_user_action,
@@ -178,7 +178,7 @@ pub async fn create_post(
178178
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
179179

180180
if let Some(url) = inserted_post.url.clone() {
181-
if community.visibility == CommunityVisibility::Public {
181+
if community.visibility == CommunityVisibility::Public && validate_link_ip(&url).await.is_ok() {
182182
spawn_try_task(async move {
183183
let mut webmention =
184184
Webmention::new::<Url>(inserted_post.ap_id.clone().into(), url.clone().into())?;

0 commit comments

Comments
 (0)