Improved url verification, explicit debug mode
This commit is contained in:
parent
318995b456
commit
73d34d8011
7 changed files with 59 additions and 15 deletions
15
.drone.yml
15
.drone.yml
|
@ -14,20 +14,27 @@ steps:
|
|||
- /root/.cargo/bin/cargo fmt -- --check
|
||||
|
||||
- name: cargo check
|
||||
image: clux/muslrust:1.59.0
|
||||
image: rust:1.61-bullseye
|
||||
commands:
|
||||
- cargo check
|
||||
- cargo check --all --all-targets
|
||||
|
||||
- name: cargo clippy
|
||||
image: clux/muslrust:1.59.0
|
||||
image: rust:1.61-bullseye
|
||||
commands:
|
||||
- rustup component add clippy
|
||||
- cargo clippy --workspace --tests --all-targets --all-features -- -D warnings -D deprecated -D clippy::perf -D clippy::complexity -D clippy::dbg_macro
|
||||
- cargo clippy --workspace -- -D clippy::unwrap_used
|
||||
|
||||
- name: cargo test
|
||||
image: clux/muslrust:1.59.0
|
||||
image: rust:1.61-bullseye
|
||||
environment:
|
||||
RUST_BACKTRACE: 1
|
||||
commands:
|
||||
- cargo test --workspace --no-fail-fast
|
||||
|
||||
- name: cargo run
|
||||
image: rust:1.61-bullseye
|
||||
environment:
|
||||
RUST_BACKTRACE: 1
|
||||
commands:
|
||||
- cargo run -p activitypub_federation --example federation
|
||||
|
|
|
@ -40,7 +40,7 @@ pub struct Instance {
|
|||
impl Instance {
|
||||
pub fn new(hostname: String) -> Result<InstanceHandle, Error> {
|
||||
let settings = InstanceSettingsBuilder::default()
|
||||
.testing_send_sync(true)
|
||||
.debug(true)
|
||||
.worker_count(1)
|
||||
.build()?;
|
||||
let local_instance =
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
core::signatures::{sign_request, PublicKey},
|
||||
utils::verify_url_valid,
|
||||
Error,
|
||||
LocalInstance,
|
||||
APUB_JSON_CONTENT_TYPE,
|
||||
|
@ -44,6 +45,9 @@ impl SendActivity {
|
|||
pub async fn send(self, instance: &LocalInstance) -> Result<(), Error> {
|
||||
let activity_queue = &instance.activity_queue;
|
||||
for inbox in self.inboxes {
|
||||
if verify_url_valid(&inbox, &instance.settings).is_err() {
|
||||
continue;
|
||||
}
|
||||
let message = SendActivityTask {
|
||||
activity_id: self.activity_id.clone(),
|
||||
inbox,
|
||||
|
@ -51,7 +55,7 @@ impl SendActivity {
|
|||
public_key: self.actor_public_key.clone(),
|
||||
private_key: self.actor_private_key.clone(),
|
||||
};
|
||||
if instance.settings.testing_send_sync {
|
||||
if instance.settings.debug {
|
||||
let res =
|
||||
do_send(message, &instance.client, instance.settings.request_timeout).await;
|
||||
// Don't fail on error, as we intentionally do some invalid actions in tests, to verify that
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
|||
core::{object_id::ObjectId, signatures::verify_signature},
|
||||
data::Data,
|
||||
traits::{ActivityHandler, ApubObject},
|
||||
utils::verify_domains_match,
|
||||
utils::{verify_domains_match, verify_url_valid},
|
||||
Error,
|
||||
LocalInstance,
|
||||
};
|
||||
|
@ -29,13 +29,11 @@ where
|
|||
E: From<anyhow::Error> + From<Error>,
|
||||
{
|
||||
verify_domains_match(activity.id(), activity.actor())?;
|
||||
verify_url_valid(activity.id(), &local_instance.settings)?;
|
||||
if local_instance.is_local_url(activity.id()) {
|
||||
return Err(Error::UrlVerificationError("Activity was sent from local instance").into());
|
||||
}
|
||||
|
||||
(local_instance.settings.verify_url_function)(activity.id())
|
||||
.map_err(Error::UrlVerificationError)?;
|
||||
|
||||
let request_counter = &mut 0;
|
||||
let actor = ObjectId::<Actor>::new(activity.actor().clone())
|
||||
.dereference::<E>(data, local_instance, request_counter)
|
||||
|
|
|
@ -159,7 +159,7 @@ where
|
|||
Kind: ApubObject + Send + 'static,
|
||||
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
|
||||
{
|
||||
#[allow(clippy::to_string_in_display)]
|
||||
#[allow(clippy::recursive_format_impl)]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
// Use to_string here because Url.display is not useful for us
|
||||
write!(f, "{}", self.0)
|
||||
|
|
|
@ -32,10 +32,12 @@ pub struct InstanceSettings {
|
|||
/// Number of worker threads for sending outgoing activities
|
||||
#[builder(default = "64")]
|
||||
worker_count: u64,
|
||||
/// Send outgoing activities synchronously, not in background thread. Helps to make tests
|
||||
/// more consistent, but not recommended for production.
|
||||
/// Run library in debug mode. This allows usage of http and localhost urls. It also sends
|
||||
/// outgoing activities synchronously, not in background thread. This helps to make tests
|
||||
/// more consistent.
|
||||
/// Do not use for production.
|
||||
#[builder(default = "false")]
|
||||
testing_send_sync: bool,
|
||||
debug: bool,
|
||||
/// Timeout for all HTTP requests. HTTP signatures are valid for 10s, so it makes sense to
|
||||
/// use the same as timeout when sending
|
||||
#[builder(default = "Duration::from_secs(10)")]
|
||||
|
|
35
src/utils.rs
35
src/utils.rs
|
@ -1,4 +1,4 @@
|
|||
use crate::{Error, LocalInstance, APUB_JSON_CONTENT_TYPE};
|
||||
use crate::{Error, InstanceSettings, LocalInstance, APUB_JSON_CONTENT_TYPE};
|
||||
use http::StatusCode;
|
||||
use serde::de::DeserializeOwned;
|
||||
use tracing::log::info;
|
||||
|
@ -11,6 +11,7 @@ pub async fn fetch_object_http<Kind: DeserializeOwned>(
|
|||
) -> Result<Kind, Error> {
|
||||
// dont fetch local objects this way
|
||||
debug_assert!(url.domain() != Some(&instance.hostname));
|
||||
verify_url_valid(url, &instance.settings)?;
|
||||
info!("Fetching remote object {}", url.to_string());
|
||||
|
||||
*request_counter += 1;
|
||||
|
@ -49,3 +50,35 @@ pub fn verify_urls_match(a: &Url, b: &Url) -> Result<(), Error> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform some security checks on URLs as mentioned in activitypub spec, and call user-supplied
|
||||
/// [`InstanceSettings.verify_url_function`].
|
||||
///
|
||||
/// https://www.w3.org/TR/activitypub/#security-considerations
|
||||
pub fn verify_url_valid(url: &Url, settings: &InstanceSettings) -> Result<(), Error> {
|
||||
match url.scheme() {
|
||||
"https" => {}
|
||||
"http" => {
|
||||
if !settings.debug {
|
||||
return Err(Error::UrlVerificationError(
|
||||
"Http urls are only allowed in debug mode",
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::UrlVerificationError("Invalid url scheme")),
|
||||
};
|
||||
|
||||
if url.domain().is_none() {
|
||||
return Err(Error::UrlVerificationError("Url must have a domain"));
|
||||
}
|
||||
|
||||
if url.domain() == Some("localhost") && !settings.debug {
|
||||
return Err(Error::UrlVerificationError(
|
||||
"Localhost is only allowed in debug mode",
|
||||
));
|
||||
}
|
||||
|
||||
(settings.verify_url_function)(url).map_err(Error::UrlVerificationError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue