Avoid overwriting local objects via federation (#4611)

* Dont allow federation to overwrite local objects

* is_local check in apub lib

* use imports

* fix check, update lib

* use verify_is_remote_object()

* submodule
This commit is contained in:
Nutomic 2024-04-11 16:05:49 +02:00 committed by GitHub
parent 0f6b13a4ec
commit 3a0c1dca90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 34 additions and 13 deletions

4
Cargo.lock generated
View file

@ -16,9 +16,9 @@ checksum = "8f27d075294830fcab6f66e320dab524bc6d048f4a151698e153205559113772"
[[package]] [[package]]
name = "activitypub_federation" name = "activitypub_federation"
version = "0.5.3" version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2a21d597eb09353bc83bea36095e1de0ef5297a6fe16edba3de676898a5ba9" checksum = "6e16130d5914e6483f99bde5a9bb97ca62e1f359e0b9791c8ebd5c7abd50fe8e"
dependencies = [ dependencies = [
"activitystreams-kinds", "activitystreams-kinds",
"actix-web", "actix-web",

View file

@ -99,7 +99,7 @@ lemmy_db_views = { version = "=0.19.4-beta.3", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.4-beta.3", path = "./crates/db_views_actor" } lemmy_db_views_actor = { version = "=0.19.4-beta.3", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.4-beta.3", path = "./crates/db_views_moderator" } lemmy_db_views_moderator = { version = "=0.19.4-beta.3", path = "./crates/db_views_moderator" }
lemmy_federate = { version = "=0.19.4-beta.3", path = "./crates/federate" } lemmy_federate = { version = "=0.19.4-beta.3", path = "./crates/federate" }
activitypub_federation = { version = "0.5.3", default-features = false, features = [ activitypub_federation = { version = "0.5.4", default-features = false, features = [
"actix-web", "actix-web",
] } ] }
diesel = "2.1.4" diesel = "2.1.4"

View file

@ -140,7 +140,7 @@ impl Object for ApubComment {
let community = note.community(context).await?; let community = note.community(context).await?;
check_apub_id_valid_with_strictness(note.id.inner(), community.local, context).await?; check_apub_id_valid_with_strictness(note.id.inner(), community.local, context).await?;
verify_is_remote_object(note.id.inner(), context.settings())?; verify_is_remote_object(&note.id, context)?;
verify_person_in_community(&note.attributed_to, &community, context).await?; verify_person_in_community(&note.attributed_to, &community, context).await?;
let (post, _) = note.get_parents(context).await?; let (post, _) = note.get_parents(context).await?;

View file

@ -1,3 +1,4 @@
use super::verify_is_remote_object;
use crate::{ use crate::{
activities::GetActorType, activities::GetActorType,
check_apub_id_valid_with_strictness, check_apub_id_valid_with_strictness,
@ -124,6 +125,7 @@ impl Object for ApubSite {
) -> LemmyResult<()> { ) -> LemmyResult<()> {
check_apub_id_valid_with_strictness(apub.id.inner(), true, data).await?; check_apub_id_valid_with_strictness(apub.id.inner(), true, data).await?;
verify_domains_match(expected_domain, apub.id.inner())?; verify_domains_match(expected_domain, apub.id.inner())?;
verify_is_remote_object(&apub.id, data)?;
let local_site_data = local_site_data_cached(&mut data.pool()).await?; let local_site_data = local_site_data_cached(&mut data.pool()).await?;
let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);

View file

@ -1,9 +1,16 @@
use crate::protocol::Source; use crate::protocol::Source;
use activitypub_federation::protocol::values::MediaTypeMarkdownOrHtml; use activitypub_federation::{
config::Data,
fetch::object_id::ObjectId,
protocol::values::MediaTypeMarkdownOrHtml,
traits::Object,
};
use anyhow::anyhow; use anyhow::anyhow;
use html2md::parse_html; use html2md::parse_html;
use lemmy_utils::{error::LemmyResult, settings::structs::Settings}; use lemmy_api_common::context::LemmyContext;
use url::Url; use lemmy_utils::error::LemmyResult;
use serde::Deserialize;
use std::fmt::Debug;
pub mod comment; pub mod comment;
pub mod community; pub mod community;
@ -43,9 +50,15 @@ pub(crate) fn read_from_string_or_source_opt(
/// wrapped in Announce. If we simply receive this like any other federated object, overwrite the /// wrapped in Announce. If we simply receive this like any other federated object, overwrite the
/// existing, local Post. In particular, it will set the field local = false, so that the object /// existing, local Post. In particular, it will set the field local = false, so that the object
/// can't be fetched from the Activitypub HTTP endpoint anymore (which only serves local objects). /// can't be fetched from the Activitypub HTTP endpoint anymore (which only serves local objects).
pub(crate) fn verify_is_remote_object(id: &Url, settings: &Settings) -> LemmyResult<()> { pub(crate) fn verify_is_remote_object<T>(
let local_domain = settings.get_hostname_without_port()?; id: &ObjectId<T>,
if id.domain() == Some(&local_domain) { context: &Data<LemmyContext>,
) -> LemmyResult<()>
where
T: Object<DataType = LemmyContext> + Debug + Send + 'static,
for<'de2> <T as Object>::Kind: Deserialize<'de2>,
{
if id.is_local(context) {
Err(anyhow!("cant accept local object from remote instance").into()) Err(anyhow!("cant accept local object from remote instance").into())
} else { } else {
Ok(()) Ok(())

View file

@ -1,3 +1,4 @@
use super::verify_is_remote_object;
use crate::{ use crate::{
activities::GetActorType, activities::GetActorType,
check_apub_id_valid_with_strictness, check_apub_id_valid_with_strictness,
@ -137,6 +138,7 @@ impl Object for ApubPerson {
check_slurs_opt(&person.name, slur_regex)?; check_slurs_opt(&person.name, slur_regex)?;
verify_domains_match(person.id.inner(), expected_domain)?; verify_domains_match(person.id.inner(), expected_domain)?;
verify_is_remote_object(&person.id, context)?;
check_apub_id_valid_with_strictness(person.id.inner(), false, context).await?; check_apub_id_valid_with_strictness(person.id.inner(), false, context).await?;
let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source); let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source);

View file

@ -165,7 +165,7 @@ impl Object for ApubPost {
// instance from the post author. // instance from the post author.
if !page.is_mod_action(context).await? { if !page.is_mod_action(context).await? {
verify_domains_match(page.id.inner(), expected_domain)?; verify_domains_match(page.id.inner(), expected_domain)?;
verify_is_remote_object(page.id.inner(), context.settings())?; verify_is_remote_object(&page.id, context)?;
}; };
let community = page.community(context).await?; let community = page.community(context).await?;

View file

@ -1,3 +1,4 @@
use super::verify_is_remote_object;
use crate::{ use crate::{
check_apub_id_valid_with_strictness, check_apub_id_valid_with_strictness,
objects::read_from_string_or_source, objects::read_from_string_or_source,
@ -105,6 +106,7 @@ impl Object for ApubPrivateMessage {
) -> LemmyResult<()> { ) -> LemmyResult<()> {
verify_domains_match(note.id.inner(), expected_domain)?; verify_domains_match(note.id.inner(), expected_domain)?;
verify_domains_match(note.attributed_to.inner(), note.id.inner())?; verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
verify_is_remote_object(&note.id, context)?;
check_apub_id_valid_with_strictness(note.id.inner(), false, context).await?; check_apub_id_valid_with_strictness(note.id.inner(), false, context).await?;
let person = note.attributed_to.dereference(context).await?; let person = note.attributed_to.dereference(context).await?;

View file

@ -7,7 +7,7 @@ use crate::{
community_outbox::ApubCommunityOutbox, community_outbox::ApubCommunityOutbox,
}, },
local_site_data_cached, local_site_data_cached,
objects::{community::ApubCommunity, read_from_string_or_source_opt}, objects::{community::ApubCommunity, read_from_string_or_source_opt, verify_is_remote_object},
protocol::{ protocol::{
objects::{Endpoints, LanguageTag}, objects::{Endpoints, LanguageTag},
ImageObject, ImageObject,
@ -15,6 +15,7 @@ use crate::{
}, },
}; };
use activitypub_federation::{ use activitypub_federation::{
config::Data,
fetch::{collection_id::CollectionId, object_id::ObjectId}, fetch::{collection_id::CollectionId, object_id::ObjectId},
kinds::actor::GroupType, kinds::actor::GroupType,
protocol::{ protocol::{
@ -75,10 +76,11 @@ impl Group {
pub(crate) async fn verify( pub(crate) async fn verify(
&self, &self,
expected_domain: &Url, expected_domain: &Url,
context: &LemmyContext, context: &Data<LemmyContext>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
check_apub_id_valid_with_strictness(self.id.inner(), true, context).await?; check_apub_id_valid_with_strictness(self.id.inner(), true, context).await?;
verify_domains_match(expected_domain, self.id.inner())?; verify_domains_match(expected_domain, self.id.inner())?;
verify_is_remote_object(&self.id, context)?;
let local_site_data = local_site_data_cached(&mut context.pool()).await?; let local_site_data = local_site_data_cached(&mut context.pool()).await?;
let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);