Merge branch 'main' into more-url-validation

This commit is contained in:
Felix Ableitner 2025-01-21 13:01:02 +01:00
commit 686da0f03a
5 changed files with 49 additions and 5 deletions

View file

@ -9,6 +9,15 @@ steps:
when:
- event: pull_request
cargo_shear:
image: *rust_nightly_image
commands:
- *install_binstall
- cargo binstall -y cargo-shear
- cargo shear
when:
- event: pull_request
cargo_clippy:
image: *rust_image
environment:

View file

@ -93,7 +93,6 @@ tower = { version = "0.4.13", optional = true }
[dev-dependencies]
anyhow = "1.0.86"
axum = { version = "0.7.5", features = ["macros"] }
axum-extra = { version = "0.9.3", features = ["typed-header"] }
env_logger = "0.11.3"
tokio = { version = "1.38.0", features = ["full"] }

View file

@ -360,7 +360,7 @@ const _IMPL_DIESEL_NEW_TYPE_FOR_OBJECT_ID: () = {
}
};
/// Shared test code
/// Internal only
#[cfg(test)]
#[allow(clippy::unwrap_used)]
pub mod tests {

View file

@ -277,7 +277,7 @@ pub(crate) fn verify_body_hash(
Ok(())
}
/// Shared test code
/// Internal only
#[cfg(test)]
#[allow(clippy::unwrap_used)]
pub mod test {
@ -379,7 +379,7 @@ pub mod test {
assert_eq!(invalid, Err(Error::ActivityBodyDigestInvalid));
}
/// Return hardcoded keypair
/// Internal only, return hardcoded keypair for testing
pub fn test_keypair() -> Keypair {
let rsa = RsaPrivateKey::from_pkcs1_pem(PRIVATE_KEY).unwrap();
let pkey = RsaPublicKey::from(&rsa);

View file

@ -1,6 +1,7 @@
//! Verify that received data is valid
use crate::error::Error;
use crate::{config::Data, error::Error, fetch::object_id::ObjectId, traits::Object};
use serde::Deserialize;
use url::Url;
/// Check that both urls have the same domain. If not, return UrlVerificationError.
@ -36,3 +37,38 @@ pub fn verify_urls_match(a: &Url, b: &Url) -> Result<(), Error> {
}
Ok(())
}
/// Check that the given ID doesn't match the local domain.
///
/// It is important to verify this to avoid local objects from being overwritten. In general
/// locally created objects should be considered authorative, while incoming federated data
/// is untrusted. Lack of such a check could allow an attacker to rewrite local posts. It could
/// also result in an `object.local` field being overwritten with `false` for local objects, resulting in invalid data.
///
/// ```
/// # use activitypub_federation::fetch::object_id::ObjectId;
/// # use activitypub_federation::config::FederationConfig;
/// # use activitypub_federation::protocol::verification::verify_is_remote_object;
/// # use activitypub_federation::traits::tests::{DbConnection, DbUser};
/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
/// # let config = FederationConfig::builder().domain("example.com").app_data(DbConnection).build().await?;
/// # let data = config.to_request_data();
/// let id = ObjectId::<DbUser>::parse("https://remote.com/u/name")?;
/// assert!(verify_is_remote_object(&id, &data).is_ok());
/// # Ok::<(), anyhow::Error>(())
/// # }).unwrap();
/// ```
pub fn verify_is_remote_object<Kind, R: Clone>(
id: &ObjectId<Kind>,
data: &Data<<Kind as Object>::DataType>,
) -> Result<(), Error>
where
Kind: Object<DataType = R> + Send + 'static,
for<'de2> <Kind as Object>::Kind: Deserialize<'de2>,
{
if id.is_local(data) {
Err(Error::UrlVerificationError("Object is not remote"))
} else {
Ok(())
}
}