From d8b53c7c9393b7e7ed7731851f7514eb33890d93 Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 30 May 2024 23:22:58 +0200 Subject: [PATCH] fix: make sure we're fetching what id claims also configurable max thread depth --- src/config.rs | 6 ++++++ src/server/fetcher.rs | 45 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/config.rs b/src/config.rs index 5ebd2476..a040a305 100644 --- a/src/config.rs +++ b/src/config.rs @@ -73,6 +73,12 @@ pub struct SecurityConfig { #[serde(default)] pub allow_login_refresh: bool, + + #[serde_inline_default(2)] + pub max_id_redirects: u32, + + #[serde_inline_default(20)] + pub thread_crawl_depth: u32, } diff --git a/src/server/fetcher.rs b/src/server/fetcher.rs index 82229b93..f8928416 100644 --- a/src/server/fetcher.rs +++ b/src/server/fetcher.rs @@ -9,12 +9,21 @@ use crate::{errors::UpubError, model, VERSION}; use super::{addresser::Addresser, httpsign::HttpSignature, normalizer::Normalizer, Context}; +#[derive(Debug, Clone)] pub enum PullResult { Actor(T), Activity(T), Object(T), } +impl PullResult { + pub fn inner(self) -> T { + match self { + Self::Actor(x) | Self::Activity(x) | Self::Object(x) => x + } + } +} + impl PullResult { pub fn actor(self) -> crate::Result { match self { @@ -39,11 +48,23 @@ impl PullResult { Self::Object(x) => Ok(x), } } + + pub async fn resolve(self, ctx: &(impl Fetcher + Sync)) -> crate::Result<()> { + match self { + Self::Actor(x) => { ctx.resolve_user(x).await?; }, + Self::Object(x) => { ctx.resolve_object(x).await?; }, + Self::Activity(x) => { ctx.resolve_activity(x).await?; }, + } + Ok(()) + } } #[axum::async_trait] pub trait Fetcher { - async fn pull(&self, id: &str) -> crate::Result>; + async fn pull(&self, id: &str) -> crate::Result> { self.pull_r(id, 0).await } + async fn pull_r(&self, id: &str, depth: i32) -> crate::Result>; + + async fn webfinger(&self, user: &str, host: &str) -> crate::Result; async fn fetch_domain(&self, domain: &str) -> crate::Result; @@ -57,8 +78,8 @@ pub trait Fetcher { async fn fetch_object(&self, id: &str) -> crate::Result { self.fetch_object_r(id, 0).await } #[allow(unused)] async fn resolve_object(&self, object: serde_json::Value) -> crate::Result { self.resolve_object_r(object, 0).await } - async fn fetch_object_r(&self, id: &str, depth: i32) -> crate::Result; - async fn resolve_object_r(&self, object: serde_json::Value, depth: i32) -> crate::Result; + async fn fetch_object_r(&self, id: &str, depth: u32) -> crate::Result; + async fn resolve_object_r(&self, object: serde_json::Value, depth: u32) -> crate::Result; async fn fetch_thread(&self, id: &str) -> crate::Result<()>; @@ -122,7 +143,7 @@ pub trait Fetcher { #[axum::async_trait] impl Fetcher for Context { - async fn pull(&self, id: &str) -> crate::Result> { + async fn pull_r(&self, id: &str, depth: u32) -> crate::Result> { let _domain = self.fetch_domain(&Context::server(id)).await?; let document = Self::request( @@ -133,6 +154,14 @@ impl Fetcher for Context { .json::() .await?; + let doc_id = document.id().ok_or_else(|| UpubError::field("id"))?; + if id != doc_id { + if depth >= self.cfg().security.max_id_redirects { + return Err(UpubError::unprocessable()); + } + return self.pull(doc_id).await; + } + match document.object_type() { None => Err(UpubError::bad_request()), Some(apb::ObjectType::Collection(_)) => Err(UpubError::unprocessable()), @@ -321,7 +350,7 @@ impl Fetcher for Context { todo!() } - async fn fetch_object_r(&self, id: &str, depth: i32) -> crate::Result { + async fn fetch_object_r(&self, id: &str, depth: u32) -> crate::Result { if let Some(x) = model::object::Entity::find_by_ap_id(id).one(self.db()).await? { return Ok(x); // already in db, easy } @@ -331,7 +360,7 @@ impl Fetcher for Context { self.resolve_object_r(object, depth).await } - async fn resolve_object_r(&self, object: serde_json::Value, depth: i32) -> crate::Result { + async fn resolve_object_r(&self, object: serde_json::Value, depth: u32) -> crate::Result { let id = object.id().ok_or_else(|| UpubError::field("id"))?.to_string(); if let Some(oid) = object.id() { @@ -351,10 +380,10 @@ impl Fetcher for Context { let addressed = object.addressed(); if let Some(reply) = object.in_reply_to().id() { - if depth <= 16 { + if depth <= self.cfg().security.thread_crawl_depth { self.fetch_object_r(&reply, depth + 1).await?; } else { - tracing::warn!("thread deeper than 16, giving up fetching more replies"); + tracing::warn!("thread deeper than {}, giving up fetching more replies", self.cfg().security.thread_crawl_depth); } }