Make actor ID and object ID parsers more strict

This commit is contained in:
silverpill 2021-09-20 21:45:33 +00:00
parent 602e4ec229
commit 087a077f7d

View file

@ -17,7 +17,8 @@ use crate::models::profiles::types::ProfileUpdateData;
use crate::models::relationships::queries::{ use crate::models::relationships::queries::{
follow_request_accepted, follow_request_accepted,
follow_request_rejected, follow_request_rejected,
follow, unfollow, follow,
unfollow,
}; };
use crate::models::users::queries::get_user_by_id; use crate::models::users::queries::get_user_by_id;
use super::activity::{Object, Activity, create_activity_accept_follow}; use super::activity::{Object, Activity, create_activity_accept_follow};
@ -26,8 +27,16 @@ use super::deliverer::deliver_activity;
use super::fetcher::{fetch_avatar_and_banner, fetch_attachment}; use super::fetcher::{fetch_avatar_and_banner, fetch_attachment};
use super::vocabulary::*; use super::vocabulary::*;
fn parse_actor_id(actor_id: &str) -> Result<String, ValidationError> { fn parse_actor_id(
let url_regexp = Regex::new(r"^https?://.+/users/(?P<username>[0-9a-z_]+)$").unwrap(); instance_url: &str,
actor_id: &str,
) -> Result<String, ValidationError> {
let url_regexp_str = format!(
"^{}/users/(?P<username>[0-9a-z_]+)$",
instance_url.replace(".", r"\."),
);
let url_regexp = Regex::new(&url_regexp_str)
.map_err(|_| ValidationError("error"))?;
let url_caps = url_regexp.captures(&actor_id) let url_caps = url_regexp.captures(&actor_id)
.ok_or(ValidationError("invalid actor ID"))?; .ok_or(ValidationError("invalid actor ID"))?;
let username = url_caps.name("username") let username = url_caps.name("username")
@ -37,8 +46,16 @@ fn parse_actor_id(actor_id: &str) -> Result<String, ValidationError> {
Ok(username) Ok(username)
} }
fn parse_object_id(object_id: &str) -> Result<Uuid, ValidationError> { fn parse_object_id(
let url_regexp = Regex::new(r"^https?://.+/objects/(?P<uuid>[0-9a-f-]+)$").unwrap(); instance_url: &str,
object_id: &str,
) -> Result<Uuid, ValidationError> {
let url_regexp_str = format!(
"^{}/objects/(?P<uuid>[0-9a-f-]+)$",
instance_url.replace(".", r"\."),
);
let url_regexp = Regex::new(&url_regexp_str)
.map_err(|_| ValidationError("error"))?;
let url_caps = url_regexp.captures(&object_id) let url_caps = url_regexp.captures(&object_id)
.ok_or(ValidationError("invalid object ID"))?; .ok_or(ValidationError("invalid object ID"))?;
let object_uuid: Uuid = url_caps.name("uuid") let object_uuid: Uuid = url_caps.name("uuid")
@ -66,14 +83,13 @@ pub async fn receive_activity(
(ACCEPT, FOLLOW) => { (ACCEPT, FOLLOW) => {
let object: Object = serde_json::from_value(activity.object) let object: Object = serde_json::from_value(activity.object)
.map_err(|_| ValidationError("invalid object"))?; .map_err(|_| ValidationError("invalid object"))?;
// TODO: reject if object ID contains wrong instance URI let follow_request_id = parse_object_id(&config.instance_url(), &object.id)?;
let follow_request_id = parse_object_id(&object.id)?;
follow_request_accepted(db_client, &follow_request_id).await?; follow_request_accepted(db_client, &follow_request_id).await?;
}, },
(REJECT, FOLLOW) => { (REJECT, FOLLOW) => {
let object: Object = serde_json::from_value(activity.object) let object: Object = serde_json::from_value(activity.object)
.map_err(|_| ValidationError("invalid object"))?; .map_err(|_| ValidationError("invalid object"))?;
let follow_request_id = parse_object_id(&object.id)?; let follow_request_id = parse_object_id(&config.instance_url(), &object.id)?;
follow_request_rejected(db_client, &follow_request_id).await?; follow_request_rejected(db_client, &follow_request_id).await?;
}, },
(CREATE, NOTE) => { (CREATE, NOTE) => {
@ -118,8 +134,7 @@ pub async fn receive_activity(
.map_err(|_| HttpError::InternalError)?; .map_err(|_| HttpError::InternalError)?;
let target_actor_id = activity.object.as_str() let target_actor_id = activity.object.as_str()
.ok_or(ValidationError("invalid object"))?; .ok_or(ValidationError("invalid object"))?;
// TODO: reject if object ID contains wrong instance URI let target_username = parse_actor_id(&config.instance_url(), &target_actor_id)?;
let target_username = parse_actor_id(&target_actor_id)?;
let target_profile = get_profile_by_acct(db_client, &target_username).await?; let target_profile = get_profile_by_acct(db_client, &target_username).await?;
// Create and send 'Accept' activity // Create and send 'Accept' activity
let target_user = get_user_by_id(db_client, &target_profile.id).await?; let target_user = get_user_by_id(db_client, &target_profile.id).await?;
@ -145,8 +160,7 @@ pub async fn receive_activity(
let source_profile = get_profile_by_actor_id(db_client, &activity.actor).await?; let source_profile = get_profile_by_actor_id(db_client, &activity.actor).await?;
let target_actor_id = object.object let target_actor_id = object.object
.ok_or(ValidationError("invalid object"))?; .ok_or(ValidationError("invalid object"))?;
// TODO: reject if actor ID contains wrong instance URI let target_username = parse_actor_id(&config.instance_url(), &target_actor_id)?;
let target_username = parse_actor_id(&target_actor_id)?;
let target_profile = get_profile_by_acct(db_client, &target_username).await?; let target_profile = get_profile_by_acct(db_client, &target_username).await?;
unfollow(db_client, &source_profile.id, &target_profile.id).await?; unfollow(db_client, &source_profile.id, &target_profile.id).await?;
}, },
@ -185,21 +199,29 @@ pub async fn receive_activity(
mod tests { mod tests {
use super::*; use super::*;
const INSTANCE_URL: &str = "https://example.org";
#[test] #[test]
fn test_parse_actor_id() { fn test_parse_actor_id() {
let username = parse_actor_id("https://example.org/users/test").unwrap(); let username = parse_actor_id(INSTANCE_URL, "https://example.org/users/test").unwrap();
assert_eq!(username, "test".to_string()); assert_eq!(username, "test".to_string());
} }
#[test] #[test]
fn test_parse_actor_id_wrong_path() { fn test_parse_actor_id_wrong_path() {
let error = parse_actor_id("https://example.org/user/test").unwrap_err(); let error = parse_actor_id(INSTANCE_URL, "https://example.org/user/test").unwrap_err();
assert_eq!(error.to_string(), "invalid actor ID"); assert_eq!(error.to_string(), "invalid actor ID");
} }
#[test] #[test]
fn test_parse_actor_id_invalid_username() { fn test_parse_actor_id_invalid_username() {
let error = parse_actor_id("https://example.org/users/tes-t").unwrap_err(); let error = parse_actor_id(INSTANCE_URL, "https://example.org/users/tes-t").unwrap_err();
assert_eq!(error.to_string(), "invalid actor ID");
}
#[test]
fn test_parse_actor_id_invalid_instance_url() {
let error = parse_actor_id(INSTANCE_URL, "https://example.gov/users/test").unwrap_err();
assert_eq!(error.to_string(), "invalid actor ID"); assert_eq!(error.to_string(), "invalid actor ID");
} }
@ -210,13 +232,14 @@ mod tests {
"https://example.org/objects/{}", "https://example.org/objects/{}",
expected_uuid, expected_uuid,
); );
let object_uuid = parse_object_id(&object_id).unwrap(); let object_uuid = parse_object_id(INSTANCE_URL, &object_id).unwrap();
assert_eq!(object_uuid, expected_uuid); assert_eq!(object_uuid, expected_uuid);
} }
#[test] #[test]
fn test_parse_object_id_invalid_uuid() { fn test_parse_object_id_invalid_uuid() {
let error = parse_object_id("https://example.org/objects/1234").unwrap_err(); let object_id = "https://example.org/objects/1234";
let error = parse_object_id(INSTANCE_URL, object_id).unwrap_err();
assert_eq!(error.to_string(), "invalid object ID"); assert_eq!(error.to_string(), "invalid object ID");
} }
} }