Disallow <code> tags in bio
This commit is contained in:
parent
448b5afa88
commit
0548e6e72a
5 changed files with 59 additions and 37 deletions
|
@ -3,3 +3,4 @@ pub mod helpers;
|
||||||
pub mod mentions;
|
pub mod mentions;
|
||||||
pub mod queries;
|
pub mod queries;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
mod validators;
|
||||||
|
|
|
@ -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::errors::{ConversionError, DatabaseError, ValidationError};
|
||||||
use crate::models::attachments::types::DbMediaAttachment;
|
use crate::models::attachments::types::DbMediaAttachment;
|
||||||
use crate::models::profiles::types::DbActorProfile;
|
use crate::models::profiles::types::DbActorProfile;
|
||||||
use crate::utils::html::clean_html_strict;
|
use super::validators::clean_content;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Visibility {
|
pub enum Visibility {
|
||||||
|
@ -263,15 +263,7 @@ impl PostCreateData {
|
||||||
/// Validate and clean post data (only for local posts).
|
/// Validate and clean post data (only for local posts).
|
||||||
pub fn clean(&mut self, character_limit: usize) -> Result<(), ValidationError> {
|
pub fn clean(&mut self, character_limit: usize) -> Result<(), ValidationError> {
|
||||||
assert!(self.object_id.is_none());
|
assert!(self.object_id.is_none());
|
||||||
if self.content.chars().count() > character_limit {
|
self.content = clean_content(&self.content, character_limit)?;
|
||||||
return Err(ValidationError("post is too long"));
|
|
||||||
};
|
|
||||||
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"));
|
|
||||||
};
|
|
||||||
self.content = content_trimmed.to_string();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,16 +280,7 @@ mod tests {
|
||||||
const POST_CHARACTER_LIMIT: usize = 1000;
|
const POST_CHARACTER_LIMIT: usize = 1000;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_post_data_empty() {
|
fn test_post_data_clean() {
|
||||||
let mut post_data_1 = PostCreateData {
|
|
||||||
content: " ".to_string(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
assert_eq!(post_data_1.clean(POST_CHARACTER_LIMIT).is_ok(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_post_data_trimming() {
|
|
||||||
let mut post_data_2 = PostCreateData {
|
let mut post_data_2 = PostCreateData {
|
||||||
content: "test ".to_string(),
|
content: "test ".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
40
src/models/posts/validators.rs
Normal file
40
src/models/posts/validators.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use crate::errors::ValidationError;
|
||||||
|
use crate::utils::html::clean_html_strict;
|
||||||
|
|
||||||
|
const CONTENT_ALLOWED_TAGS: [&str; 4] = ["a", "br", "pre", "code"];
|
||||||
|
|
||||||
|
pub fn clean_content(
|
||||||
|
content: &str,
|
||||||
|
character_limit: usize,
|
||||||
|
) -> Result<String, ValidationError> {
|
||||||
|
if content.chars().count() > character_limit {
|
||||||
|
return Err(ValidationError("post is too long"));
|
||||||
|
};
|
||||||
|
let content_safe = clean_html_strict(content, &CONTENT_ALLOWED_TAGS);
|
||||||
|
let content_trimmed = content_safe.trim();
|
||||||
|
if content_trimmed.is_empty() {
|
||||||
|
return Err(ValidationError("post can not be empty"));
|
||||||
|
};
|
||||||
|
Ok(content_trimmed.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const POST_CHARACTER_LIMIT: usize = 1000;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clean_content_empty() {
|
||||||
|
let content = " ";
|
||||||
|
let result = clean_content(content, POST_CHARACTER_LIMIT);
|
||||||
|
assert_eq!(result.is_ok(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clean_content_trimming() {
|
||||||
|
let content = "test ";
|
||||||
|
let cleaned = clean_content(content, POST_CHARACTER_LIMIT).unwrap();
|
||||||
|
assert_eq!(cleaned, "test");
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,11 @@ use crate::utils::html::{clean_html, clean_html_strict};
|
||||||
use super::types::ExtraField;
|
use super::types::ExtraField;
|
||||||
|
|
||||||
const USERNAME_RE: &str = r"^[a-zA-Z0-9_\.-]+$";
|
const USERNAME_RE: &str = r"^[a-zA-Z0-9_\.-]+$";
|
||||||
|
const DISPLAY_NAME_MAX_LENGTH: usize = 200;
|
||||||
|
const BIO_MAX_LENGTH: usize = 10000;
|
||||||
|
const BIO_ALLOWED_TAGS: [&str; 2] = ["a", "br"];
|
||||||
|
const FIELD_NAME_MAX_SIZE: usize = 500;
|
||||||
|
const FIELD_VALUE_MAX_SIZE: usize = 5000;
|
||||||
|
|
||||||
pub fn validate_username(username: &str) -> Result<(), ValidationError> {
|
pub fn validate_username(username: &str) -> Result<(), ValidationError> {
|
||||||
if username.is_empty() {
|
if username.is_empty() {
|
||||||
|
@ -19,8 +24,6 @@ pub fn validate_username(username: &str) -> Result<(), ValidationError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
const DISPLAY_NAME_MAX_LENGTH: usize = 200;
|
|
||||||
|
|
||||||
pub fn validate_display_name(display_name: &str)
|
pub fn validate_display_name(display_name: &str)
|
||||||
-> Result<(), ValidationError>
|
-> Result<(), ValidationError>
|
||||||
{
|
{
|
||||||
|
@ -30,8 +33,6 @@ pub fn validate_display_name(display_name: &str)
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
const BIO_MAX_LENGTH: usize = 10000;
|
|
||||||
|
|
||||||
pub fn clean_bio(bio: &str, is_remote: bool) -> Result<String, ValidationError> {
|
pub fn clean_bio(bio: &str, is_remote: bool) -> Result<String, ValidationError> {
|
||||||
let cleaned_bio = if is_remote {
|
let cleaned_bio = if is_remote {
|
||||||
// Remote profile
|
// Remote profile
|
||||||
|
@ -42,14 +43,11 @@ pub fn clean_bio(bio: &str, is_remote: bool) -> Result<String, ValidationError>
|
||||||
if bio.chars().count() > BIO_MAX_LENGTH {
|
if bio.chars().count() > BIO_MAX_LENGTH {
|
||||||
return Err(ValidationError("bio is too long"));
|
return Err(ValidationError("bio is too long"));
|
||||||
};
|
};
|
||||||
clean_html_strict(bio)
|
clean_html_strict(bio, &BIO_ALLOWED_TAGS)
|
||||||
};
|
};
|
||||||
Ok(cleaned_bio)
|
Ok(cleaned_bio)
|
||||||
}
|
}
|
||||||
|
|
||||||
const FIELD_NAME_MAX_SIZE: usize = 500;
|
|
||||||
const FIELD_VALUE_MAX_SIZE: usize = 5000;
|
|
||||||
|
|
||||||
/// Validates extra fields and removes fields with empty labels
|
/// Validates extra fields and removes fields with empty labels
|
||||||
pub fn clean_extra_fields(extra_fields: &[ExtraField])
|
pub fn clean_extra_fields(extra_fields: &[ExtraField])
|
||||||
-> Result<Vec<ExtraField>, ValidationError>
|
-> Result<Vec<ExtraField>, ValidationError>
|
||||||
|
@ -57,7 +55,7 @@ pub fn clean_extra_fields(extra_fields: &[ExtraField])
|
||||||
let mut cleaned_extra_fields = vec![];
|
let mut cleaned_extra_fields = vec![];
|
||||||
for mut field in extra_fields.iter().cloned() {
|
for mut field in extra_fields.iter().cloned() {
|
||||||
field.name = field.name.trim().to_string();
|
field.name = field.name.trim().to_string();
|
||||||
field.value = clean_html_strict(&field.value);
|
field.value = clean_html_strict(&field.value, &BIO_ALLOWED_TAGS);
|
||||||
if field.name.is_empty() {
|
if field.name.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
use ammonia::Builder;
|
use ammonia::Builder;
|
||||||
|
|
||||||
|
@ -12,13 +13,12 @@ pub fn clean_html(unsafe_html: &str) -> String {
|
||||||
safe_html
|
safe_html
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean_html_strict(unsafe_html: &str) -> String {
|
pub fn clean_html_strict(
|
||||||
let mut allowed_tags = HashSet::new();
|
unsafe_html: &str,
|
||||||
allowed_tags.insert("a");
|
allowed_tags: &[&str],
|
||||||
allowed_tags.insert("br");
|
) -> String {
|
||||||
allowed_tags.insert("pre");
|
let allowed_tags =
|
||||||
allowed_tags.insert("code");
|
HashSet::from_iter(allowed_tags.iter().copied());
|
||||||
|
|
||||||
let safe_html = Builder::default()
|
let safe_html = Builder::default()
|
||||||
.tags(allowed_tags)
|
.tags(allowed_tags)
|
||||||
.clean(unsafe_html)
|
.clean(unsafe_html)
|
||||||
|
@ -47,7 +47,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_clean_html_strict() {
|
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 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);
|
let safe_html = clean_html_strict(unsafe_html, &["a", "br", "code"]);
|
||||||
assert_eq!(safe_html, r#"test bold with <a href="https://example.com" rel="noopener noreferrer">link</a> and <code>code</code>"#);
|
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