Add actor validation to Update(Note) and Undo(Follow) handlers

This commit is contained in:
silverpill 2023-04-11 21:30:34 +00:00 committed by Rafael Caricio
parent b6abcf252a
commit c022e0d320
Signed by: rafaelcaricio
GPG key ID: 3C86DBCE8E93C947
4 changed files with 27 additions and 6 deletions

View file

@ -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. - Added emoji count check to profile data validator.
- Check mention and link counts when creating post. - Check mention and link counts when creating post.
- Re-fetch object if `attributedTo` value doesn't match `actor` of `Create` activity. - 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 ## [1.20.0] - 2023-03-07

View file

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use chrono::Utc; use chrono::Utc;
use serde::Deserialize;
use serde_json::{Value as JsonValue}; use serde_json::{Value as JsonValue};
use uuid::Uuid; use uuid::Uuid;
@ -676,14 +677,21 @@ async fn is_unsolicited_message(
Ok(result) Ok(result)
} }
#[derive(Deserialize)]
struct CreateNote {
actor: String,
object: Object,
}
pub async fn handle_create( pub async fn handle_create(
config: &Config, config: &Config,
db_client: &mut impl DatabaseClient, db_client: &mut impl DatabaseClient,
activity: JsonValue, activity: JsonValue,
mut is_authenticated: bool, mut is_authenticated: bool,
) -> HandlerResult { ) -> 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"))?; .map_err(|_| ValidationError("invalid object"))?;
let object = activity.object;
let instance = config.instance(); let instance = config.instance();
if is_unsolicited_message(db_client, &instance.url(), &object).await? { if is_unsolicited_message(db_client, &instance.url(), &object).await? {
@ -692,10 +700,8 @@ pub async fn handle_create(
}; };
// Verify attribution // Verify attribution
let actor_id = activity["actor"].as_str()
.ok_or(ValidationError("actor property is missing"))?;
let author_id = get_object_attributed_to(&object)?; 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"); log::warn!("attributedTo value doesn't match actor");
is_authenticated = false; // Object will be fetched is_authenticated = false; // Object will be fetched
}; };

View file

@ -91,6 +91,9 @@ pub async fn handle_undo(
match get_follow_request_by_activity_id(db_client, &activity.object).await { match get_follow_request_by_activity_id(db_client, &activity.object).await {
Ok(follow_request) => { Ok(follow_request) => {
// Undo(Follow) // Undo(Follow)
if follow_request.source_id != actor_profile.id {
return Err(ValidationError("actor is not a follower").into());
};
unfollow( unfollow(
db_client, db_client,
&follow_request.source_id, &follow_request.source_id,

View file

@ -27,6 +27,7 @@ use crate::activitypub::{
get_object_tags, get_object_tags,
get_object_url, get_object_url,
}, },
identifiers::profile_actor_id,
types::Object, types::Object,
vocabulary::{NOTE, PERSON}, vocabulary::{NOTE, PERSON},
}; };
@ -35,13 +36,20 @@ use crate::media::MediaStorage;
use super::HandlerResult; use super::HandlerResult;
#[derive(Deserialize)]
struct UpdateNote {
actor: String,
object: Object,
}
async fn handle_update_note( async fn handle_update_note(
config: &Config, config: &Config,
db_client: &mut impl DatabaseClient, db_client: &mut impl DatabaseClient,
activity: Value, activity: Value,
) -> HandlerResult { ) -> 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"))?; .map_err(|_| ValidationError("invalid object"))?;
let object = activity.object;
let post = match get_post_by_remote_object_id( let post = match get_post_by_remote_object_id(
db_client, db_client,
&object.id, &object.id,
@ -51,13 +59,16 @@ async fn handle_update_note(
Err(DatabaseError::NotFound(_)) => return Ok(None), Err(DatabaseError::NotFound(_)) => return Ok(None),
Err(other_error) => return Err(other_error.into()), 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)?; let mut content = get_object_content(&object)?;
if object.object_type != NOTE { if object.object_type != NOTE {
// Append link to object // Append link to object
let object_url = get_object_url(&object)?; let object_url = get_object_url(&object)?;
content += &create_content_link(object_url); content += &create_content_link(object_url);
}; };
let instance = config.instance();
let storage = MediaStorage::from(config); let storage = MediaStorage::from(config);
let (attachments, unprocessed) = get_object_attachments( let (attachments, unprocessed) = get_object_attachments(
db_client, db_client,