diff --git a/api_tests/src/post.spec.ts b/api_tests/src/post.spec.ts index 49840d49f..30a176fee 100644 --- a/api_tests/src/post.spec.ts +++ b/api_tests/src/post.spec.ts @@ -554,29 +554,3 @@ test("Report a post", async () => { expect(betaReport.original_post_body).toBe(alphaReport.original_post_body); expect(betaReport.reason).toBe(alphaReport.reason); }); - -test("Sanitize HTML", async () => { - let betaCommunity = (await resolveBetaCommunity(beta)).community; - if (!betaCommunity) { - throw "Missing beta community"; - } - - let name = randomString(5); - let body = " hello &\"'"; - let form: CreatePost = { - name, - body, - community_id: betaCommunity.community.id, - }; - let post = await beta.createPost(form); - // first escaping for the api - expect(post.post_view.post.body).toBe( - "<script>alert('xss');</script> hello &"'", - ); - - let alphaPost = (await resolvePost(alpha, post.post_view.post)).post; - // second escaping over federation, avoid double escape of & - expect(alphaPost?.post.body).toBe( - "<script>alert('xss');</script> hello &"'", - ); -}); diff --git a/crates/api/src/comment_report/create.rs b/crates/api/src/comment_report/create.rs index 3c1304674..e1040fd5e 100644 --- a/crates/api/src/comment_report/create.rs +++ b/crates/api/src/comment_report/create.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ comment::{CommentReportResponse, CreateCommentReport}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, sanitize_html_api, send_new_report_email_to_admins}, + utils::{check_community_ban, send_new_report_email_to_admins}, }; use lemmy_db_schema::{ source::{ @@ -26,7 +26,7 @@ pub async fn create_comment_report( ) -> Result, LemmyError> { let local_site = LocalSite::read(&mut context.pool()).await?; - let reason = sanitize_html_api(data.reason.trim()); + let reason = data.reason.trim().to_string(); check_report_reason(&reason, &local_site)?; let person_id = local_user_view.person.id; diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs index c2f70b7c0..2cc40cd1d 100644 --- a/crates/api/src/community/ban.rs +++ b/crates/api/src/community/ban.rs @@ -4,7 +4,7 @@ use lemmy_api_common::{ community::{BanFromCommunity, BanFromCommunityResponse}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{is_mod_or_admin, remove_user_data_in_community, sanitize_html_api_opt}, + utils::{is_mod_or_admin, remove_user_data_in_community}, }; use lemmy_db_schema::{ source::{ @@ -81,7 +81,7 @@ pub async fn ban_from_community( mod_person_id: local_user_view.person.id, other_person_id: data.person_id, community_id: data.community_id, - reason: sanitize_html_api_opt(&data.reason), + reason: data.reason.clone(), banned: Some(data.ban), expires, }; diff --git a/crates/api/src/community/hide.rs b/crates/api/src/community/hide.rs index 5f2b9d6fb..dd8343927 100644 --- a/crates/api/src/community/hide.rs +++ b/crates/api/src/community/hide.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ community::{CommunityResponse, HideCommunity}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{is_admin, sanitize_html_api_opt}, + utils::is_admin, }; use lemmy_db_schema::{ source::{ @@ -34,7 +34,7 @@ pub async fn hide_community( let mod_hide_community_form = ModHideCommunityForm { community_id: data.community_id, mod_person_id: local_user_view.person.id, - reason: sanitize_html_api_opt(&data.reason), + reason: data.reason.clone(), hidden: Some(data.hidden), }; diff --git a/crates/api/src/local_user/ban_person.rs b/crates/api/src/local_user/ban_person.rs index e8eaecc0a..8ff203f0e 100644 --- a/crates/api/src/local_user/ban_person.rs +++ b/crates/api/src/local_user/ban_person.rs @@ -4,7 +4,7 @@ use lemmy_api_common::{ context::LemmyContext, person::{BanPerson, BanPersonResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{is_admin, remove_user_data, sanitize_html_api_opt}, + utils::{is_admin, remove_user_data}, }; use lemmy_db_schema::{ source::{ @@ -61,7 +61,7 @@ pub async fn ban_from_site( let form = ModBanForm { mod_person_id: local_user_view.person.id, other_person_id: data.person_id, - reason: sanitize_html_api_opt(&data.reason), + reason: data.reason.clone(), banned: Some(data.ban), expires, }; diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs index c3d7eca23..d219416f8 100644 --- a/crates/api/src/local_user/save_settings.rs +++ b/crates/api/src/local_user/save_settings.rs @@ -2,7 +2,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ context::LemmyContext, person::SaveUserSettings, - utils::{sanitize_html_api_opt, send_verification_email}, + utils::send_verification_email, SuccessResponse, }; use lemmy_db_schema::{ @@ -28,13 +28,10 @@ pub async fn save_user_settings( ) -> Result, LemmyError> { let site_view = SiteView::read_local(&mut context.pool()).await?; - let bio = sanitize_html_api_opt(&data.bio); - let display_name = sanitize_html_api_opt(&data.display_name); - let avatar = diesel_option_overwrite_to_url(&data.avatar)?; let banner = diesel_option_overwrite_to_url(&data.banner)?; - let bio = diesel_option_overwrite(bio); - let display_name = diesel_option_overwrite(display_name); + let bio = diesel_option_overwrite(data.bio.clone()); + let display_name = diesel_option_overwrite(data.display_name.clone()); let matrix_user_id = diesel_option_overwrite(data.matrix_user_id.clone()); let email_deref = data.email.as_deref().map(str::to_lowercase); let email = diesel_option_overwrite(email_deref.clone()); @@ -82,7 +79,6 @@ pub async fn save_user_settings( let person_id = local_user_view.person.id; let default_listing_type = data.default_listing_type; let default_sort_type = data.default_sort_type; - let theme = sanitize_html_api_opt(&data.theme); let person_form = PersonUpdateForm { display_name, @@ -114,7 +110,7 @@ pub async fn save_user_settings( show_scores: data.show_scores, default_sort_type, default_listing_type, - theme, + theme: data.theme.clone(), interface_language: data.interface_language.clone(), open_links_in_new_tab: data.open_links_in_new_tab, infinite_scroll_enabled: data.infinite_scroll_enabled, diff --git a/crates/api/src/post_report/create.rs b/crates/api/src/post_report/create.rs index 6a32d505b..126475c0c 100644 --- a/crates/api/src/post_report/create.rs +++ b/crates/api/src/post_report/create.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ context::LemmyContext, post::{CreatePostReport, PostReportResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, sanitize_html_api, send_new_report_email_to_admins}, + utils::{check_community_ban, send_new_report_email_to_admins}, }; use lemmy_db_schema::{ source::{ @@ -26,7 +26,7 @@ pub async fn create_post_report( ) -> Result, LemmyError> { let local_site = LocalSite::read(&mut context.pool()).await?; - let reason = sanitize_html_api(data.reason.trim()); + let reason = data.reason.trim().to_string(); check_report_reason(&reason, &local_site)?; let person_id = local_user_view.person.id; diff --git a/crates/api/src/private_message_report/create.rs b/crates/api/src/private_message_report/create.rs index 2c4291581..75620bf8b 100644 --- a/crates/api/src/private_message_report/create.rs +++ b/crates/api/src/private_message_report/create.rs @@ -3,7 +3,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ context::LemmyContext, private_message::{CreatePrivateMessageReport, PrivateMessageReportResponse}, - utils::{sanitize_html_api, send_new_report_email_to_admins}, + utils::send_new_report_email_to_admins, }; use lemmy_db_schema::{ source::{ @@ -24,7 +24,7 @@ pub async fn create_pm_report( ) -> Result, LemmyError> { let local_site = LocalSite::read(&mut context.pool()).await?; - let reason = sanitize_html_api(data.reason.trim()); + let reason = data.reason.trim().to_string(); check_report_reason(&reason, &local_site)?; let person_id = local_user_view.person.id; @@ -35,7 +35,7 @@ pub async fn create_pm_report( creator_id: person_id, private_message_id, original_pm_text: private_message.content, - reason: reason.clone(), + reason, }; let report = PrivateMessageReport::report(&mut context.pool(), &report_form) diff --git a/crates/api/src/site/purge/comment.rs b/crates/api/src/site/purge/comment.rs index ec4ef02af..1537bc416 100644 --- a/crates/api/src/site/purge/comment.rs +++ b/crates/api/src/site/purge/comment.rs @@ -2,7 +2,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ context::LemmyContext, site::{PurgeComment, PurgeItemResponse}, - utils::{is_admin, sanitize_html_api_opt}, + utils::is_admin, }; use lemmy_db_schema::{ source::{ @@ -35,10 +35,9 @@ pub async fn purge_comment( Comment::delete(&mut context.pool(), comment_id).await?; // Mod tables - let reason = sanitize_html_api_opt(&data.reason); let form = AdminPurgeCommentForm { admin_person_id: local_user_view.person.id, - reason, + reason: data.reason.clone(), post_id, }; diff --git a/crates/api/src/site/purge/community.rs b/crates/api/src/site/purge/community.rs index 8e731fa29..6ca30125d 100644 --- a/crates/api/src/site/purge/community.rs +++ b/crates/api/src/site/purge/community.rs @@ -3,7 +3,7 @@ use lemmy_api_common::{ context::LemmyContext, request::purge_image_from_pictrs, site::{PurgeCommunity, PurgeItemResponse}, - utils::{is_admin, purge_image_posts_for_community, sanitize_html_api_opt}, + utils::{is_admin, purge_image_posts_for_community}, }; use lemmy_db_schema::{ source::{ @@ -42,10 +42,9 @@ pub async fn purge_community( Community::delete(&mut context.pool(), community_id).await?; // Mod tables - let reason = sanitize_html_api_opt(&data.reason); let form = AdminPurgeCommunityForm { admin_person_id: local_user_view.person.id, - reason, + reason: data.reason.clone(), }; AdminPurgeCommunity::create(&mut context.pool(), &form).await?; diff --git a/crates/api/src/site/purge/person.rs b/crates/api/src/site/purge/person.rs index 1e68db88f..96b8f7859 100644 --- a/crates/api/src/site/purge/person.rs +++ b/crates/api/src/site/purge/person.rs @@ -3,7 +3,7 @@ use lemmy_api_common::{ context::LemmyContext, request::delete_image_from_pictrs, site::{PurgeItemResponse, PurgePerson}, - utils::{is_admin, sanitize_html_api_opt}, + utils::is_admin, }; use lemmy_db_schema::{ source::{ @@ -41,10 +41,9 @@ pub async fn purge_person( Person::delete(&mut context.pool(), person_id).await?; // Mod tables - let reason = sanitize_html_api_opt(&data.reason); let form = AdminPurgePersonForm { admin_person_id: local_user_view.person.id, - reason, + reason: data.reason.clone(), }; AdminPurgePerson::create(&mut context.pool(), &form).await?; diff --git a/crates/api/src/site/purge/post.rs b/crates/api/src/site/purge/post.rs index 5e1ff4ea5..a7469aa90 100644 --- a/crates/api/src/site/purge/post.rs +++ b/crates/api/src/site/purge/post.rs @@ -3,7 +3,7 @@ use lemmy_api_common::{ context::LemmyContext, request::purge_image_from_pictrs, site::{PurgeItemResponse, PurgePost}, - utils::{is_admin, sanitize_html_api_opt}, + utils::is_admin, }; use lemmy_db_schema::{ source::{ @@ -43,10 +43,9 @@ pub async fn purge_post( Post::delete(&mut context.pool(), post_id).await?; // Mod tables - let reason = sanitize_html_api_opt(&data.reason); let form = AdminPurgePostForm { admin_person_id: local_user_view.person.id, - reason, + reason: data.reason.clone(), community_id, }; diff --git a/crates/api_common/src/build_response.rs b/crates/api_common/src/build_response.rs index e0d399764..5083616c1 100644 --- a/crates/api_common/src/build_response.rs +++ b/crates/api_common/src/build_response.rs @@ -20,7 +20,10 @@ use lemmy_db_schema::{ }; use lemmy_db_views::structs::{CommentView, LocalUserView, PostView}; use lemmy_db_views_actor::structs::CommunityView; -use lemmy_utils::{error::LemmyError, utils::mention::MentionData}; +use lemmy_utils::{ + error::LemmyError, + utils::{markdown::markdown_to_html, mention::MentionData}, +}; pub async fn build_comment_response( context: &LemmyContext, @@ -121,10 +124,11 @@ pub async fn send_local_notifs( // Send an email to those local users that have notifications on if do_send_email { let lang = get_interface_language(&mention_user_view); + let content = markdown_to_html(&comment.content); send_email_to_user( &mention_user_view, &lang.notification_mentioned_by_subject(&person.name), - &lang.notification_mentioned_by_body(&comment.content, &inbox_link, &person.name), + &lang.notification_mentioned_by_body(&content, &inbox_link, &person.name), context.settings(), ) .await @@ -164,10 +168,11 @@ pub async fn send_local_notifs( if do_send_email { let lang = get_interface_language(&parent_user_view); + let content = markdown_to_html(&comment.content); send_email_to_user( &parent_user_view, &lang.notification_comment_reply_subject(&person.name), - &lang.notification_comment_reply_body(&comment.content, &inbox_link, &person.name), + &lang.notification_comment_reply_body(&content, &inbox_link, &person.name), context.settings(), ) .await @@ -201,10 +206,11 @@ pub async fn send_local_notifs( if do_send_email { let lang = get_interface_language(&parent_user_view); + let content = markdown_to_html(&comment.content); send_email_to_user( &parent_user_view, &lang.notification_post_reply_subject(&person.name), - &lang.notification_post_reply_body(&comment.content, &inbox_link, &person.name), + &lang.notification_post_reply_body(&content, &inbox_link, &person.name), context.settings(), ) .await diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index 891a35855..4dfad3645 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -723,37 +723,6 @@ pub fn generate_moderators_url(community_id: &DbUrl) -> Result` is left in place because it is interpreted as markdown quote. -pub fn sanitize_html_api(data: &str) -> String { - data - .replace('&', "&") - .replace('<', "<") - .replace('\"', """) - .replace('\'', "'") -} - -pub fn sanitize_html_api_opt(data: &Option) -> Option { - data.as_ref().map(|d| sanitize_html_api(d)) -} - -/// Replace special HTML characters in federation parameters to prevent XSS attacks. -/// -/// Unlike [sanitize_html_api()] it leaves `&` in place to avoid double escaping. -pub fn sanitize_html_federation(data: &str) -> String { - data - .replace('<', "<") - .replace('\"', """) - .replace('\'', "'") -} - -pub fn sanitize_html_federation_opt(data: &Option) -> Option { - data.as_ref().map(|d| sanitize_html_federation(d)) -} - pub fn create_login_cookie(jwt: Sensitive) -> Cookie<'static> { let mut cookie = Cookie::new(AUTH_COOKIE_NAME, jwt.into_inner()); cookie.set_secure(true); diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index c9aa4f774..dcf998bd7 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -12,7 +12,6 @@ use lemmy_api_common::{ generate_local_apub_endpoint, get_post, local_site_to_slur_regex, - sanitize_html_api, EndpointType, }, }; @@ -52,7 +51,6 @@ pub async fn create_comment( &local_site_to_slur_regex(&local_site), ); is_valid_body_field(&Some(content.clone()), false)?; - let content = sanitize_html_api(&content); // Check for a community ban let post_id = data.post_id; diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs index 2d966d0fa..d6c672262 100644 --- a/crates/api_crud/src/comment/update.rs +++ b/crates/api_crud/src/comment/update.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ comment::{CommentResponse, EditComment}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, local_site_to_slur_regex, sanitize_html_api_opt}, + utils::{check_community_ban, local_site_to_slur_regex}, }; use lemmy_db_schema::{ source::{ @@ -63,7 +63,6 @@ pub async fn update_comment( .as_ref() .map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site))); is_valid_body_field(&content, false)?; - let content = sanitize_html_api_opt(&content); let comment_id = data.comment_id; let form = CommentUpdateForm { diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index 633fa81a9..91725c409 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -11,8 +11,6 @@ use lemmy_api_common::{ generate_shared_inbox_url, is_admin, local_site_to_slur_regex, - sanitize_html_api, - sanitize_html_api_opt, EndpointType, }, }; @@ -57,14 +55,10 @@ pub async fn create_community( let icon = diesel_option_overwrite_to_url_create(&data.icon)?; let banner = diesel_option_overwrite_to_url_create(&data.banner)?; - let name = sanitize_html_api(&data.name); - let title = sanitize_html_api(&data.title); - let description = sanitize_html_api_opt(&data.description); - let slur_regex = local_site_to_slur_regex(&local_site); - check_slurs(&name, &slur_regex)?; - check_slurs(&title, &slur_regex)?; - check_slurs_opt(&description, &slur_regex)?; + check_slurs(&data.name, &slur_regex)?; + check_slurs(&data.title, &slur_regex)?; + check_slurs_opt(&data.description, &slur_regex)?; is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?; is_valid_body_field(&data.description, false)?; @@ -85,9 +79,9 @@ pub async fn create_community( let keypair = generate_actor_keypair()?; let community_form = CommunityInsertForm::builder() - .name(name) - .title(title) - .description(description) + .name(data.name.clone()) + .title(data.title.clone()) + .description(data.description.clone()) .icon(icon) .banner(banner) .nsfw(data.nsfw) diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs index 893128d8c..8e5ec77c0 100644 --- a/crates/api_crud/src/community/update.rs +++ b/crates/api_crud/src/community/update.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ community::{CommunityResponse, EditCommunity}, context::LemmyContext, send_activity::{ActivityChannel, SendActivityData}, - utils::{local_site_to_slur_regex, sanitize_html_api_opt}, + utils::local_site_to_slur_regex, }; use lemmy_db_schema::{ newtypes::PersonId, @@ -37,12 +37,9 @@ pub async fn update_community( check_slurs_opt(&data.description, &slur_regex)?; is_valid_body_field(&data.description, false)?; - let title = sanitize_html_api_opt(&data.title); - let description = sanitize_html_api_opt(&data.description); - let icon = diesel_option_overwrite_to_url(&data.icon)?; let banner = diesel_option_overwrite_to_url(&data.banner)?; - let description = diesel_option_overwrite(description); + let description = diesel_option_overwrite(data.description.clone()); // Verify its a mod (only mods can edit it) let community_id = data.community_id; @@ -67,7 +64,7 @@ pub async fn update_community( } let community_form = CommunityUpdateForm { - title, + title: data.title.clone(), description, icon, banner, diff --git a/crates/api_crud/src/custom_emoji/create.rs b/crates/api_crud/src/custom_emoji/create.rs index 7a2ae9b2d..cd30ef1e9 100644 --- a/crates/api_crud/src/custom_emoji/create.rs +++ b/crates/api_crud/src/custom_emoji/create.rs @@ -3,7 +3,7 @@ use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, custom_emoji::{CreateCustomEmoji, CustomEmojiResponse}, - utils::{is_admin, sanitize_html_api}, + utils::is_admin, }; use lemmy_db_schema::source::{ custom_emoji::{CustomEmoji, CustomEmojiInsertForm}, @@ -23,15 +23,11 @@ pub async fn create_custom_emoji( // Make sure user is an admin is_admin(&local_user_view)?; - let shortcode = sanitize_html_api(data.shortcode.to_lowercase().trim()); - let alt_text = sanitize_html_api(&data.alt_text); - let category = sanitize_html_api(&data.category); - let emoji_form = CustomEmojiInsertForm::builder() .local_site_id(local_site.id) - .shortcode(shortcode) - .alt_text(alt_text) - .category(category) + .shortcode(data.shortcode.to_lowercase().trim().to_string()) + .alt_text(data.alt_text.to_string()) + .category(data.category.to_string()) .image_url(data.clone().image_url.into()) .build(); let emoji = CustomEmoji::create(&mut context.pool(), &emoji_form).await?; diff --git a/crates/api_crud/src/custom_emoji/update.rs b/crates/api_crud/src/custom_emoji/update.rs index 5c115d5c1..5a2631a62 100644 --- a/crates/api_crud/src/custom_emoji/update.rs +++ b/crates/api_crud/src/custom_emoji/update.rs @@ -3,7 +3,7 @@ use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, custom_emoji::{CustomEmojiResponse, EditCustomEmoji}, - utils::{is_admin, sanitize_html_api}, + utils::is_admin, }; use lemmy_db_schema::source::{ custom_emoji::{CustomEmoji, CustomEmojiUpdateForm}, @@ -23,13 +23,10 @@ pub async fn update_custom_emoji( // Make sure user is an admin is_admin(&local_user_view)?; - let alt_text = sanitize_html_api(&data.alt_text); - let category = sanitize_html_api(&data.category); - let emoji_form = CustomEmojiUpdateForm::builder() .local_site_id(local_site.id) - .alt_text(alt_text) - .category(category) + .alt_text(data.alt_text.to_string()) + .category(data.category.to_string()) .image_url(data.clone().image_url.into()) .build(); let emoji = CustomEmoji::update(&mut context.pool(), data.id, &emoji_form).await?; diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 4fb97c1c7..33408b774 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -13,8 +13,6 @@ use lemmy_api_common::{ honeypot_check, local_site_to_slur_regex, mark_post_as_read, - sanitize_html_api, - sanitize_html_api_opt, EndpointType, }, }; @@ -92,11 +90,6 @@ pub async fn create_post( .map(|u| (u.title, u.description, u.embed_video_url)) .unwrap_or_default(); - let name = sanitize_html_api(data.name.trim()); - let body = sanitize_html_api_opt(&data.body); - let embed_title = sanitize_html_api_opt(&embed_title); - let embed_description = sanitize_html_api_opt(&embed_description); - // Only need to check if language is allowed in case user set it explicitly. When using default // language, it already only returns allowed languages. CommunityLanguage::is_allowed_community_language( @@ -120,9 +113,9 @@ pub async fn create_post( }; let post_form = PostInsertForm::builder() - .name(name) + .name(data.name.trim().to_string()) .url(url) - .body(body) + .body(data.body.clone()) .community_id(data.community_id) .creator_id(local_user_view.person.id) .nsfw(data.nsfw) diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index 04e6191a8..dfd311f3f 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ post::{EditPost, PostResponse}, request::fetch_site_data, send_activity::{ActivityChannel, SendActivityData}, - utils::{check_community_ban, local_site_to_slur_regex, sanitize_html_api_opt}, + utils::{check_community_ban, local_site_to_slur_regex}, }; use lemmy_db_schema::{ source::{ @@ -75,12 +75,6 @@ pub async fn update_post( .map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url))) .unwrap_or_default(); - let name = sanitize_html_api_opt(&data.name); - let body = sanitize_html_api_opt(&data.body); - let body = diesel_option_overwrite(body); - let embed_title = embed_title.map(|e| sanitize_html_api_opt(&e)); - let embed_description = embed_description.map(|e| sanitize_html_api_opt(&e)); - let language_id = data.language_id; CommunityLanguage::is_allowed_community_language( &mut context.pool(), @@ -90,9 +84,9 @@ pub async fn update_post( .await?; let post_form = PostUpdateForm { - name, + name: data.name.clone(), url, - body, + body: diesel_option_overwrite(data.body.clone()), nsfw: data.nsfw, embed_title, embed_description, diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index c0bdb8ed1..a176cdcb2 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -9,7 +9,6 @@ use lemmy_api_common::{ generate_local_apub_endpoint, get_interface_language, local_site_to_slur_regex, - sanitize_html_api, send_email_to_user, EndpointType, }, @@ -24,7 +23,7 @@ use lemmy_db_schema::{ use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; use lemmy_utils::{ error::{LemmyError, LemmyErrorExt, LemmyErrorType}, - utils::{slurs::remove_slurs, validation::is_valid_body_field}, + utils::{markdown::markdown_to_html, slurs::remove_slurs, validation::is_valid_body_field}, }; #[tracing::instrument(skip(context))] @@ -35,8 +34,7 @@ pub async fn create_private_message( ) -> Result, LemmyError> { let local_site = LocalSite::read(&mut context.pool()).await?; - let content = sanitize_html_api(&data.content); - let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site)); + let content = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site)); is_valid_body_field(&Some(content.clone()), false)?; check_person_block( @@ -83,6 +81,7 @@ pub async fn create_private_message( let lang = get_interface_language(&local_recipient); let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname()); let sender_name = &local_user_view.person.name; + let content = markdown_to_html(&content); send_email_to_user( &local_recipient, &lang.notification_private_message_subject(sender_name), diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs index 7e55c6416..9e3b7c6b3 100644 --- a/crates/api_crud/src/private_message/update.rs +++ b/crates/api_crud/src/private_message/update.rs @@ -4,7 +4,7 @@ use lemmy_api_common::{ context::LemmyContext, private_message::{EditPrivateMessage, PrivateMessageResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{local_site_to_slur_regex, sanitize_html_api}, + utils::local_site_to_slur_regex, }; use lemmy_db_schema::{ source::{ @@ -36,8 +36,7 @@ pub async fn update_private_message( } // Doing the update - let content = sanitize_html_api(&data.content); - let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site)); + let content = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site)); is_valid_body_field(&Some(content.clone()), false)?; let private_message_id = data.private_message_id; diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs index 136a187f2..61dfd7c77 100644 --- a/crates/api_crud/src/site/create.rs +++ b/crates/api_crud/src/site/create.rs @@ -4,13 +4,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ context::LemmyContext, site::{CreateSite, SiteResponse}, - utils::{ - generate_site_inbox_url, - is_admin, - local_site_rate_limit_to_rate_limit_config, - sanitize_html_api, - sanitize_html_api_opt, - }, + utils::{generate_site_inbox_url, is_admin, local_site_rate_limit_to_rate_limit_config}, }; use lemmy_db_schema::{ newtypes::DbUrl, @@ -55,14 +49,11 @@ pub async fn create_site( let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into(); let inbox_url = Some(generate_site_inbox_url(&actor_id)?); let keypair = generate_actor_keypair()?; - let name = sanitize_html_api(&data.name); - let sidebar = sanitize_html_api_opt(&data.sidebar); - let description = sanitize_html_api_opt(&data.description); let site_form = SiteUpdateForm { - name: Some(name), - sidebar: diesel_option_overwrite(sidebar), - description: diesel_option_overwrite(description), + name: Some(data.name.clone()), + sidebar: diesel_option_overwrite(data.sidebar.clone()), + description: diesel_option_overwrite(data.description.clone()), icon: diesel_option_overwrite_to_url(&data.icon)?, banner: diesel_option_overwrite_to_url(&data.banner)?, actor_id: Some(actor_id), @@ -77,10 +68,6 @@ pub async fn create_site( Site::update(&mut context.pool(), site_id, &site_form).await?; - let application_question = sanitize_html_api_opt(&data.application_question); - let default_theme = sanitize_html_api_opt(&data.default_theme); - let legal_information = sanitize_html_api_opt(&data.legal_information); - let local_site_form = LocalSiteUpdateForm { // Set the site setup to true site_setup: Some(true), @@ -89,11 +76,11 @@ pub async fn create_site( enable_nsfw: data.enable_nsfw, community_creation_admin_only: data.community_creation_admin_only, require_email_verification: data.require_email_verification, - application_question: diesel_option_overwrite(application_question), + application_question: diesel_option_overwrite(data.application_question.clone()), private_instance: data.private_instance, - default_theme, + default_theme: data.default_theme.clone(), default_post_listing_type: data.default_post_listing_type, - legal_information: diesel_option_overwrite(legal_information), + legal_information: diesel_option_overwrite(data.legal_information.clone()), application_email_admins: data.application_email_admins, hide_modlog_mod_names: data.hide_modlog_mod_names, updated: Some(Some(naive_now())), diff --git a/crates/api_crud/src/site/update.rs b/crates/api_crud/src/site/update.rs index 792faa785..3afc79559 100644 --- a/crates/api_crud/src/site/update.rs +++ b/crates/api_crud/src/site/update.rs @@ -3,7 +3,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ context::LemmyContext, site::{EditSite, SiteResponse}, - utils::{is_admin, local_site_rate_limit_to_rate_limit_config, sanitize_html_api_opt}, + utils::{is_admin, local_site_rate_limit_to_rate_limit_config}, }; use lemmy_db_schema::{ source::{ @@ -54,14 +54,10 @@ pub async fn update_site( SiteLanguage::update(&mut context.pool(), discussion_languages.clone(), &site).await?; } - let name = sanitize_html_api_opt(&data.name); - let sidebar = sanitize_html_api_opt(&data.sidebar); - let description = sanitize_html_api_opt(&data.description); - let site_form = SiteUpdateForm { - name, - sidebar: diesel_option_overwrite(sidebar), - description: diesel_option_overwrite(description), + name: data.name.clone(), + sidebar: diesel_option_overwrite(data.sidebar.clone()), + description: diesel_option_overwrite(data.description.clone()), icon: diesel_option_overwrite_to_url(&data.icon)?, banner: diesel_option_overwrite_to_url(&data.banner)?, updated: Some(Some(naive_now())), @@ -74,21 +70,17 @@ pub async fn update_site( // Diesel will throw an error for empty update forms .ok(); - let application_question = sanitize_html_api_opt(&data.application_question); - let default_theme = sanitize_html_api_opt(&data.default_theme); - let legal_information = sanitize_html_api_opt(&data.legal_information); - let local_site_form = LocalSiteUpdateForm { enable_downvotes: data.enable_downvotes, registration_mode: data.registration_mode, enable_nsfw: data.enable_nsfw, community_creation_admin_only: data.community_creation_admin_only, require_email_verification: data.require_email_verification, - application_question: diesel_option_overwrite(application_question), + application_question: diesel_option_overwrite(data.application_question.clone()), private_instance: data.private_instance, - default_theme, + default_theme: data.default_theme.clone(), default_post_listing_type: data.default_post_listing_type, - legal_information: diesel_option_overwrite(legal_information), + legal_information: diesel_option_overwrite(data.legal_information.clone()), application_email_admins: data.application_email_admins, hide_modlog_mod_names: data.hide_modlog_mod_names, updated: Some(Some(naive_now())), diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index e9ac6d5cb..4a326a3ac 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -12,8 +12,6 @@ use lemmy_api_common::{ honeypot_check, local_site_to_slur_regex, password_length_check, - sanitize_html_api, - sanitize_html_api_opt, send_new_applicant_email_to_admins, send_verification_email, EndpointType, @@ -93,12 +91,6 @@ pub async fn register( check_slurs(&data.username, &slur_regex)?; check_slurs_opt(&data.answer, &slur_regex)?; - if sanitize_html_api(&data.username) != data.username { - Err(LemmyErrorType::InvalidName)?; - } - - let answer = sanitize_html_api_opt(&data.answer); - let actor_keypair = generate_actor_keypair()?; is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize)?; let actor_id = generate_local_apub_endpoint( @@ -154,7 +146,7 @@ pub async fn register( let form = RegistrationApplicationInsertForm { local_user_id: inserted_local_user.id, // We already made sure answer was not null above - answer: answer.expect("must have an answer"), + answer: data.answer.clone().expect("must have an answer"), }; RegistrationApplication::create(&mut context.pool(), &form).await?; diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs index 2d1f760c2..4469be53e 100644 --- a/crates/apub/src/activities/block/block_user.rs +++ b/crates/apub/src/activities/block/block_user.rs @@ -23,7 +23,7 @@ use anyhow::anyhow; use chrono::{DateTime, Utc}; use lemmy_api_common::{ context::LemmyContext, - utils::{remove_user_data, remove_user_data_in_community, sanitize_html_federation_opt}, + utils::{remove_user_data, remove_user_data_in_community}, }; use lemmy_db_schema::{ source::{ @@ -173,7 +173,7 @@ impl ActivityHandler for BlockUser { let form = ModBanForm { mod_person_id: mod_person.id, other_person_id: blocked_person.id, - reason: sanitize_html_federation_opt(&self.summary), + reason: self.summary, banned: Some(true), expires, }; @@ -207,7 +207,7 @@ impl ActivityHandler for BlockUser { mod_person_id: mod_person.id, other_person_id: blocked_person.id, community_id: community.id, - reason: sanitize_html_federation_opt(&self.summary), + reason: self.summary, banned: Some(true), expires, }; diff --git a/crates/apub/src/activities/block/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs index 0796f86e7..97e2bc336 100644 --- a/crates/apub/src/activities/block/undo_block_user.rs +++ b/crates/apub/src/activities/block/undo_block_user.rs @@ -17,7 +17,7 @@ use activitypub_federation::{ protocol::verification::verify_domains_match, traits::{ActivityHandler, Actor}, }; -use lemmy_api_common::{context::LemmyContext, utils::sanitize_html_federation_opt}; +use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ source::{ activity::ActivitySendTargets, @@ -118,7 +118,7 @@ impl ActivityHandler for UndoBlockUser { let form = ModBanForm { mod_person_id: mod_person.id, other_person_id: blocked_person.id, - reason: sanitize_html_federation_opt(&self.object.summary), + reason: self.object.summary, banned: Some(false), expires, }; @@ -137,7 +137,7 @@ impl ActivityHandler for UndoBlockUser { mod_person_id: mod_person.id, other_person_id: blocked_person.id, community_id: community.id, - reason: sanitize_html_federation_opt(&self.object.summary), + reason: self.object.summary, banned: Some(false), expires, }; diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs index d6c058448..7da5ac8ae 100644 --- a/crates/apub/src/activities/community/report.rs +++ b/crates/apub/src/activities/community/report.rs @@ -11,7 +11,7 @@ use activitypub_federation::{ kinds::activity::FlagType, traits::{ActivityHandler, Actor}, }; -use lemmy_api_common::{context::LemmyContext, utils::sanitize_html_federation}; +use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ source::{ activity::ActivitySendTargets, @@ -90,7 +90,7 @@ impl ActivityHandler for Report { post_id: post.id, original_post_name: post.name.clone(), original_post_url: post.url.clone(), - reason: sanitize_html_federation(&self.summary), + reason: self.summary.clone(), original_post_body: post.body.clone(), }; PostReport::report(&mut context.pool(), &report_form).await?; @@ -100,7 +100,7 @@ impl ActivityHandler for Report { creator_id: actor.id, comment_id: comment.id, original_comment_text: comment.content.clone(), - reason: sanitize_html_federation(&self.summary), + reason: self.summary.clone(), }; CommentReport::report(&mut context.pool(), &report_form).await?; } diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 88bf2b523..28c4eace7 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -8,7 +8,7 @@ use crate::{ protocol::{activities::deletion::delete::Delete, IdOrNestedObject}, }; use activitypub_federation::{config::Data, kinds::activity::DeleteType, traits::ActivityHandler}; -use lemmy_api_common::{context::LemmyContext, utils::sanitize_html_federation_opt}; +use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ source::{ comment::{Comment, CommentUpdateForm}, @@ -105,8 +105,6 @@ pub(in crate::activities) async fn receive_remove_action( reason: Option, context: &Data, ) -> Result<(), LemmyError> { - let reason = sanitize_html_federation_opt(&reason); - match DeletableObjects::read_from_db(object, context).await? { DeletableObjects::Community(community) => { if community.local { diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index 599dd7387..4d57b50ee 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -16,10 +16,7 @@ use activitypub_federation::{ traits::Object, }; use chrono::{DateTime, Utc}; -use lemmy_api_common::{ - context::LemmyContext, - utils::{local_site_opt_to_slur_regex, sanitize_html_federation}, -}; +use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex}; use lemmy_db_schema::{ source::{ comment::{Comment, CommentInsertForm, CommentUpdateForm}, @@ -162,7 +159,6 @@ impl Object for ApubComment { let local_site = LocalSite::read(&mut context.pool()).await.ok(); let slur_regex = &local_site_opt_to_slur_regex(&local_site); let content = remove_slurs(&content, slur_regex); - let content = sanitize_html_federation(&content); let language_id = LanguageTag::to_language_id_single(note.language, &mut context.pool()).await?; diff --git a/crates/apub/src/objects/instance.rs b/crates/apub/src/objects/instance.rs index 5949e12bf..d6086fdc2 100644 --- a/crates/apub/src/objects/instance.rs +++ b/crates/apub/src/objects/instance.rs @@ -17,10 +17,7 @@ use activitypub_federation::{ traits::{Actor, Object}, }; use chrono::{DateTime, Utc}; -use lemmy_api_common::{ - context::LemmyContext, - utils::{local_site_opt_to_slur_regex, sanitize_html_federation_opt}, -}; +use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex}; use lemmy_db_schema::{ newtypes::InstanceId, source::{ @@ -135,8 +132,6 @@ impl Object for ApubSite { let instance = DbInstance::read_or_create(&mut data.pool(), domain.to_string()).await?; let sidebar = read_from_string_or_source_opt(&apub.content, &None, &apub.source); - let sidebar = sanitize_html_federation_opt(&sidebar); - let description = sanitize_html_federation_opt(&apub.summary); let site_form = SiteInsertForm { name: apub.name.clone(), @@ -144,7 +139,7 @@ impl Object for ApubSite { updated: apub.updated, icon: apub.icon.clone().map(|i| i.url.into()), banner: apub.image.clone().map(|i| i.url.into()), - description, + description: apub.summary, actor_id: Some(apub.id.clone().into()), last_refreshed_at: Some(naive_now()), inbox_url: Some(apub.inbox.clone().into()), diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index 34d824890..3ca473616 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -20,12 +20,7 @@ use activitypub_federation::{ use chrono::{DateTime, Utc}; use lemmy_api_common::{ context::LemmyContext, - utils::{ - generate_outbox_url, - local_site_opt_to_slur_regex, - sanitize_html_federation, - sanitize_html_federation_opt, - }, + utils::{generate_outbox_url, local_site_opt_to_slur_regex}, }; use lemmy_db_schema::{ source::{ @@ -150,17 +145,14 @@ impl Object for ApubPerson { ) -> Result { let instance_id = fetch_instance_actor_for_object(&person.id, context).await?; - let name = sanitize_html_federation(&person.preferred_username); - let display_name = sanitize_html_federation_opt(&person.name); let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source); - let bio = sanitize_html_federation_opt(&bio); // Some Mastodon users have `name: ""` (empty string), need to convert that to `None` // https://github.com/mastodon/mastodon/issues/25233 - let display_name = display_name.filter(|n| !n.is_empty()); + let display_name = person.name.filter(|n| !n.is_empty()); let person_form = PersonInsertForm { - name, + name: person.preferred_username, display_name, banned: None, ban_expires: None, @@ -275,7 +267,7 @@ pub(crate) mod tests { assert_eq!(person.name, "lanodan"); assert!(!person.local); assert_eq!(context.request_count(), 0); - assert_eq!(person.bio.as_ref().unwrap().len(), 878); + assert_eq!(person.bio.as_ref().unwrap().len(), 873); cleanup((person, site), &context).await; } diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 2fc859ddf..c25f988dc 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -25,13 +25,7 @@ use html2md::parse_html; use lemmy_api_common::{ context::LemmyContext, request::fetch_site_data, - utils::{ - is_mod_or_admin, - local_site_opt_to_sensitive, - local_site_opt_to_slur_regex, - sanitize_html_federation, - sanitize_html_federation_opt, - }, + utils::{is_mod_or_admin, local_site_opt_to_sensitive, local_site_opt_to_slur_regex}, }; use lemmy_db_schema::{ self, @@ -231,17 +225,11 @@ impl Object for ApubPost { .unwrap_or_default(); let slur_regex = &local_site_opt_to_slur_regex(&local_site); - let body_slurs_removed = - read_from_string_or_source_opt(&page.content, &page.media_type, &page.source) - .map(|s| remove_slurs(&s, slur_regex)); + let body = read_from_string_or_source_opt(&page.content, &page.media_type, &page.source) + .map(|s| remove_slurs(&s, slur_regex)); let language_id = LanguageTag::to_language_id_single(page.language, &mut context.pool()).await?; - let name = sanitize_html_federation(&name); - let body = sanitize_html_federation_opt(&body_slurs_removed); - let embed_title = sanitize_html_federation_opt(&embed_title); - let embed_description = sanitize_html_federation_opt(&embed_description); - PostInsertForm { name, url: url.map(Into::into), diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 97bf595d5..f683a989f 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -12,10 +12,7 @@ use activitypub_federation::{ traits::Object, }; use chrono::{DateTime, Utc}; -use lemmy_api_common::{ - context::LemmyContext, - utils::{check_person_block, sanitize_html_federation}, -}; +use lemmy_api_common::{context::LemmyContext, utils::check_person_block}; use lemmy_db_schema::{ source::{ person::Person, @@ -125,7 +122,6 @@ impl Object for ApubPrivateMessage { check_person_block(creator.id, recipient.id, &mut context.pool()).await?; let content = read_from_string_or_source(¬e.content, &None, ¬e.source); - let content = sanitize_html_federation(&content); let form = PrivateMessageInsertForm { creator_id: creator.id, diff --git a/crates/apub/src/protocol/objects/group.rs b/crates/apub/src/protocol/objects/group.rs index 9d0229785..ab14ef6ea 100644 --- a/crates/apub/src/protocol/objects/group.rs +++ b/crates/apub/src/protocol/objects/group.rs @@ -23,10 +23,7 @@ use activitypub_federation::{ }, }; use chrono::{DateTime, Utc}; -use lemmy_api_common::{ - context::LemmyContext, - utils::{local_site_opt_to_slur_regex, sanitize_html_federation, sanitize_html_federation_opt}, -}; +use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex}; use lemmy_db_schema::{ newtypes::InstanceId, source::community::{CommunityInsertForm, CommunityUpdateForm}, @@ -97,14 +94,11 @@ impl Group { } pub(crate) fn into_insert_form(self, instance_id: InstanceId) -> CommunityInsertForm { - let name = sanitize_html_federation(&self.preferred_username); - let title = sanitize_html_federation(&self.name.unwrap_or(self.preferred_username)); let description = read_from_string_or_source_opt(&self.summary, &None, &self.source); - let description = sanitize_html_federation_opt(&description); CommunityInsertForm { - name, - title, + name: self.preferred_username.clone(), + title: self.name.unwrap_or(self.preferred_username.clone()), description, removed: None, published: self.published, diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs index 446db86ea..3ef760a51 100644 --- a/crates/routes/src/feeds.rs +++ b/crates/routes/src/feeds.rs @@ -22,7 +22,7 @@ use lemmy_db_views_actor::{ use lemmy_utils::{ cache_header::cache_1hour, error::LemmyError, - utils::markdown::markdown_to_html, + utils::markdown::{markdown_to_html, sanitize_html}, }; use once_cell::sync::Lazy; use rss::{ @@ -289,7 +289,7 @@ async fn get_feed_community( .items(items); if let Some(community_desc) = community.description { - channel_builder.description(&community_desc); + channel_builder.description(markdown_to_html(&community_desc)); } Ok(channel_builder) @@ -328,7 +328,7 @@ async fn get_feed_front( .items(items); if let Some(site_desc) = site_view.site.description { - channel_builder.description(&site_desc); + channel_builder.description(markdown_to_html(&site_desc)); } Ok(channel_builder) @@ -457,7 +457,7 @@ fn create_post_items( let mut i = ItemBuilder::default(); let mut dc_extension = DublinCoreExtensionBuilder::default(); - i.title(p.post.name); + i.title(sanitize_html(&p.post.name)); dc_extension.creators(vec![p.creator.actor_id.to_string()]); @@ -472,14 +472,18 @@ fn create_post_items( .build(); i.guid(guid); - let community_url = format!("{}/c/{}", protocol_and_hostname, p.community.name); + let community_url = format!( + "{}/c/{}", + protocol_and_hostname, + sanitize_html(&p.community.name) + ); // TODO add images let mut description = format!("submitted by {} to {}
{} points | {} comments", p.creator.actor_id, - p.creator.name, + sanitize_html(&p.creator.name), community_url, - p.community.name, + sanitize_html(&p.community.name), p.counts.score, post_url, p.counts.comments); diff --git a/crates/utils/src/utils/markdown.rs b/crates/utils/src/utils/markdown.rs index 5f851589b..a051310ca 100644 --- a/crates/utils/src/utils/markdown.rs +++ b/crates/utils/src/utils/markdown.rs @@ -12,6 +12,20 @@ static MARKDOWN_PARSER: Lazy = Lazy::new(|| { parser }); +/// Replace special HTML characters in API parameters to prevent XSS attacks. +/// +/// Taken from https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.md#output-encoding-for-html-contexts +/// +/// `>` is left in place because it is interpreted as markdown quote. +pub fn sanitize_html(text: &str) -> String { + text + .replace('&', "&") + .replace('<', "<") + .replace('\"', """) + .replace('\'', "'") +} + +/// Converts text from markdown to HTML, while escaping special characters. pub fn markdown_to_html(text: &str) -> String { MARKDOWN_PARSER.parse(text).xrender() } @@ -21,7 +35,7 @@ mod tests { #![allow(clippy::unwrap_used)] #![allow(clippy::indexing_slicing)] - use crate::utils::markdown::markdown_to_html; + use super::*; #[test] fn test_basic_markdown() { @@ -71,6 +85,11 @@ mod tests { "::: spoiler click to see more\nhow spicy!\n:::\n", "
click to see more

how spicy!\n

\n" ), + ( + "escape html special chars", + " hello &\"", + "

<script>alert(‘xss’);</script> hello &"

\n" + ) ]; tests.iter().for_each(|&(msg, input, expected)| { @@ -83,4 +102,11 @@ mod tests { ); }); } + + #[test] + fn test_sanitize_html() { + let sanitized = sanitize_html(" hello &\"'"); + let expected = "<script>alert('xss');</script> hello &"'"; + assert_eq!(expected, sanitized) + } } diff --git a/crates/utils/translations b/crates/utils/translations index 18da10858..e943f97fe 160000 --- a/crates/utils/translations +++ b/crates/utils/translations @@ -1 +1 @@ -Subproject commit 18da10858d8c63750beb06247947f25d91944741 +Subproject commit e943f97fe481dc425acdebc8872bf1fdcabaf875