chio-kernel-core 0.1.2

Portable (no_std + alloc) Chio kernel core: pure verdict evaluation, capability verification, and receipt signing
Documentation
#![forbid(clippy::unwrap_used)]
#![forbid(clippy::expect_used)]

use chio_core_types::capability::scope::{ChioScope, Constraint, Operation, ToolGrant};
use chio_kernel_core::scope::resolve_matching_grants;
use proptest::prelude::*;

fn proptest_config_for_lane(default_cases: u32) -> ProptestConfig {
    let cases = std::env::var("PROPTEST_CASES")
        .ok()
        .and_then(|value| value.parse::<u32>().ok())
        .unwrap_or(default_cases);
    ProptestConfig::with_cases(cases)
}

fn ident_strategy() -> impl Strategy<Value = String> {
    "[a-z][a-z0-9_-]{0,10}".prop_map(|value| value)
}

fn domain_label_strategy() -> impl Strategy<Value = String> {
    "[a-z][a-z0-9-]{0,10}".prop_map(|value| value)
}

fn grant(
    server_id: &str,
    tool_name: &str,
    operations: Vec<Operation>,
    constraints: Vec<Constraint>,
) -> ToolGrant {
    ToolGrant {
        server_id: server_id.to_string(),
        tool_name: tool_name.to_string(),
        operations,
        constraints,
        max_invocations: None,
        max_cost_per_invocation: None,
        max_total_cost: None,
        dpop_required: None,
    }
}

fn scope(grants: Vec<ToolGrant>) -> ChioScope {
    ChioScope {
        grants,
        ..ChioScope::default()
    }
}

fn matching_count(scope: &ChioScope, args: serde_json::Value) -> usize {
    match resolve_matching_grants(scope, "read", "files", &args) {
        Ok(matches) => matches.len(),
        Err(error) => panic!("portable scope matcher returned unexpected error: {error:?}"),
    }
}

proptest! {
    #![proptest_config(proptest_config_for_lane(96))]

    #[test]
    fn path_prefix_allows_normalized_descendants_and_denies_siblings(
        tenant in ident_strategy(),
        leaf in ident_strategy(),
    ) {
        let prefix = format!("/tenant/{tenant}/private");
        let scope = scope(vec![grant(
            "files",
            "read",
            vec![Operation::Invoke],
            vec![Constraint::PathPrefix(prefix.clone())],
        )]);

        let allowed = serde_json::json!({
            "path": format!("{prefix}/./{leaf}.txt")
        });
        prop_assert_eq!(matching_count(&scope, allowed), 1);

        let sibling = serde_json::json!({
            "path": format!("/tenant/{tenant}/public/{leaf}.txt")
        });
        prop_assert_eq!(matching_count(&scope, sibling), 0);
    }

    #[test]
    fn domain_exact_normalizes_case_and_url_authority(
        label in domain_label_strategy(),
    ) {
        let domain = format!("api.{label}.example.com");
        let scope = scope(vec![grant(
            "files",
            "read",
            vec![Operation::Invoke],
            vec![Constraint::DomainExact(domain.to_ascii_uppercase())],
        )]);

        let allowed = serde_json::json!({
            "url": format!("https://{domain}:443/v1/events?cursor=1")
        });
        prop_assert_eq!(matching_count(&scope, allowed), 1);

        let denied = serde_json::json!({
            "url": format!("https://evil.{label}.example.com/v1/events")
        });
        prop_assert_eq!(matching_count(&scope, denied), 0);
    }

    #[test]
    fn domain_glob_matches_subdomain_but_not_base_domain(
        label in domain_label_strategy(),
    ) {
        let base = format!("{label}.example.com");
        let scope = scope(vec![grant(
            "files",
            "read",
            vec![Operation::Invoke],
            vec![Constraint::DomainGlob(format!("*.{base}"))],
        )]);

        let allowed = serde_json::json!({
            "url": format!("https://api.{base}/v1")
        });
        prop_assert_eq!(matching_count(&scope, allowed), 1);

        let denied = serde_json::json!({
            "url": format!("https://{base}/v1")
        });
        prop_assert_eq!(matching_count(&scope, denied), 0);
    }

    #[test]
    fn max_length_and_max_args_size_boundaries_are_inclusive(
        value in "[a-z]{1,24}",
    ) {
        let len = value.len();
        let exact_length_scope = scope(vec![grant(
            "files",
            "read",
            vec![Operation::Invoke],
            vec![Constraint::MaxLength(len)],
        )]);
        prop_assert_eq!(
            matching_count(&exact_length_scope, serde_json::json!({ "value": value.clone() })),
            1
        );
        prop_assert_eq!(
            matching_count(&exact_length_scope, serde_json::json!({ "value": format!("{value}x") })),
            0
        );

        let compact = serde_json::json!({ "v": value });
        let serialized_len = compact.to_string().len();
        let exact_size_scope = scope(vec![grant(
            "files",
            "read",
            vec![Operation::Invoke],
            vec![Constraint::MaxArgsSize(serialized_len)],
        )]);
        prop_assert_eq!(matching_count(&exact_size_scope, compact), 1);
    }
}

#[test]
fn exact_grants_sort_ahead_of_wildcard_fallbacks() {
    let scope = scope(vec![
        grant("*", "*", vec![Operation::Invoke], Vec::new()),
        grant("files", "read", vec![Operation::Invoke], Vec::new()),
    ]);
    let matches = match resolve_matching_grants(
        &scope,
        "read",
        "files",
        &serde_json::json!({ "path": "/tenant/a/private/report.txt" }),
    ) {
        Ok(matches) => matches,
        Err(error) => panic!("expected grant resolution: {error:?}"),
    };

    assert_eq!(matches.len(), 2);
    assert_eq!(matches[0].index, 1);
    assert_eq!(matches[0].specificity, (1, 1, 0));
    assert_eq!(matches[1].index, 0);
}