Forbid remote URLs for avatars/banners (fixes #1618) (#2132)

This commit is contained in:
Nutomic 2022-03-18 15:46:58 +00:00 committed by GitHub
parent 611d336f94
commit bcf7ec6109
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 58 additions and 8 deletions

View file

@ -427,14 +427,10 @@ export async function createCommunity(
name_: string = randomString(5) name_: string = randomString(5)
): Promise<CommunityResponse> { ): Promise<CommunityResponse> {
let description = 'a sample description'; let description = 'a sample description';
let icon = 'https://image.flaticon.com/icons/png/512/35/35896.png';
let banner = 'https://image.flaticon.com/icons/png/512/35/35896.png';
let form: CreateCommunity = { let form: CreateCommunity = {
name: name_, name: name_,
title: name_, title: name_,
description, description,
icon,
banner,
nsfw: false, nsfw: false,
auth: api.auth, auth: api.auth,
}; };

View file

@ -5,6 +5,7 @@ use captcha::{gen, Difficulty};
use chrono::Duration; use chrono::Duration;
use lemmy_api_common::{ use lemmy_api_common::{
blocking, blocking,
check_image_has_local_domain,
check_registration_application, check_registration_application,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_admin, is_admin,
@ -175,6 +176,9 @@ impl Perform for SaveUserSettings {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
check_image_has_local_domain(&data.avatar)?;
check_image_has_local_domain(&data.banner)?;
let avatar = diesel_option_overwrite_to_url(&data.avatar)?; let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
let banner = diesel_option_overwrite_to_url(&data.banner)?; let banner = diesel_option_overwrite_to_url(&data.banner)?;
let bio = diesel_option_overwrite(&data.bio); let bio = diesel_option_overwrite(&data.bio);

View file

@ -40,6 +40,7 @@ use lemmy_utils::{
LemmyError, LemmyError,
Sensitive, Sensitive,
}; };
use url::Url;
pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError> pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
where where
@ -623,3 +624,15 @@ pub async fn remove_user_data_in_community(
Ok(()) Ok(())
} }
pub fn check_image_has_local_domain(url: &Option<String>) -> Result<(), LemmyError> {
if let Some(url) = url {
let settings = Settings::get();
let url = Url::parse(url)?;
let domain = url.domain().expect("url has domain");
if domain != settings.hostname {
return Err(LemmyError::from_message("image_not_local"));
}
}
Ok(())
}

View file

@ -2,6 +2,7 @@ use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
blocking, blocking,
check_image_has_local_domain,
community::{CommunityResponse, CreateCommunity}, community::{CommunityResponse, CreateCommunity},
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_admin, is_admin,
@ -63,6 +64,8 @@ impl PerformCrud for CreateCommunity {
check_slurs(&data.name, &context.settings().slur_regex())?; check_slurs(&data.name, &context.settings().slur_regex())?;
check_slurs(&data.title, &context.settings().slur_regex())?; check_slurs(&data.title, &context.settings().slur_regex())?;
check_slurs_opt(&data.description, &context.settings().slur_regex())?; check_slurs_opt(&data.description, &context.settings().slur_regex())?;
check_image_has_local_domain(&data.icon)?;
check_image_has_local_domain(&data.banner)?;
if !is_valid_actor_name(&data.name, context.settings().actor_name_max_length) { if !is_valid_actor_name(&data.name, context.settings().actor_name_max_length) {
return Err(LemmyError::from_message("invalid_community_name")); return Err(LemmyError::from_message("invalid_community_name"));

View file

@ -2,6 +2,7 @@ use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
blocking, blocking,
check_image_has_local_domain,
community::{CommunityResponse, EditCommunity, HideCommunity}, community::{CommunityResponse, EditCommunity, HideCommunity},
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_admin, is_admin,
@ -37,6 +38,8 @@ impl PerformCrud for EditCommunity {
check_slurs_opt(&data.title, &context.settings().slur_regex())?; check_slurs_opt(&data.title, &context.settings().slur_regex())?;
check_slurs_opt(&data.description, &context.settings().slur_regex())?; check_slurs_opt(&data.description, &context.settings().slur_regex())?;
check_image_has_local_domain(&data.icon)?;
check_image_has_local_domain(&data.banner)?;
// Verify its a mod (only mods can edit it) // Verify its a mod (only mods can edit it)
let community_id = data.community_id; let community_id = data.community_id;

View file

@ -2,6 +2,7 @@ use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
blocking, blocking,
check_image_has_local_domain,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_admin, is_admin,
site::*, site::*,
@ -49,6 +50,8 @@ impl PerformCrud for CreateSite {
check_slurs(&data.name, &context.settings().slur_regex())?; check_slurs(&data.name, &context.settings().slur_regex())?;
check_slurs_opt(&data.description, &context.settings().slur_regex())?; check_slurs_opt(&data.description, &context.settings().slur_regex())?;
check_image_has_local_domain(&data.icon)?;
check_image_has_local_domain(&data.banner)?;
// Make sure user is an admin // Make sure user is an admin
is_admin(&local_user_view)?; is_admin(&local_user_view)?;

View file

@ -2,6 +2,7 @@ use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
blocking, blocking,
check_image_has_local_domain,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_admin, is_admin,
site::{EditSite, SiteResponse}, site::{EditSite, SiteResponse},
@ -38,6 +39,8 @@ impl PerformCrud for EditSite {
check_slurs_opt(&data.name, &context.settings().slur_regex())?; check_slurs_opt(&data.name, &context.settings().slur_regex())?;
check_slurs_opt(&data.description, &context.settings().slur_regex())?; check_slurs_opt(&data.description, &context.settings().slur_regex())?;
check_image_has_local_domain(&data.icon)?;
check_image_has_local_domain(&data.banner)?;
// Make sure user is an admin // Make sure user is an admin
is_admin(&local_user_view)?; is_admin(&local_user_view)?;

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
check_is_apub_id_valid, check_is_apub_id_valid,
objects::get_summary_from_string_or_source, objects::{get_summary_from_string_or_source, verify_image_domain_matches},
protocol::{objects::instance::Instance, ImageObject, Source}, protocol::{objects::instance::Instance, ImageObject, Source},
}; };
use activitystreams_kinds::actor::ServiceType; use activitystreams_kinds::actor::ServiceType;
@ -103,6 +103,8 @@ impl ApubObject for ApubSite {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
check_is_apub_id_valid(apub.id.inner(), true, &data.settings())?; check_is_apub_id_valid(apub.id.inner(), true, &data.settings())?;
verify_domains_match(expected_domain, apub.id.inner())?; verify_domains_match(expected_domain, apub.id.inner())?;
verify_image_domain_matches(expected_domain, &apub.icon)?;
verify_image_domain_matches(expected_domain, &apub.image)?;
let slur_regex = &data.settings().slur_regex(); let slur_regex = &data.settings().slur_regex();
check_slurs(&apub.name, slur_regex)?; check_slurs(&apub.name, slur_regex)?;

View file

@ -1,5 +1,8 @@
use crate::protocol::Source; use crate::protocol::{ImageObject, Source};
use html2md::parse_html; use html2md::parse_html;
use lemmy_apub_lib::verify::verify_domains_match;
use lemmy_utils::LemmyError;
use url::Url;
pub mod comment; pub mod comment;
pub mod community; pub mod community;
@ -19,6 +22,14 @@ pub(crate) fn get_summary_from_string_or_source(
} }
} }
pub fn verify_image_domain_matches(a: &Url, b: &Option<ImageObject>) -> Result<(), LemmyError> {
if let Some(b) = b {
verify_domains_match(a, &b.url)
} else {
Ok(())
}
}
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use actix::Actor; use actix::Actor;

View file

@ -1,7 +1,11 @@
use crate::{ use crate::{
check_is_apub_id_valid, check_is_apub_id_valid,
generate_outbox_url, generate_outbox_url,
objects::{get_summary_from_string_or_source, instance::fetch_instance_actor_for_object}, objects::{
get_summary_from_string_or_source,
instance::fetch_instance_actor_for_object,
verify_image_domain_matches,
},
protocol::{ protocol::{
objects::{ objects::{
person::{Person, UserTypes}, person::{Person, UserTypes},
@ -123,6 +127,8 @@ impl ApubObject for ApubPerson {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_domains_match(person.id.inner(), expected_domain)?; verify_domains_match(person.id.inner(), expected_domain)?;
check_is_apub_id_valid(person.id.inner(), false, &context.settings())?; check_is_apub_id_valid(person.id.inner(), false, &context.settings())?;
verify_image_domain_matches(expected_domain, &person.icon)?;
verify_image_domain_matches(expected_domain, &person.image)?;
let slur_regex = &context.settings().slur_regex(); let slur_regex = &context.settings().slur_regex();
check_slurs(&person.preferred_username, slur_regex)?; check_slurs(&person.preferred_username, slur_regex)?;

View file

@ -4,7 +4,11 @@ use crate::{
community_moderators::ApubCommunityModerators, community_moderators::ApubCommunityModerators,
community_outbox::ApubCommunityOutbox, community_outbox::ApubCommunityOutbox,
}, },
objects::{community::ApubCommunity, get_summary_from_string_or_source}, objects::{
community::ApubCommunity,
get_summary_from_string_or_source,
verify_image_domain_matches,
},
protocol::{objects::Endpoints, ImageObject, Source}, protocol::{objects::Endpoints, ImageObject, Source},
}; };
use activitystreams_kinds::actor::GroupType; use activitystreams_kinds::actor::GroupType;
@ -58,6 +62,8 @@ impl Group {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
check_is_apub_id_valid(self.id.inner(), true, &context.settings())?; check_is_apub_id_valid(self.id.inner(), true, &context.settings())?;
verify_domains_match(expected_domain, self.id.inner())?; verify_domains_match(expected_domain, self.id.inner())?;
verify_image_domain_matches(expected_domain, &self.icon)?;
verify_image_domain_matches(expected_domain, &self.image)?;
let slur_regex = &context.settings().slur_regex(); let slur_regex = &context.settings().slur_regex();
check_slurs(&self.preferred_username, slur_regex)?; check_slurs(&self.preferred_username, slur_regex)?;