Compare commits

...

6 commits
0.5.4 ... main

Author SHA1 Message Date
Felix Ableitner 16844f048a Version 0.5.6 2024-05-06 11:09:47 +02:00
Nutomic cf1f84993b
Make response content-type check case insensitive (#111)
* Make response content-type check case insensitive

For wordpress compat

* cleaner

* clippy

* fmt

* fmt
2024-05-06 11:09:23 +02:00
Felix Ableitner 24afad7abc Version 0.5.5 2024-05-02 13:06:22 +02:00
Nutomic c48de9e944
Upgrade dependencies (#110) 2024-05-02 06:58:08 -04:00
Nutomic be69efdee3
Require signed digest when verifying signatures (#109) 2024-05-02 10:58:56 +02:00
Nutomic ddc455510b
Dont crash when calling is_local_url() without domain (#108) 2024-05-02 10:58:33 +02:00
4 changed files with 54 additions and 39 deletions

View file

@ -1,6 +1,6 @@
[package]
name = "activitypub_federation"
version = "0.5.4"
version = "0.5.6"
edition = "2021"
description = "High-level Activitypub framework"
keywords = ["activitypub", "activitystreams", "federation", "fediverse"]
@ -32,21 +32,21 @@ redundant_closure_for_method_calls = "deny"
unwrap_used = "deny"
[dependencies]
chrono = { version = "0.4.34", features = ["clock"], default-features = false }
serde = { version = "1.0.197", features = ["derive"] }
async-trait = "0.1.77"
chrono = { version = "0.4.38", features = ["clock"], default-features = false }
serde = { version = "1.0.200", features = ["derive"] }
async-trait = "0.1.80"
url = { version = "2.5.0", features = ["serde"] }
serde_json = { version = "1.0.114", features = ["preserve_order"] }
reqwest = { version = "0.11.24", features = ["json", "stream"] }
reqwest-middleware = "0.2.4"
serde_json = { version = "1.0.116", features = ["preserve_order"] }
reqwest = { version = "0.11.27", features = ["json", "stream"] }
reqwest-middleware = "0.2.5"
tracing = "0.1.40"
base64 = "0.21.7"
base64 = "0.22.1"
openssl = "0.10.64"
once_cell = "1.19.0"
http = "0.2.11"
http = "0.2.12"
sha2 = "0.10.8"
thiserror = "1.0.57"
derive_builder = "0.12.0"
thiserror = "1.0.59"
derive_builder = "0.20.0"
itertools = "0.12.1"
dyn-clone = "1.0.17"
enum_delegate = "0.2.0"
@ -57,20 +57,20 @@ http-signature-normalization-reqwest = { version = "0.10.0", default-features =
"default-spawner",
] }
http-signature-normalization = "0.7.0"
bytes = "1.5.0"
bytes = "1.6.0"
futures-core = { version = "0.3.30", default-features = false }
pin-project-lite = "0.2.13"
pin-project-lite = "0.2.14"
activitystreams-kinds = "0.3.0"
regex = { version = "1.10.3", default-features = false, features = ["std", "unicode-case"] }
tokio = { version = "1.36.0", features = [
regex = { version = "1.10.4", default-features = false, features = ["std", "unicode-case"] }
tokio = { version = "1.37.0", features = [
"sync",
"rt",
"rt-multi-thread",
"time",
] }
diesel = { version = "2.1.4", features = ["postgres"], default-features = false, optional = true }
diesel = { version = "2.1.6", features = ["postgres"], default-features = false, optional = true }
futures = "0.3.30"
moka = { version = "0.12.5", features = ["future"] }
moka = { version = "0.12.7", features = ["future"] }
# Actix-web
actix-web = { version = "4.5.1", default-features = false, optional = true }
@ -82,12 +82,12 @@ axum = { version = "0.6.20", features = [
], default-features = false, optional = true }
tower = { version = "0.4.13", optional = true }
hyper = { version = "0.14", optional = true }
http-body-util = {version = "0.1.0", optional = true }
http-body-util = {version = "0.1.1", optional = true }
[dev-dependencies]
anyhow = "1.0.80"
anyhow = "1.0.82"
rand = "0.8.5"
env_logger = "0.10.2"
env_logger = "0.11.3"
tower-http = { version = "0.5.2", features = ["map-request-body", "util"] }
axum = { version = "0.6.20", features = [
"http1",
@ -95,7 +95,7 @@ axum = { version = "0.6.20", features = [
"query",
], default-features = false }
axum-macros = "0.3.8"
tokio = { version = "1.36.0", features = ["full"] }
tokio = { version = "1.37.0", features = ["full"] }
[profile.dev]
strip = "symbols"

View file

@ -174,11 +174,17 @@ impl<T: Clone> FederationConfig<T> {
/// Returns true if the url refers to this instance. Handles hostnames like `localhost:8540` for
/// local debugging.
pub(crate) fn is_local_url(&self, url: &Url) -> bool {
let mut domain = url.host_str().expect("id has domain").to_string();
if let Some(port) = url.port() {
domain = format!("{}:{}", domain, port);
match url.host_str() {
Some(domain) => {
let domain = if let Some(port) = url.port() {
format!("{}:{}", domain, port)
} else {
domain.to_string()
};
domain == self.domain
}
None => false,
}
domain == self.domain
}
/// Returns the local domain
@ -355,13 +361,17 @@ mod test {
.await
.unwrap()
}
#[tokio::test]
async fn test_url_is_local() -> Result<(), Error> {
let config = config().await;
assert!(config.is_local_url(&Url::parse("http://example.com")?));
assert!(!config.is_local_url(&Url::parse("http://other.com")?));
// ensure that missing domain doesnt cause crash
assert!(!config.is_local_url(&Url::parse("http://127.0.0.1")?));
Ok(())
}
#[tokio::test]
async fn test_get_domain() {
let config = config().await;

View file

@ -53,19 +53,21 @@ pub async fn fetch_object_http<T: Clone, Kind: DeserializeOwned>(
url: &Url,
data: &Data<T>,
) -> Result<FetchObjectResponse<Kind>, Error> {
static CONTENT_TYPE: HeaderValue = HeaderValue::from_static(FEDERATION_CONTENT_TYPE);
static ALT_CONTENT_TYPE: HeaderValue = HeaderValue::from_static(
r#"application/ld+json; profile="https://www.w3.org/ns/activitystreams""#,
);
static ALT_CONTENT_TYPE_MASTODON: HeaderValue =
HeaderValue::from_static(r#"application/activity+json; charset=utf-8"#);
let res = fetch_object_http_with_accept(url, data, &CONTENT_TYPE).await?;
static FETCH_CONTENT_TYPE: HeaderValue = HeaderValue::from_static(FEDERATION_CONTENT_TYPE);
const VALID_RESPONSE_CONTENT_TYPES: [&str; 3] = [
FEDERATION_CONTENT_TYPE, // lemmy
r#"application/ld+json; profile="https://www.w3.org/ns/activitystreams""#, // activitypub standard
r#"application/activity+json; charset=utf-8"#, // mastodon
];
let res = fetch_object_http_with_accept(url, data, &FETCH_CONTENT_TYPE).await?;
// Ensure correct content-type to prevent vulnerabilities.
if res.content_type.as_ref() != Some(&CONTENT_TYPE)
&& res.content_type.as_ref() != Some(&ALT_CONTENT_TYPE)
&& res.content_type.as_ref() != Some(&ALT_CONTENT_TYPE_MASTODON)
{
// Ensure correct content-type to prevent vulnerabilities, with case insensitive comparison.
let content_type = res
.content_type
.as_ref()
.and_then(|c| c.to_str().ok())
.ok_or(Error::FetchInvalidContentType(res.url.clone()))?;
if !VALID_RESPONSE_CONTENT_TYPES.contains(&content_type) {
return Err(Error::FetchInvalidContentType(res.url));
}

View file

@ -189,8 +189,11 @@ fn verify_signature_inner(
uri: &Uri,
public_key: &str,
) -> Result<(), Error> {
static CONFIG: Lazy<http_signature_normalization::Config> =
Lazy::new(|| http_signature_normalization::Config::new().set_expiration(EXPIRES_AFTER));
static CONFIG: Lazy<http_signature_normalization::Config> = Lazy::new(|| {
http_signature_normalization::Config::new()
.set_expiration(EXPIRES_AFTER)
.require_digest()
});
let path_and_query = uri.path_and_query().map(PathAndQuery::as_str).unwrap_or("");