Software-defined Application Firewall for ActivityPub inboxes.
Find a file
eternal-flame-AD 3ce21511bf
init
Signed-off-by: eternal-flame-AD <yume@yumechi.jp>
2024-10-14 23:31:42 -05:00
src init 2024-10-14 23:31:42 -05:00
.gitignore init 2024-10-14 23:31:42 -05:00
Cargo.lock init 2024-10-14 23:31:42 -05:00
Cargo.toml init 2024-10-14 23:31:42 -05:00
LICENSE init 2024-10-14 23:31:42 -05:00
README.md init 2024-10-14 23:31:42 -05:00

Fedivet

Software-defined Application Firewall for ActivityPub inboxes.

Objective

The goal of this project is to write a dynamic trust evaluation system for ActivityPub inboxes that do not rely on the message content itself by default.

Content-based filtering is simple but usually ineffective against spam and abuse who are actively trying to bypass the filters, they sometimes produce false positives where legitimate messages are blocked. The core reason is hard to fix: machines are not designed to understand human languages.

Instead we will focus on machine-readable data that can be used to evaluate the trustworthiness of the incoming requests.

Infrastructure

Data Sources

In addition to the decoded inbox message itself, we provide keyed LRU caches with TTL to retrieve additional supporting information about the incoming requests, such as domain history, user history, instance metadata, etc.

Built-in Data Sources

  • Fediverse Observer
  • Domain WHOIS
  • Advertised NodeInfo
  • Federation Reports from Friend Instances

Evaluators

Evaluator is a function that takes an incoming request and either passes it through or return an response early.

Evaluators can be written as a free async closure or a struct implementing the Evaluator trait.

Built-in Evaluators

  • Completely fresh instance sending PMs or large number of mentions
  • Open Registration Instances with Abnormal User Growth
  • WHOIS from known bad registrars
  • Instances already blocked by Friend Instances
#[allow(clippy::unused_async)]
async fn build_state(args: &Args) -> AppState<MisskeyError> {
    let mut state = AppState::new(args.backend.parse().expect("Invalid backend URL"));

    let instance_history = Arc::new(LruData::sized(
        &|host| async move { Ok::<_, ()>("Todo") },
        512.try_into().unwrap(),
        Some(Duration::from_secs(600)),
    ));

    state.push_evaluator(Box::new(move |info: &APRequestInfo<'_>| {
        let act = info.activity.as_ref().map_err(|_| ERROR_DENIED).cloned();
        let instance_history = Arc::clone(&instance_history);

        async move {
            let act = act?;
            let host = act
                .actor
                .as_ref()
                .and_then(|s| Url::parse(s).ok())
                .ok_or(ERROR_DENIED)?;

            let instance = instance_history
                .query(host.host_str().unwrap().to_owned())
                .await;

            match instance {
                Ok(i) => {
                    log::info!("Instance history: {:?}", i);
                    Ok(())
                }
                Err(_) => Err::<(), _>(ERROR_DENIED),
            }
        }
    }));

    // let user_history = Arc::new(LruData::sized( ... ));

    state.push_evaluator(Box::new(|info: &APRequestInfo<'_>| {
        let act = info.activity.as_ref().map_err(|_| ERROR_DENIED).cloned();
        async move {
            let act = act?;
            log::debug!("Activity: {:?}", act);
            Ok(())
        }
    }));

    state
}