From c022e0d320bf1ac72253a4812186712f1ad2cb75 Mon Sep 17 00:00:00 2001 From: silverpill Date: Tue, 11 Apr 2023 21:30:34 +0000 Subject: [PATCH] Add actor validation to Update(Note) and Undo(Follow) handlers --- CHANGELOG.md | 1 + src/activitypub/handlers/create.rs | 14 ++++++++++---- src/activitypub/handlers/undo.rs | 3 +++ src/activitypub/handlers/update.rs | 15 +++++++++++++-- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6afacad..66147d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Added emoji count check to profile data validator. - Check mention and link counts when creating post. - Re-fetch object if `attributedTo` value doesn't match `actor` of `Create` activity. +- Added actor validation to `Update(Note)` and `Undo(Follow)` handlers. ## [1.20.0] - 2023-03-07 diff --git a/src/activitypub/handlers/create.rs b/src/activitypub/handlers/create.rs index 0fa5359..741923f 100644 --- a/src/activitypub/handlers/create.rs +++ b/src/activitypub/handlers/create.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use chrono::Utc; +use serde::Deserialize; use serde_json::{Value as JsonValue}; use uuid::Uuid; @@ -676,14 +677,21 @@ async fn is_unsolicited_message( Ok(result) } +#[derive(Deserialize)] +struct CreateNote { + actor: String, + object: Object, +} + pub async fn handle_create( config: &Config, db_client: &mut impl DatabaseClient, activity: JsonValue, mut is_authenticated: bool, ) -> HandlerResult { - let object: Object = serde_json::from_value(activity["object"].to_owned()) + let activity: CreateNote = serde_json::from_value(activity) .map_err(|_| ValidationError("invalid object"))?; + let object = activity.object; let instance = config.instance(); if is_unsolicited_message(db_client, &instance.url(), &object).await? { @@ -692,10 +700,8 @@ pub async fn handle_create( }; // Verify attribution - let actor_id = activity["actor"].as_str() - .ok_or(ValidationError("actor property is missing"))?; let author_id = get_object_attributed_to(&object)?; - if actor_id != author_id { + if author_id != activity.actor { log::warn!("attributedTo value doesn't match actor"); is_authenticated = false; // Object will be fetched }; diff --git a/src/activitypub/handlers/undo.rs b/src/activitypub/handlers/undo.rs index 2bf298c..561cd6e 100644 --- a/src/activitypub/handlers/undo.rs +++ b/src/activitypub/handlers/undo.rs @@ -91,6 +91,9 @@ pub async fn handle_undo( match get_follow_request_by_activity_id(db_client, &activity.object).await { Ok(follow_request) => { // Undo(Follow) + if follow_request.source_id != actor_profile.id { + return Err(ValidationError("actor is not a follower").into()); + }; unfollow( db_client, &follow_request.source_id, diff --git a/src/activitypub/handlers/update.rs b/src/activitypub/handlers/update.rs index 03d48d7..97fd42b 100644 --- a/src/activitypub/handlers/update.rs +++ b/src/activitypub/handlers/update.rs @@ -27,6 +27,7 @@ use crate::activitypub::{ get_object_tags, get_object_url, }, + identifiers::profile_actor_id, types::Object, vocabulary::{NOTE, PERSON}, }; @@ -35,13 +36,20 @@ use crate::media::MediaStorage; use super::HandlerResult; +#[derive(Deserialize)] +struct UpdateNote { + actor: String, + object: Object, +} + async fn handle_update_note( config: &Config, db_client: &mut impl DatabaseClient, activity: Value, ) -> HandlerResult { - let object: Object = serde_json::from_value(activity["object"].to_owned()) + let activity: UpdateNote = serde_json::from_value(activity) .map_err(|_| ValidationError("invalid object"))?; + let object = activity.object; let post = match get_post_by_remote_object_id( db_client, &object.id, @@ -51,13 +59,16 @@ async fn handle_update_note( Err(DatabaseError::NotFound(_)) => return Ok(None), Err(other_error) => return Err(other_error.into()), }; + let instance = config.instance(); + if profile_actor_id(&instance.url(), &post.author) != activity.actor { + return Err(ValidationError("actor is not an author").into()); + }; let mut content = get_object_content(&object)?; if object.object_type != NOTE { // Append link to object let object_url = get_object_url(&object)?; content += &create_content_link(object_url); }; - let instance = config.instance(); let storage = MediaStorage::from(config); let (attachments, unprocessed) = get_object_attachments( db_client,