Only allow http(s) scheme for urls (ref #3505) (#3508)
authorNutomic <me@nutomic.com>
Thu, 6 Jul 2023 12:29:51 +0000 (14:29 +0200)
committerGitHub <noreply@github.com>
Thu, 6 Jul 2023 12:29:51 +0000 (08:29 -0400)
With this change only http(s) schemes are allowed for post.url
field. This is checked for incoming api and federation requests.
Existing posts in database which are sent to clients are not
checked. Neither does it check urls in markdown.

crates/api_crud/src/post/create.rs
crates/api_crud/src/post/update.rs
crates/apub/src/objects/post.rs
crates/utils/src/utils/validation.rs

index 1dcc90241157fb06564b13e3b79ebf218ae98881..13d896b413cfa562ec81b5919f45d8d2674ba307 100644 (file)
@@ -31,7 +31,7 @@ use lemmy_utils::{
   error::LemmyError,
   utils::{
     slurs::{check_slurs, check_slurs_opt},
-    validation::{clean_url_params, is_valid_body_field, is_valid_post_title},
+    validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title},
   },
 };
 use tracing::{warn, Instrument};
@@ -58,6 +58,7 @@ impl PerformCrud for CreatePost {
 
     is_valid_post_title(&data.name)?;
     is_valid_body_field(&data.body, true)?;
+    check_url_scheme(&data.url)?;
 
     check_community_ban(local_user_view.person.id, data.community_id, context.pool()).await?;
     check_community_deleted_or_removed(data.community_id, context.pool()).await?;
index 632bc5ae75af78483582a15605a8668ee6bf2c54..a8c5715bb46b2ab88445c3d2107a5d095f2a4d6b 100644 (file)
@@ -20,7 +20,7 @@ use lemmy_utils::{
   error::LemmyError,
   utils::{
     slurs::check_slurs_opt,
-    validation::{clean_url_params, is_valid_body_field, is_valid_post_title},
+    validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title},
   },
 };
 
@@ -50,6 +50,7 @@ impl PerformCrud for EditPost {
     }
 
     is_valid_body_field(&data.body, true)?;
+    check_url_scheme(&data.url)?;
 
     let post_id = data.post_id;
     let orig_post = Post::read(context.pool(), post_id).await?;
index 8064c771b70c85b6507310e2d5edfe75f6d27733..d73642be63969373df4fc14f422c78515be9aff2 100644 (file)
@@ -44,6 +44,7 @@ use lemmy_utils::{
     markdown::markdown_to_html,
     slurs::{check_slurs_opt, remove_slurs},
     time::convert_datetime,
+    validation::check_url_scheme,
   },
 };
 use std::ops::Deref;
@@ -191,6 +192,7 @@ impl Object for ApubPost {
       } else {
         None
       };
+      check_url_scheme(&url)?;
 
       let local_site = LocalSite::read(context.pool()).await.ok();
       let allow_sensitive = local_site_opt_to_sensitive(&local_site);
index e43ddbbb54a85f0b8789c31f6a63de14549f5b38..b5ae5b74f3d473efd9474f002d41a6c15ab1d3dd 100644 (file)
@@ -302,12 +302,22 @@ pub fn check_site_visibility_valid(
   Ok(())
 }
 
+pub fn check_url_scheme(url: &Option<Url>) -> LemmyResult<()> {
+  if let Some(url) = url {
+    if url.scheme() != "http" && url.scheme() != "https" {
+      return Err(LemmyError::from_message("invalid_url_scheme"));
+    }
+  }
+  Ok(())
+}
+
 #[cfg(test)]
 mod tests {
   use super::build_totp_2fa;
   use crate::utils::validation::{
     build_and_check_regex,
     check_site_visibility_valid,
+    check_url_scheme,
     clean_url_params,
     generate_totp_2fa_secret,
     is_valid_actor_name,
@@ -519,4 +529,13 @@ mod tests {
     assert!(check_site_visibility_valid(false, false, &Some(true), &None).is_ok());
     assert!(check_site_visibility_valid(false, false, &None, &Some(true)).is_ok());
   }
+
+  #[test]
+  fn test_check_url_scheme() {
+    assert!(check_url_scheme(&None).is_ok());
+    assert!(check_url_scheme(&Some(Url::parse("http://example.com").unwrap())).is_ok());
+    assert!(check_url_scheme(&Some(Url::parse("https://example.com").unwrap())).is_ok());
+    assert!(check_url_scheme(&Some(Url::parse("ftp://example.com").unwrap())).is_err());
+    assert!(check_url_scheme(&Some(Url::parse("javascript:void").unwrap())).is_err());
+  }
 }