Validate content of incoming Note objects
This commit is contained in:
parent
2747e7b174
commit
6fc319f7dd
5 changed files with 39 additions and 8 deletions
|
@ -337,7 +337,9 @@ paths:
|
|||
type: object
|
||||
properties:
|
||||
status:
|
||||
description: Text content of the post.
|
||||
description: |
|
||||
Text content of the post.
|
||||
Allowed HTML tags: a, br, pre, code.
|
||||
type: string
|
||||
'media_ids[]':
|
||||
description: Array of Attachment ids to be attached as media.
|
||||
|
|
|
@ -37,6 +37,7 @@ use crate::models::relationships::queries::{
|
|||
unfollow,
|
||||
};
|
||||
use crate::models::users::queries::get_user_by_name;
|
||||
use crate::utils::html::clean_html;
|
||||
use super::activity::{Object, Activity, create_activity_accept_follow};
|
||||
use super::actor::Actor;
|
||||
use super::constants::AP_PUBLIC;
|
||||
|
@ -132,6 +133,16 @@ fn get_object_id(object: Value) -> Result<String, ValidationError> {
|
|||
Ok(object_id)
|
||||
}
|
||||
|
||||
const CONTENT_MAX_SIZE: usize = 100000;
|
||||
|
||||
fn clean_note_content(content: &str) -> Result<String, ValidationError> {
|
||||
if content.len() > CONTENT_MAX_SIZE {
|
||||
return Err(ValidationError("content is too long"));
|
||||
};
|
||||
let content_safe = clean_html(content);
|
||||
Ok(content_safe)
|
||||
}
|
||||
|
||||
pub async fn process_note(
|
||||
config: &Config,
|
||||
db_client: &mut impl GenericClient,
|
||||
|
@ -221,6 +232,7 @@ pub async fn process_note(
|
|||
).await?;
|
||||
let content = object.content
|
||||
.ok_or(ValidationError("no content"))?;
|
||||
let content_cleaned = clean_note_content(&content)?;
|
||||
let mut attachments: Vec<Uuid> = Vec::new();
|
||||
if let Some(list) = object.attachment {
|
||||
let mut downloaded = vec![];
|
||||
|
@ -375,7 +387,7 @@ pub async fn process_note(
|
|||
Visibility::Direct
|
||||
};
|
||||
let post_data = PostCreateData {
|
||||
content,
|
||||
content: content_cleaned,
|
||||
in_reply_to_id,
|
||||
repost_of_id: None,
|
||||
visibility,
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::database::int_enum::{int_enum_from_sql, int_enum_to_sql};
|
|||
use crate::errors::{ConversionError, DatabaseError, ValidationError};
|
||||
use crate::models::attachments::types::DbMediaAttachment;
|
||||
use crate::models::profiles::types::DbActorProfile;
|
||||
use crate::utils::html::clean_html;
|
||||
use crate::utils::html::clean_html_strict;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Visibility {
|
||||
|
@ -220,7 +220,7 @@ impl PostCreateData {
|
|||
if self.content.chars().count() > character_limit {
|
||||
return Err(ValidationError("post is too long"));
|
||||
};
|
||||
let content_safe = clean_html(&self.content);
|
||||
let content_safe = clean_html_strict(&self.content);
|
||||
let content_trimmed = content_safe.trim();
|
||||
if content_trimmed.is_empty() {
|
||||
return Err(ValidationError("post can not be empty"));
|
||||
|
|
|
@ -11,7 +11,7 @@ use uuid::Uuid;
|
|||
use crate::activitypub::actor::Actor;
|
||||
use crate::activitypub::views::get_actor_url;
|
||||
use crate::errors::ValidationError;
|
||||
use crate::utils::html::clean_html;
|
||||
use crate::utils::html::clean_html_strict;
|
||||
use super::validators::{
|
||||
validate_username,
|
||||
validate_display_name,
|
||||
|
@ -173,12 +173,12 @@ pub struct ProfileUpdateData {
|
|||
impl ProfileUpdateData {
|
||||
pub fn clean(&mut self) -> Result<(), ValidationError> {
|
||||
// Validate and clean bio
|
||||
self.bio = self.bio.as_ref().map(|val| clean_html(val));
|
||||
self.bio = self.bio.as_ref().map(|val| clean_html_strict(val));
|
||||
// Clean extra fields and remove fields with empty labels
|
||||
self.extra_fields = self.extra_fields.iter().cloned()
|
||||
.map(|mut field| {
|
||||
field.name = field.name.trim().to_string();
|
||||
field.value = clean_html(&field.value);
|
||||
field.value = clean_html_strict(&field.value);
|
||||
field
|
||||
})
|
||||
.filter(|field| !field.name.is_empty())
|
||||
|
|
|
@ -3,6 +3,16 @@ use std::collections::HashSet;
|
|||
use ammonia::Builder;
|
||||
|
||||
pub fn clean_html(unsafe_html: &str) -> String {
|
||||
let safe_html = Builder::default()
|
||||
.add_generic_attributes(&["class"])
|
||||
.add_tag_attributes("a", &["rel", "target"])
|
||||
.link_rel(None)
|
||||
.clean(unsafe_html)
|
||||
.to_string();
|
||||
safe_html
|
||||
}
|
||||
|
||||
pub fn clean_html_strict(unsafe_html: &str) -> String {
|
||||
let mut allowed_tags = HashSet::new();
|
||||
allowed_tags.insert("a");
|
||||
allowed_tags.insert("br");
|
||||
|
@ -22,8 +32,15 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_clean_html() {
|
||||
let unsafe_html = r#"<p>test <b>bold</b><script>dangerous</script> with <a href="https://example.com">link</a> and <code>code</code></p>"#;
|
||||
let unsafe_html = r#"<p><span class="h-card"><a href="https://example.com/user" class="u-url mention" rel="ugc">@<span>user</span></a></span> test</p>"#;
|
||||
let safe_html = clean_html(unsafe_html);
|
||||
assert_eq!(safe_html, r#"<p><span class="h-card"><a href="https://example.com/user" class="u-url mention" rel="ugc">@<span>user</span></a></span> test</p>"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clean_html_strict() {
|
||||
let unsafe_html = r#"<p>test <b>bold</b><script>dangerous</script> with <a href="https://example.com">link</a> and <code>code</code></p>"#;
|
||||
let safe_html = clean_html_strict(unsafe_html);
|
||||
assert_eq!(safe_html, r#"test bold with <a href="https://example.com" rel="noopener noreferrer">link</a> and <code>code</code>"#);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue