diff --git a/Cargo.lock b/Cargo.lock index e18d8ad..3d4e0a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,7 @@ version = 4 name = "activitypub" version = "0.1.0" dependencies = [ + "ammonia", "anyhow", "async-trait", "axum", @@ -260,6 +261,19 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "ammonia" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17e913097e1a2124b46746c980134e8c954bc17a6a59bb3fde96f088d126dde6" +dependencies = [ + "cssparser", + "html5ever", + "maplit", + "tendril", + "url", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -1236,6 +1250,29 @@ dependencies = [ "phf", ] +[[package]] +name = "cssparser" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e901edd733a1472f944a45116df3f846f54d37e67e68640ac8bb69689aca2aa" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "csv" version = "1.4.0" @@ -1550,6 +1587,21 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + [[package]] name = "dunce" version = "1.0.5" @@ -1903,6 +1955,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" version = "0.3.32" @@ -2205,6 +2267,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "html5ever" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55d958c2f74b664487a2035fe1dadb032c48718a03b63f3ab0b8537db8549ed4" +dependencies = [ + "log", + "markup5ever", + "match_token", +] + [[package]] name = "http" version = "0.2.12" @@ -2965,6 +3038,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + [[package]] name = "mac_address" version = "1.1.8" @@ -2975,6 +3054,34 @@ dependencies = [ "winapi", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "markup5ever" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "311fe69c934650f8f19652b3946075f0fc41ad8757dbb68f1ca14e7900ecc1c3" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "match_token" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "matchers" version = "0.2.0" @@ -3791,6 +3898,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "presentation" version = "0.1.0" @@ -5245,6 +5358,31 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + [[package]] name = "stringprep" version = "0.1.5" @@ -5401,6 +5539,17 @@ dependencies = [ "uuid", ] +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + [[package]] name = "terminfo" version = "0.9.0" @@ -5999,6 +6148,12 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -6315,6 +6470,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + [[package]] name = "webp" version = "0.3.1" diff --git a/crates/adapters/activitypub/Cargo.toml b/crates/adapters/activitypub/Cargo.toml index b5c4e3d..4ada70a 100644 --- a/crates/adapters/activitypub/Cargo.toml +++ b/crates/adapters/activitypub/Cargo.toml @@ -16,3 +16,4 @@ tracing = { workspace = true } async-trait = { workspace = true } url = { version = "2", features = ["serde"] } +ammonia = "4" diff --git a/crates/adapters/activitypub/src/composite_handler.rs b/crates/adapters/activitypub/src/composite_handler.rs index 6da92b6..fb5907f 100644 --- a/crates/adapters/activitypub/src/composite_handler.rs +++ b/crates/adapters/activitypub/src/composite_handler.rs @@ -45,7 +45,7 @@ impl ApObjectHandler for CompositeObjectHandler { } else if is_watchlist { self.watchlist.on_create(ap_id, actor_url, object).await } else { - tracing::debug!(ap_id = %ap_id, "ignoring Create for unknown object type"); + tracing::warn!(ap_id = %ap_id, "ignoring Create for unknown object type"); Ok(()) } } diff --git a/crates/adapters/activitypub/src/federation_event_bridge.rs b/crates/adapters/activitypub/src/federation_event_bridge.rs index cc920bb..d1c171c 100644 --- a/crates/adapters/activitypub/src/federation_event_bridge.rs +++ b/crates/adapters/activitypub/src/federation_event_bridge.rs @@ -45,8 +45,8 @@ impl k_ap::EventPublisher for FederationEventBridge { .await .map_err(|e| anyhow::anyhow!(e.to_string())) } - other => { - tracing::debug!("ignoring federation event: {:?}", other); + FederationEvent::DeliveryFailed { inbox, error, .. } => { + tracing::warn!(inbox = %inbox, error = %error, "federation delivery failed permanently"); Ok(()) } } diff --git a/crates/adapters/activitypub/src/review_handler.rs b/crates/adapters/activitypub/src/review_handler.rs index 18cdfe3..d4ab3a8 100644 --- a/crates/adapters/activitypub/src/review_handler.rs +++ b/crates/adapters/activitypub/src/review_handler.rs @@ -77,13 +77,15 @@ impl ApObjectHandler for ReviewObjectHandler { _actor_url: &Url, object: serde_json::Value, ) -> anyhow::Result<()> { - let obj: ReviewObject = match serde_json::from_value(object) { + let mut obj: ReviewObject = match serde_json::from_value(object) { Ok(o) => o, Err(e) => { - tracing::debug!("ignoring unrecognized Create object: {}", e); + tracing::warn!("ignoring unrecognized Create object: {}", e); return Ok(()); } }; + obj.movie_title = ammonia::clean(&obj.movie_title); + obj.comment = obj.comment.map(|c| ammonia::clean(&c)); let actor_url_str = obj.attributed_to.to_string(); let review_id = ReviewId::generate(); @@ -130,13 +132,15 @@ impl ApObjectHandler for ReviewObjectHandler { actor_url: &Url, object: serde_json::Value, ) -> anyhow::Result<()> { - let obj: ReviewObject = match serde_json::from_value(object) { + let mut obj: ReviewObject = match serde_json::from_value(object) { Ok(o) => o, Err(_) => { - tracing::debug!(actor = %actor_url, "ignoring non-review Update activity"); + tracing::warn!(actor = %actor_url, "ignoring non-review Update activity"); return Ok(()); } }; + obj.movie_title = ammonia::clean(&obj.movie_title); + obj.comment = obj.comment.map(|c| ammonia::clean(&c)); if obj.attributed_to != *actor_url { anyhow::bail!("update actor does not match object attributed_to"); diff --git a/crates/adapters/activitypub/src/watchlist_handler.rs b/crates/adapters/activitypub/src/watchlist_handler.rs index 5fdd87c..3c49ded 100644 --- a/crates/adapters/activitypub/src/watchlist_handler.rs +++ b/crates/adapters/activitypub/src/watchlist_handler.rs @@ -24,7 +24,14 @@ impl ApObjectHandler for WatchlistObjectHandler { actor_url: &Url, object: serde_json::Value, ) -> anyhow::Result<()> { - let obj: WatchlistObject = serde_json::from_value(object)?; + let mut obj: WatchlistObject = match serde_json::from_value(object) { + Ok(o) => o, + Err(e) => { + tracing::warn!(ap_id = %ap_id, "ignoring malformed watchlist Create: {}", e); + return Ok(()); + } + }; + obj.movie_title = ammonia::clean(&obj.movie_title); let added_at = obj.published; let entry = RemoteWatchlistEntry { ap_id: ap_id.as_str().to_string(), diff --git a/crates/presentation/src/routes.rs b/crates/presentation/src/routes.rs index 8e65925..b223148 100644 --- a/crates/presentation/src/routes.rs +++ b/crates/presentation/src/routes.rs @@ -12,7 +12,7 @@ pub fn build_router(state: AppState, ap_router: Router) -> Router { let ap_cfg = GovernorConfigBuilder::default() .with_extractor(PeerIp::default()) .expect_connect_info() - .quota_default(per_minute(rate_limit)) + .quota_default(per_minute(rate_limit / 2)) .finish() .unwrap(); let ap_router = ap_router.layer(GovernorLayer::new(ap_cfg));