canic-host 0.69.2

Host-side build, install, deployment, and fleet-template library for Canic workspaces
Documentation
use crate::deployment_truth::{
    ArtifactPromotionPlanV1, PromotionMaterializationIdentityReportV1,
    PromotionWasmStoreCatalogVerificationV1, PromotionWasmStoreIdentityReportV1,
    RolePromotionProvenanceV1, SafetyFindingV1, SafetySeverityV1,
};
use std::collections::BTreeSet;

pub(super) fn artifact_promotion_provenance_blockers(
    plan: &ArtifactPromotionPlanV1,
    wasm_store_report: Option<&PromotionWasmStoreIdentityReportV1>,
    wasm_store_catalog: Option<&PromotionWasmStoreCatalogVerificationV1>,
    materialization_report: Option<&PromotionMaterializationIdentityReportV1>,
    roles: &[RolePromotionProvenanceV1],
) -> Vec<SafetyFindingV1> {
    let mut blockers = plan.blockers.clone();
    let role_names = roles
        .iter()
        .map(|role| role.role.as_str())
        .collect::<BTreeSet<_>>();
    if let Some(report) = wasm_store_report {
        blockers.extend(report.blockers.iter().cloned());
    }
    append_wasm_store_catalog_provenance_blockers(
        &mut blockers,
        wasm_store_report,
        wasm_store_catalog,
        &role_names,
    );
    if let Some(report) = materialization_report {
        blockers.extend(report.blockers.iter().cloned());
    }
    append_optional_report_unknown_role_blockers(
        &mut blockers,
        wasm_store_report,
        wasm_store_catalog,
        materialization_report,
        &role_names,
    );
    blockers
}

fn append_wasm_store_catalog_provenance_blockers(
    blockers: &mut Vec<SafetyFindingV1>,
    wasm_store_report: Option<&PromotionWasmStoreIdentityReportV1>,
    wasm_store_catalog: Option<&PromotionWasmStoreCatalogVerificationV1>,
    role_names: &BTreeSet<&str>,
) {
    let Some(verification) = wasm_store_catalog else {
        return;
    };
    blockers.extend(verification.blockers.iter().cloned());
    match wasm_store_report {
        Some(report) if verification.wasm_store_identity_report_id == report.report_id => {}
        Some(report) => blockers.push(super::super::super::promotion_finding(
            "promotion_provenance_wasm_store_catalog_identity_mismatch",
            format!(
                "wasm-store catalog verification references identity report {}, but provenance uses {}",
                verification.wasm_store_identity_report_id, report.report_id
            ),
            SafetySeverityV1::HardFailure,
            "wasm_store_catalog",
        )),
        None => blockers.push(super::super::super::promotion_finding(
            "promotion_provenance_wasm_store_catalog_identity_missing",
            "wasm-store catalog verification requires the referenced wasm-store identity report",
            SafetySeverityV1::HardFailure,
            "wasm_store_catalog",
        )),
    }
    if let Some(report) = wasm_store_report {
        append_wasm_store_catalog_locator_blockers(blockers, report, verification, role_names);
    }
}

fn append_wasm_store_catalog_locator_blockers(
    blockers: &mut Vec<SafetyFindingV1>,
    report: &PromotionWasmStoreIdentityReportV1,
    verification: &PromotionWasmStoreCatalogVerificationV1,
    role_names: &BTreeSet<&str>,
) {
    for catalog_role in &verification.roles {
        if !role_names.contains(catalog_role.role.as_str()) {
            continue;
        }
        match report.roles.iter().find(|role| role.role == catalog_role.role) {
            Some(identity_role)
                if identity_role.wasm_store_locator.as_deref()
                    == Some(catalog_role.wasm_store_locator.as_str()) => {}
            Some(identity_role) => blockers.push(super::super::super::promotion_finding(
                "promotion_provenance_wasm_store_catalog_locator_mismatch",
                format!(
                    "wasm-store catalog verification role {} uses locator {}, but identity report uses {}",
                    catalog_role.role,
                    catalog_role.wasm_store_locator,
                    identity_role.wasm_store_locator.as_deref().unwrap_or("none")
                ),
                SafetySeverityV1::HardFailure,
                &catalog_role.role,
            )),
            None => blockers.push(super::super::super::promotion_finding(
                "promotion_provenance_wasm_store_catalog_role_identity_missing",
                format!(
                    "wasm-store catalog verification role {} is missing from the wasm-store identity report",
                    catalog_role.role
                ),
                SafetySeverityV1::HardFailure,
                &catalog_role.role,
            )),
        }
    }
}

fn append_optional_report_unknown_role_blockers(
    blockers: &mut Vec<SafetyFindingV1>,
    wasm_store_report: Option<&PromotionWasmStoreIdentityReportV1>,
    wasm_store_catalog: Option<&PromotionWasmStoreCatalogVerificationV1>,
    materialization_report: Option<&PromotionMaterializationIdentityReportV1>,
    role_names: &BTreeSet<&str>,
) {
    if let Some(report) = wasm_store_report {
        for role in &report.roles {
            if !role_names.contains(role.role.as_str()) {
                blockers.push(super::super::super::promotion_finding(
                    "promotion_provenance_unknown_wasm_store_role",
                    format!(
                        "wasm-store identity report contains unknown role {}",
                        role.role
                    ),
                    SafetySeverityV1::HardFailure,
                    &role.role,
                ));
            }
        }
    }
    if let Some(verification) = wasm_store_catalog {
        for role in &verification.roles {
            if !role_names.contains(role.role.as_str()) {
                blockers.push(super::super::super::promotion_finding(
                    "promotion_provenance_unknown_wasm_store_catalog_role",
                    format!(
                        "wasm-store catalog verification contains unknown role {}",
                        role.role
                    ),
                    SafetySeverityV1::HardFailure,
                    &role.role,
                ));
            }
        }
    }
    if let Some(report) = materialization_report {
        for role in &report.roles {
            if !role_names.contains(role.role.as_str()) {
                blockers.push(super::super::super::promotion_finding(
                    "promotion_provenance_unknown_materialization_role",
                    format!(
                        "materialization identity report contains unknown role {}",
                        role.role
                    ),
                    SafetySeverityV1::HardFailure,
                    &role.role,
                ));
            }
        }
    }
}