"alloc-no-stdlib",
]
+[[package]]
+name = "ammonia"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64e6d1c7838db705c9b756557ee27c384ce695a1c51a6fe528784cb1c6840170"
+dependencies = [
+ "html5ever 0.26.0",
+ "maplit",
+ "once_cell",
+ "tendril",
+ "url",
+]
+
[[package]]
name = "android-tzdata"
version = "0.1.1"
dependencies = [
"activitypub_federation",
"actix-web",
+ "ammonia",
"anyhow",
"chrono",
"encoding",
"libc",
]
+[[package]]
+name = "maplit"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+
[[package]]
name = "markdown-it"
version = "0.5.1"
resolveCommunity,
} from "./shared";
import { PostView } from "lemmy-js-client/dist/types/PostView";
+import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
let betaCommunity: CommunityView | undefined;
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 = "<script>alert('xss');</script> hello";
+ let form: CreatePost = {
+ name,
+ body,
+ auth: beta.auth,
+ community_id: betaCommunity.community.id,
+ };
+ let post = await beta.client.createPost(form);
+ expect(post.post_view.post.body).toBe(" hello");
+});
use lemmy_api_common::{
comment::{CommentReportResponse, CreateCommentReport},
context::LemmyContext,
- utils::{check_community_ban, local_user_view_from_jwt, send_new_report_email_to_admins},
+ utils::{
+ check_community_ban,
+ local_user_view_from_jwt,
+ sanitize_html,
+ send_new_report_email_to_admins,
+ },
};
use lemmy_db_schema::{
source::{
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
- let reason = self.reason.trim();
- check_report_reason(reason, &local_site)?;
+ let reason = sanitize_html(self.reason.trim());
+ check_report_reason(&reason, &local_site)?;
let person_id = local_user_view.person.id;
let comment_id = data.comment_id;
creator_id: person_id,
comment_id,
original_comment_text: comment_view.comment.content,
- reason: reason.to_owned(),
+ reason,
};
let report = CommentReport::report(&mut context.pool(), &report_form)
use lemmy_api_common::{
community::{BanFromCommunity, BanFromCommunityResponse},
context::LemmyContext,
- utils::{is_mod_or_admin, local_user_view_from_jwt, remove_user_data_in_community},
+ utils::{
+ is_mod_or_admin,
+ local_user_view_from_jwt,
+ remove_user_data_in_community,
+ sanitize_html_opt,
+ },
};
use lemmy_db_schema::{
source::{
mod_person_id: local_user_view.person.id,
other_person_id: data.person_id,
community_id: data.community_id,
- reason: data.reason.clone(),
+ reason: sanitize_html_opt(&data.reason),
banned: Some(data.ban),
expires,
};
build_response::build_community_response,
community::{CommunityResponse, HideCommunity},
context::LemmyContext,
- utils::{is_admin, local_user_view_from_jwt},
+ utils::{is_admin, local_user_view_from_jwt, sanitize_html_opt},
};
use lemmy_db_schema::{
source::{
let mod_hide_community_form = ModHideCommunityForm {
community_id: data.community_id,
mod_person_id: local_user_view.person.id,
- reason: data.reason.clone(),
+ reason: sanitize_html_opt(&data.reason),
hidden: Some(data.hidden),
};
Ok(base64.encode(output_buffer.into_inner()))
}
-/// Check size of report and remove whitespace
+/// Check size of report
pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Result<(), LemmyError> {
let slur_regex = &local_site_to_slur_regex(local_site);
use lemmy_api_common::{
context::LemmyContext,
person::{BanPerson, BanPersonResponse},
- utils::{is_admin, local_user_view_from_jwt, remove_user_data},
+ utils::{is_admin, local_user_view_from_jwt, remove_user_data, sanitize_html_opt},
};
use lemmy_db_schema::{
source::{
let form = ModBanForm {
mod_person_id: local_user_view.person.id,
other_person_id: data.person_id,
- reason: data.reason.clone(),
+ reason: sanitize_html_opt(&data.reason),
banned: Some(data.ban),
expires,
};
use lemmy_api_common::{
context::LemmyContext,
person::{LoginResponse, SaveUserSettings},
- utils::{local_user_view_from_jwt, send_verification_email},
+ utils::{local_user_view_from_jwt, sanitize_html_opt, send_verification_email},
};
use lemmy_db_schema::{
source::{
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let site_view = SiteView::read_local(&mut context.pool()).await?;
+ let bio = sanitize_html_opt(&data.bio);
+ let display_name = sanitize_html_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(&data.bio);
- let display_name = diesel_option_overwrite(&data.display_name);
- let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
+ let bio = diesel_option_overwrite(bio);
+ let display_name = diesel_option_overwrite(display_name);
+ 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);
+ let email = diesel_option_overwrite(email_deref.clone());
if let Some(Some(email)) = &email {
let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
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_opt(&data.theme);
let person_form = PersonUpdateForm::builder()
.display_name(display_name)
.show_scores(data.show_scores)
.default_sort_type(default_sort_type)
.default_listing_type(default_listing_type)
- .theme(data.theme.clone())
+ .theme(theme)
.interface_language(data.interface_language.clone())
.totp_2fa_secret(totp_2fa_secret)
.totp_2fa_url(totp_2fa_url)
use lemmy_api_common::{
context::LemmyContext,
post::{CreatePostReport, PostReportResponse},
- utils::{check_community_ban, local_user_view_from_jwt, send_new_report_email_to_admins},
+ utils::{
+ check_community_ban,
+ local_user_view_from_jwt,
+ sanitize_html,
+ send_new_report_email_to_admins,
+ },
};
use lemmy_db_schema::{
source::{
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
- let reason = self.reason.trim();
- check_report_reason(reason, &local_site)?;
+ let reason = sanitize_html(self.reason.trim());
+ check_report_reason(&reason, &local_site)?;
let person_id = local_user_view.person.id;
let post_id = data.post_id;
original_post_name: post_view.post.name,
original_post_url: post_view.post.url,
original_post_body: post_view.post.body,
- reason: reason.to_owned(),
+ reason,
};
let report = PostReport::report(&mut context.pool(), &report_form)
use lemmy_api_common::{
context::LemmyContext,
private_message::{CreatePrivateMessageReport, PrivateMessageReportResponse},
- utils::{local_user_view_from_jwt, send_new_report_email_to_admins},
+ utils::{local_user_view_from_jwt, sanitize_html, send_new_report_email_to_admins},
};
use lemmy_db_schema::{
source::{
let local_user_view = local_user_view_from_jwt(&self.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
- let reason = self.reason.trim();
- check_report_reason(reason, &local_site)?;
+ let reason = sanitize_html(self.reason.trim());
+ check_report_reason(&reason, &local_site)?;
let person_id = local_user_view.person.id;
let private_message_id = self.private_message_id;
creator_id: person_id,
private_message_id,
original_pm_text: private_message.content,
- reason: reason.to_owned(),
+ reason: reason.clone(),
};
let report = PrivateMessageReport::report(&mut context.pool(), &report_form)
use lemmy_api_common::{
context::LemmyContext,
site::{PurgeComment, PurgeItemResponse},
- utils::{is_admin, local_user_view_from_jwt},
+ utils::{is_admin, local_user_view_from_jwt, sanitize_html_opt},
};
use lemmy_db_schema::{
source::{
Comment::delete(&mut context.pool(), comment_id).await?;
// Mod tables
- let reason = data.reason.clone();
+ let reason = sanitize_html_opt(&data.reason);
let form = AdminPurgeCommentForm {
admin_person_id: local_user_view.person.id,
reason,
context::LemmyContext,
request::purge_image_from_pictrs,
site::{PurgeCommunity, PurgeItemResponse},
- utils::{is_admin, local_user_view_from_jwt, purge_image_posts_for_community},
+ utils::{is_admin, local_user_view_from_jwt, purge_image_posts_for_community, sanitize_html_opt},
};
use lemmy_db_schema::{
source::{
Community::delete(&mut context.pool(), community_id).await?;
// Mod tables
- let reason = data.reason.clone();
+ let reason = sanitize_html_opt(&data.reason);
let form = AdminPurgeCommunityForm {
admin_person_id: local_user_view.person.id,
reason,
context::LemmyContext,
request::purge_image_from_pictrs,
site::{PurgeItemResponse, PurgePerson},
- utils::{is_admin, local_user_view_from_jwt, purge_image_posts_for_person},
+ utils::{is_admin, local_user_view_from_jwt, purge_image_posts_for_person, sanitize_html_opt},
};
use lemmy_db_schema::{
source::{
Person::delete(&mut context.pool(), person_id).await?;
// Mod tables
- let reason = data.reason.clone();
+ let reason = sanitize_html_opt(&data.reason);
let form = AdminPurgePersonForm {
admin_person_id: local_user_view.person.id,
reason,
context::LemmyContext,
request::purge_image_from_pictrs,
site::{PurgeItemResponse, PurgePost},
- utils::{is_admin, local_user_view_from_jwt},
+ utils::{is_admin, local_user_view_from_jwt, sanitize_html_opt},
};
use lemmy_db_schema::{
source::{
Post::delete(&mut context.pool(), post_id).await?;
// Mod tables
- let reason = data.reason.clone();
+ let reason = sanitize_html_opt(&data.reason);
let form = AdminPurgePostForm {
admin_person_id: local_user_view.person.id,
reason,
is_admin(&local_user_view)?;
// Update the registration with reason, admin_id
- let deny_reason = diesel_option_overwrite(&data.deny_reason);
+ let deny_reason = diesel_option_overwrite(data.deny_reason.clone());
let app_form = RegistrationApplicationUpdateForm {
admin_id: Some(Some(local_user_view.person.id)),
deny_reason,
"actix-web",
"futures",
"once_cell",
+ "ammonia",
]
[dependencies]
actix-web = { workspace = true, optional = true }
# necessary for wasmt compilation
getrandom = { version = "0.2.10", features = ["js"] }
+ammonia = { version = "3.3.0", optional = true }
Ok(())
}
-#[cfg(test)]
-mod tests {
- #![allow(clippy::unwrap_used)]
- #![allow(clippy::indexing_slicing)]
-
- use crate::utils::{honeypot_check, password_length_check};
-
- #[test]
- #[rustfmt::skip]
- fn password_length() {
- assert!(password_length_check("Õ¼¾°3yË,o¸ãtÌÈú|ÇÁÙAøüÒI©·¤(T]/ð>æºWæ[C¤bªWöaÃÎñ·{=û³&§½K/c").is_ok());
- assert!(password_length_check("1234567890").is_ok());
- assert!(password_length_check("short").is_err());
- assert!(password_length_check("looooooooooooooooooooooooooooooooooooooooooooooooooooooooooong").is_err());
- }
-
- #[test]
- fn honeypot() {
- assert!(honeypot_check(&None).is_ok());
- assert!(honeypot_check(&Some(String::new())).is_ok());
- assert!(honeypot_check(&Some("1".to_string())).is_err());
- assert!(honeypot_check(&Some("message".to_string())).is_err());
- }
-}
-
pub enum EndpointType {
Community,
Person,
pub fn generate_moderators_url(community_id: &DbUrl) -> Result<DbUrl, LemmyError> {
Ok(Url::parse(&format!("{community_id}/moderators"))?.into())
}
+
+/// Sanitize HTML with default options. Additionally, dont allow bypassing markdown
+/// links and images
+pub fn sanitize_html(data: &str) -> String {
+ ammonia::Builder::default()
+ .rm_tags(&["a", "img"])
+ .clean(data)
+ .to_string()
+}
+
+pub fn sanitize_html_opt(data: &Option<String>) -> Option<String> {
+ data.as_ref().map(|d| sanitize_html(d))
+}
+
+#[cfg(test)]
+mod tests {
+ #![allow(clippy::unwrap_used)]
+ #![allow(clippy::indexing_slicing)]
+
+ use crate::utils::{honeypot_check, password_length_check, sanitize_html};
+
+ #[test]
+ #[rustfmt::skip]
+ fn password_length() {
+ assert!(password_length_check("Õ¼¾°3yË,o¸ãtÌÈú|ÇÁÙAøüÒI©·¤(T]/ð>æºWæ[C¤bªWöaÃÎñ·{=û³&§½K/c").is_ok());
+ assert!(password_length_check("1234567890").is_ok());
+ assert!(password_length_check("short").is_err());
+ assert!(password_length_check("looooooooooooooooooooooooooooooooooooooooooooooooooooooooooong").is_err());
+ }
+
+ #[test]
+ fn honeypot() {
+ assert!(honeypot_check(&None).is_ok());
+ assert!(honeypot_check(&Some(String::new())).is_ok());
+ assert!(honeypot_check(&Some("1".to_string())).is_err());
+ assert!(honeypot_check(&Some("message".to_string())).is_err());
+ }
+
+ #[test]
+ fn test_sanitize_html() {
+ let sanitized = sanitize_html("<script>alert(1);</script> hello");
+ assert_eq!(sanitized, " hello");
+ let sanitized = sanitize_html("<img src='http://example.com'> test");
+ assert_eq!(sanitized, " test");
+ }
+}
get_post,
local_site_to_slur_regex,
local_user_view_from_jwt,
+ sanitize_html,
EndpointType,
},
};
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
- let content_slurs_removed = remove_slurs(
+ let content = remove_slurs(
&data.content.clone(),
&local_site_to_slur_regex(&local_site),
);
- is_valid_body_field(&Some(content_slurs_removed.clone()), false)?;
+ is_valid_body_field(&Some(content.clone()), false)?;
+ let content = sanitize_html(&content);
// Check for a community ban
let post_id = data.post_id;
};
let comment_form = CommentInsertForm::builder()
- .content(content_slurs_removed.clone())
+ .content(content.clone())
.post_id(data.post_id)
.creator_id(local_user_view.person.id)
.language_id(language_id)
.with_lemmy_type(LemmyErrorType::CouldntCreateComment)?;
// Scan the comment for user mentions, add those rows
- let mentions = scrape_text_for_mentions(&content_slurs_removed);
+ let mentions = scrape_text_for_mentions(&content);
let recipient_ids = send_local_notifs(
mentions,
&updated_comment,
build_response::{build_comment_response, send_local_notifs},
comment::{CommentResponse, EditComment},
context::LemmyContext,
- utils::{check_community_ban, local_site_to_slur_regex, local_user_view_from_jwt},
+ utils::{
+ check_community_ban,
+ local_site_to_slur_regex,
+ local_user_view_from_jwt,
+ sanitize_html_opt,
+ },
};
use lemmy_db_schema::{
source::{
.await?;
// Update the Content
- let content_slurs_removed = data
+ let content = data
.content
.as_ref()
.map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
-
- is_valid_body_field(&content_slurs_removed, false)?;
+ is_valid_body_field(&content, false)?;
+ let content = sanitize_html_opt(&content);
let comment_id = data.comment_id;
let form = CommentUpdateForm::builder()
- .content(content_slurs_removed)
+ .content(content)
.language_id(data.language_id)
.updated(Some(Some(naive_now())))
.build();
is_admin,
local_site_to_slur_regex,
local_user_view_from_jwt,
+ sanitize_html,
+ sanitize_html_opt,
EndpointType,
},
};
let icon = diesel_option_overwrite_to_url_create(&data.icon)?;
let banner = diesel_option_overwrite_to_url_create(&data.banner)?;
+ let name = sanitize_html(&data.name);
+ let title = sanitize_html(&data.title);
+ let description = sanitize_html_opt(&data.description);
+
let slur_regex = local_site_to_slur_regex(&local_site);
- check_slurs(&data.name, &slur_regex)?;
- check_slurs(&data.title, &slur_regex)?;
- check_slurs_opt(&data.description, &slur_regex)?;
+ check_slurs(&name, &slur_regex)?;
+ check_slurs(&title, &slur_regex)?;
+ check_slurs_opt(&description, &slur_regex)?;
is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?;
is_valid_body_field(&data.description, false)?;
let keypair = generate_actor_keypair()?;
let community_form = CommunityInsertForm::builder()
- .name(data.name.clone())
- .title(data.title.clone())
- .description(data.description.clone())
+ .name(name)
+ .title(title)
+ .description(description)
.icon(icon)
.banner(banner)
.nsfw(data.nsfw)
build_response::build_community_response,
community::{CommunityResponse, EditCommunity},
context::LemmyContext,
- utils::{local_site_to_slur_regex, local_user_view_from_jwt},
+ utils::{local_site_to_slur_regex, local_user_view_from_jwt, sanitize_html_opt},
};
use lemmy_db_schema::{
newtypes::PersonId,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
- let icon = diesel_option_overwrite_to_url(&data.icon)?;
- let banner = diesel_option_overwrite_to_url(&data.banner)?;
- let description = diesel_option_overwrite(&data.description);
-
let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs_opt(&data.title, &slur_regex)?;
check_slurs_opt(&data.description, &slur_regex)?;
is_valid_body_field(&data.description, false)?;
+ let title = sanitize_html_opt(&data.title);
+ let description = sanitize_html_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);
+
// Verify its a mod (only mods can edit it)
let community_id = data.community_id;
let mods: Vec<PersonId> =
}
let community_form = CommunityUpdateForm::builder()
- .title(data.title.clone())
+ .title(title)
.description(description)
.icon(icon)
.banner(banner)
use lemmy_api_common::{
context::LemmyContext,
custom_emoji::{CreateCustomEmoji, CustomEmojiResponse},
- utils::{is_admin, local_user_view_from_jwt},
+ utils::{is_admin, local_user_view_from_jwt, sanitize_html},
};
use lemmy_db_schema::source::{
custom_emoji::{CustomEmoji, CustomEmojiInsertForm},
// Make sure user is an admin
is_admin(&local_user_view)?;
+ let shortcode = sanitize_html(data.shortcode.to_lowercase().trim());
+ let alt_text = sanitize_html(&data.alt_text);
+ let category = sanitize_html(&data.category);
+
let emoji_form = CustomEmojiInsertForm::builder()
.local_site_id(local_site.id)
- .shortcode(data.shortcode.to_lowercase().trim().to_string())
- .alt_text(data.alt_text.to_string())
- .category(data.category.to_string())
+ .shortcode(shortcode)
+ .alt_text(alt_text)
+ .category(category)
.image_url(data.clone().image_url.into())
.build();
let emoji = CustomEmoji::create(&mut context.pool(), &emoji_form).await?;
use lemmy_api_common::{
context::LemmyContext,
custom_emoji::{CustomEmojiResponse, EditCustomEmoji},
- utils::{is_admin, local_user_view_from_jwt},
+ utils::{is_admin, local_user_view_from_jwt, sanitize_html},
};
use lemmy_db_schema::source::{
custom_emoji::{CustomEmoji, CustomEmojiUpdateForm},
// Make sure user is an admin
is_admin(&local_user_view)?;
+ let alt_text = sanitize_html(&data.alt_text);
+ let category = sanitize_html(&data.category);
+
let emoji_form = CustomEmojiUpdateForm::builder()
.local_site_id(local_site.id)
- .alt_text(data.alt_text.to_string())
- .category(data.category.to_string())
+ .alt_text(alt_text)
+ .category(category)
.image_url(data.clone().image_url.into())
.build();
let emoji = CustomEmoji::update(&mut context.pool(), data.id, &emoji_form).await?;
local_site_to_slur_regex,
local_user_view_from_jwt,
mark_post_as_read,
+ sanitize_html,
+ sanitize_html_opt,
EndpointType,
},
};
.map(|u| (u.title, u.description, u.embed_video_url))
.unwrap_or_default();
+ let name = sanitize_html(data.name.trim());
+ let body = sanitize_html_opt(&data.body);
+ let embed_title = sanitize_html_opt(&embed_title);
+ let embed_description = sanitize_html_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(
};
let post_form = PostInsertForm::builder()
- .name(data.name.trim().to_owned())
+ .name(name)
.url(url)
- .body(data.body.clone())
+ .body(body)
.community_id(data.community_id)
.creator_id(local_user_view.person.id)
.nsfw(data.nsfw)
context::LemmyContext,
post::{EditPost, PostResponse},
request::fetch_site_data,
- utils::{check_community_ban, local_site_to_slur_regex, local_user_view_from_jwt},
+ utils::{
+ check_community_ban,
+ local_site_to_slur_regex,
+ local_user_view_from_jwt,
+ sanitize_html_opt,
+ },
};
use lemmy_db_schema::{
source::{
// TODO No good way to handle a clear.
// Issue link: https://github.com/LemmyNet/lemmy/issues/2287
let url = Some(data_url.map(clean_url_params).map(Into::into));
- let body = diesel_option_overwrite(&data.body);
let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs_opt(&data.name, &slur_regex)?;
.map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
.unwrap_or_default();
+ let name = sanitize_html_opt(&data.name);
+ let body = sanitize_html_opt(&data.body);
+ let body = diesel_option_overwrite(body);
+ let embed_title = embed_title.map(|e| sanitize_html_opt(&e));
+ let embed_description = embed_description.map(|e| sanitize_html_opt(&e));
+
let language_id = self.language_id;
CommunityLanguage::is_allowed_community_language(
&mut context.pool(),
.await?;
let post_form = PostUpdateForm::builder()
- .name(data.name.clone())
+ .name(name)
.url(url)
.body(body)
.nsfw(data.nsfw)
get_interface_language,
local_site_to_slur_regex,
local_user_view_from_jwt,
+ sanitize_html,
send_email_to_user,
EndpointType,
},
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
- let content_slurs_removed = remove_slurs(
- &data.content.clone(),
- &local_site_to_slur_regex(&local_site),
- );
- is_valid_body_field(&Some(content_slurs_removed.clone()), false)?;
+ let content = sanitize_html(&data.content);
+ let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site));
+ is_valid_body_field(&Some(content.clone()), false)?;
check_person_block(
local_user_view.person.id,
.await?;
let private_message_form = PrivateMessageInsertForm::builder()
- .content(content_slurs_removed.clone())
+ .content(content.clone())
.creator_id(local_user_view.person.id)
.recipient_id(data.recipient_id)
.build();
send_email_to_user(
&local_recipient,
&lang.notification_private_message_subject(sender_name),
- &lang.notification_private_message_body(inbox_link, &content_slurs_removed, sender_name),
+ &lang.notification_private_message_body(inbox_link, &content, sender_name),
context.settings(),
)
.await;
use lemmy_api_common::{
context::LemmyContext,
private_message::{EditPrivateMessage, PrivateMessageResponse},
- utils::{local_site_to_slur_regex, local_user_view_from_jwt},
+ utils::{local_site_to_slur_regex, local_user_view_from_jwt, sanitize_html},
};
use lemmy_db_schema::{
source::{
}
// Doing the update
- let content_slurs_removed = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site));
- is_valid_body_field(&Some(content_slurs_removed.clone()), false)?;
+ let content = sanitize_html(&data.content);
+ let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site));
+ is_valid_body_field(&Some(content.clone()), false)?;
let private_message_id = data.private_message_id;
PrivateMessage::update(
&mut context.pool(),
private_message_id,
&PrivateMessageUpdateForm::builder()
- .content(Some(content_slurs_removed))
+ .content(Some(content))
.updated(Some(Some(naive_now())))
.build(),
)
is_admin,
local_site_rate_limit_to_rate_limit_config,
local_user_view_from_jwt,
+ sanitize_html,
+ sanitize_html_opt,
},
};
use lemmy_db_schema::{
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(&data.name);
+ let sidebar = sanitize_html_opt(&data.sidebar);
+ let description = sanitize_html_opt(&data.description);
+
let site_form = SiteUpdateForm::builder()
- .name(Some(data.name.clone()))
- .sidebar(diesel_option_overwrite(&data.sidebar))
- .description(diesel_option_overwrite(&data.description))
+ .name(Some(name))
+ .sidebar(diesel_option_overwrite(sidebar))
+ .description(diesel_option_overwrite(description))
.icon(diesel_option_overwrite_to_url(&data.icon)?)
.banner(diesel_option_overwrite_to_url(&data.banner)?)
.actor_id(Some(actor_id))
Site::update(&mut context.pool(), site_id, &site_form).await?;
+ let application_question = sanitize_html_opt(&data.application_question);
+ let default_theme = sanitize_html_opt(&data.default_theme);
+ let legal_information = sanitize_html_opt(&data.legal_information);
+
let local_site_form = LocalSiteUpdateForm::builder()
// Set the site setup to true
.site_setup(Some(true))
.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(&data.application_question))
+ .application_question(diesel_option_overwrite(application_question))
.private_instance(data.private_instance)
- .default_theme(data.default_theme.clone())
+ .default_theme(default_theme)
.default_post_listing_type(data.default_post_listing_type)
- .legal_information(diesel_option_overwrite(&data.legal_information))
+ .legal_information(diesel_option_overwrite(legal_information))
.application_email_admins(data.application_email_admins)
.hide_modlog_mod_names(data.hide_modlog_mod_names)
.updated(Some(Some(naive_now())))
- .slur_filter_regex(diesel_option_overwrite(&data.slur_filter_regex))
+ .slur_filter_regex(diesel_option_overwrite(data.slur_filter_regex.clone()))
.actor_name_max_length(data.actor_name_max_length)
.federation_enabled(data.federation_enabled)
.captcha_enabled(data.captcha_enabled)
use lemmy_api_common::{
context::LemmyContext,
site::{EditSite, SiteResponse},
- utils::{is_admin, local_site_rate_limit_to_rate_limit_config, local_user_view_from_jwt},
+ utils::{
+ is_admin,
+ local_site_rate_limit_to_rate_limit_config,
+ local_user_view_from_jwt,
+ sanitize_html_opt,
+ },
};
use lemmy_db_schema::{
source::{
SiteLanguage::update(&mut context.pool(), discussion_languages.clone(), &site).await?;
}
+ let name = sanitize_html_opt(&data.name);
+ let sidebar = sanitize_html_opt(&data.sidebar);
+ let description = sanitize_html_opt(&data.description);
+
let site_form = SiteUpdateForm::builder()
- .name(data.name.clone())
- .sidebar(diesel_option_overwrite(&data.sidebar))
- .description(diesel_option_overwrite(&data.description))
+ .name(name)
+ .sidebar(diesel_option_overwrite(sidebar))
+ .description(diesel_option_overwrite(description))
.icon(diesel_option_overwrite_to_url(&data.icon)?)
.banner(diesel_option_overwrite_to_url(&data.banner)?)
.updated(Some(Some(naive_now())))
// Diesel will throw an error for empty update forms
.ok();
+ let application_question = sanitize_html_opt(&data.application_question);
+ let default_theme = sanitize_html_opt(&data.default_theme);
+ let legal_information = sanitize_html_opt(&data.legal_information);
+
let local_site_form = LocalSiteUpdateForm::builder()
.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(&data.application_question))
+ .application_question(diesel_option_overwrite(application_question))
.private_instance(data.private_instance)
- .default_theme(data.default_theme.clone())
+ .default_theme(default_theme)
.default_post_listing_type(data.default_post_listing_type)
- .legal_information(diesel_option_overwrite(&data.legal_information))
+ .legal_information(diesel_option_overwrite(legal_information))
.application_email_admins(data.application_email_admins)
.hide_modlog_mod_names(data.hide_modlog_mod_names)
.updated(Some(Some(naive_now())))
- .slur_filter_regex(diesel_option_overwrite(&data.slur_filter_regex))
+ .slur_filter_regex(diesel_option_overwrite(data.slur_filter_regex.clone()))
.actor_name_max_length(data.actor_name_max_length)
.federation_enabled(data.federation_enabled)
.captcha_enabled(data.captcha_enabled)
honeypot_check,
local_site_to_slur_regex,
password_length_check,
+ sanitize_html,
send_new_applicant_email_to_admins,
send_verification_email,
EndpointType,
let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs(&data.username, &slur_regex)?;
check_slurs_opt(&data.answer, &slur_regex)?;
+ let username = sanitize_html(&data.username);
let actor_keypair = generate_actor_keypair()?;
is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize)?;
// Register the new person
let person_form = PersonInsertForm::builder()
- .name(data.username.clone())
+ .name(username)
.actor_id(Some(actor_id.clone()))
.private_key(Some(actor_keypair.private_key))
.public_key(actor_keypair.public_key)
use chrono::NaiveDateTime;
use lemmy_api_common::{
context::LemmyContext,
- utils::{remove_user_data, remove_user_data_in_community},
+ utils::{remove_user_data, remove_user_data_in_community, sanitize_html_opt},
};
use lemmy_db_schema::{
source::{
let form = ModBanForm {
mod_person_id: mod_person.id,
other_person_id: blocked_person.id,
- reason: self.summary,
+ reason: sanitize_html_opt(&self.summary),
banned: Some(true),
expires,
};
mod_person_id: mod_person.id,
other_person_id: blocked_person.id,
community_id: community.id,
- reason: self.summary,
+ reason: sanitize_html_opt(&self.summary),
banned: Some(true),
expires,
};
protocol::verification::verify_domains_match,
traits::{ActivityHandler, Actor},
};
-use lemmy_api_common::context::LemmyContext;
+use lemmy_api_common::{context::LemmyContext, utils::sanitize_html_opt};
use lemmy_db_schema::{
source::{
community::{CommunityPersonBan, CommunityPersonBanForm},
let form = ModBanForm {
mod_person_id: mod_person.id,
other_person_id: blocked_person.id,
- reason: self.object.summary,
+ reason: sanitize_html_opt(&self.object.summary),
banned: Some(false),
expires,
};
mod_person_id: mod_person.id,
other_person_id: blocked_person.id,
community_id: community.id,
- reason: self.object.summary,
+ reason: sanitize_html_opt(&self.object.summary),
banned: Some(false),
expires,
};
comment::{CommentReportResponse, CreateCommentReport},
context::LemmyContext,
post::{CreatePostReport, PostReportResponse},
- utils::local_user_view_from_jwt,
+ utils::{local_user_view_from_jwt, sanitize_html},
};
use lemmy_db_schema::{
source::{
post_id: post.id,
original_post_name: post.name.clone(),
original_post_url: post.url.clone(),
- reason: self.summary,
+ reason: sanitize_html(&self.summary),
original_post_body: post.body.clone(),
};
PostReport::report(&mut context.pool(), &report_form).await?;
creator_id: actor.id,
comment_id: comment.id,
original_comment_text: comment.content.clone(),
- reason: self.summary,
+ reason: sanitize_html(&self.summary),
};
CommentReport::report(&mut context.pool(), &report_form).await?;
}
protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
};
use activitypub_federation::{config::Data, kinds::activity::DeleteType, traits::ActivityHandler};
-use lemmy_api_common::context::LemmyContext;
+use lemmy_api_common::{context::LemmyContext, utils::sanitize_html_opt};
use lemmy_db_schema::{
source::{
comment::{Comment, CommentUpdateForm},
reason: Option<String>,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
+ let reason = sanitize_html_opt(&reason);
+
match DeletableObjects::read_from_db(object, context).await? {
DeletableObjects::Community(community) => {
if community.local {
traits::Object,
};
use chrono::NaiveDateTime;
-use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
+use lemmy_api_common::{
+ context::LemmyContext,
+ utils::{local_site_opt_to_slur_regex, sanitize_html},
+};
use lemmy_db_schema::{
source::{
comment::{Comment, CommentInsertForm, CommentUpdateForm},
let local_site = LocalSite::read(&mut context.pool()).await.ok();
let slur_regex = &local_site_opt_to_slur_regex(&local_site);
- let content_slurs_removed = remove_slurs(&content, slur_regex);
+ let content = remove_slurs(&content, slur_regex);
+ let content = sanitize_html(&content);
let language_id =
LanguageTag::to_language_id_single(note.language, &mut context.pool()).await?;
let form = CommentInsertForm {
creator_id: creator.id,
post_id: post.id,
- content: content_slurs_removed,
+ content,
removed: None,
published: note.published.map(|u| u.naive_local()),
updated: note.updated.map(|u| u.naive_local()),
traits::{Actor, Object},
};
use chrono::NaiveDateTime;
-use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
+use lemmy_api_common::{
+ context::LemmyContext,
+ utils::{local_site_opt_to_slur_regex, sanitize_html_opt},
+};
use lemmy_db_schema::{
newtypes::InstanceId,
source::{
let domain = apub.id.inner().domain().expect("group id has domain");
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_opt(&sidebar);
+ let description = sanitize_html_opt(&apub.summary);
+
let site_form = SiteInsertForm {
name: apub.name.clone(),
- sidebar: read_from_string_or_source_opt(&apub.content, &None, &apub.source),
+ sidebar,
updated: apub.updated.map(|u| u.clone().naive_local()),
icon: apub.icon.clone().map(|i| i.url.into()),
banner: apub.image.clone().map(|i| i.url.into()),
- description: apub.summary.clone(),
+ description,
actor_id: Some(apub.id.clone().into()),
last_refreshed_at: Some(naive_now()),
inbox_url: Some(apub.inbox.clone().into()),
use chrono::NaiveDateTime;
use lemmy_api_common::{
context::LemmyContext,
- utils::{generate_outbox_url, local_site_opt_to_slur_regex},
+ utils::{generate_outbox_url, local_site_opt_to_slur_regex, sanitize_html, sanitize_html_opt},
};
use lemmy_db_schema::{
source::person::{Person as DbPerson, PersonInsertForm, PersonUpdateForm},
) -> Result<ApubPerson, LemmyError> {
let instance_id = fetch_instance_actor_for_object(&person.id, context).await?;
+ let name = sanitize_html(&person.preferred_username);
+ let display_name = sanitize_html_opt(&person.name);
+ let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source);
+ let bio = sanitize_html_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 = person.name.filter(|n| !n.is_empty());
+ let display_name = display_name.filter(|n| !n.is_empty());
let person_form = PersonInsertForm {
- name: person.preferred_username,
+ name,
display_name,
banned: None,
ban_expires: None,
published: person.published.map(|u| u.naive_local()),
updated: person.updated.map(|u| u.naive_local()),
actor_id: Some(person.id.into()),
- bio: read_from_string_or_source_opt(&person.summary, &None, &person.source),
+ bio,
local: Some(false),
admin: Some(false),
bot_account: Some(person.kind == UserTypes::Service),
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},
+ utils::{
+ is_mod_or_admin,
+ local_site_opt_to_sensitive,
+ local_site_opt_to_slur_regex,
+ sanitize_html,
+ sanitize_html_opt,
+ },
};
use lemmy_db_schema::{
self,
let language_id =
LanguageTag::to_language_id_single(page.language, &mut context.pool()).await?;
+ let name = sanitize_html(&name);
+ let embed_title = sanitize_html_opt(&embed_title);
+ let embed_description = sanitize_html_opt(&embed_description);
+
PostInsertForm {
name,
url: url.map(Into::into),
traits::Object,
};
use chrono::NaiveDateTime;
-use lemmy_api_common::{context::LemmyContext, utils::check_person_block};
+use lemmy_api_common::{
+ context::LemmyContext,
+ utils::{check_person_block, sanitize_html},
+};
use lemmy_db_schema::{
source::{
person::Person,
let recipient = note.to[0].dereference(context).await?;
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(&content);
+
let form = PrivateMessageInsertForm {
creator_id: creator.id,
recipient_id: recipient.id,
- content: read_from_string_or_source(¬e.content, &None, ¬e.source),
+ content,
published: note.published.map(|u| u.naive_local()),
updated: note.updated.map(|u| u.naive_local()),
deleted: Some(false),
},
};
use chrono::{DateTime, FixedOffset};
-use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
+use lemmy_api_common::{
+ context::LemmyContext,
+ utils::{local_site_opt_to_slur_regex, sanitize_html, sanitize_html_opt},
+};
use lemmy_db_schema::{
newtypes::InstanceId,
source::community::{CommunityInsertForm, CommunityUpdateForm},
}
pub(crate) fn into_insert_form(self, instance_id: InstanceId) -> CommunityInsertForm {
+ let name = sanitize_html(&self.preferred_username);
+ let title = sanitize_html(&self.name.unwrap_or(self.preferred_username));
+ let description = read_from_string_or_source_opt(&self.summary, &None, &self.source);
+ let description = sanitize_html_opt(&description);
+
CommunityInsertForm {
- name: self.preferred_username.clone(),
- title: self.name.unwrap_or(self.preferred_username),
- description: read_from_string_or_source_opt(&self.summary, &None, &self.source),
+ name,
+ title,
+ description,
removed: None,
published: self.published.map(|u| u.naive_local()),
updated: self.updated.map(|u| u.naive_local()),
EMAIL_REGEX.is_match(test)
}
-pub fn diesel_option_overwrite(opt: &Option<String>) -> Option<Option<String>> {
+pub fn diesel_option_overwrite(opt: Option<String>) -> Option<Option<String>> {
match opt {
// An empty string is an erase
Some(unwrapped) => {
if !unwrapped.eq("") {
- Some(Some(unwrapped.clone()))
+ Some(Some(unwrapped))
} else {
Some(None)
}
#[test]
fn test_diesel_option_overwrite() {
- assert_eq!(diesel_option_overwrite(&None), None);
- assert_eq!(diesel_option_overwrite(&Some(String::new())), Some(None));
+ assert_eq!(diesel_option_overwrite(None), None);
+ assert_eq!(diesel_option_overwrite(Some(String::new())), Some(None));
assert_eq!(
- diesel_option_overwrite(&Some("test".to_string())),
+ diesel_option_overwrite(Some("test".to_string())),
Some(Some("test".to_string()))
);
}