openfair 0.1.1

Factor Analysis of Information Risk (OpenFAIR)
Documentation
use crate::{error::Error, Result};
use serde::{Deserialize, Serialize};
use serde_json::{json, to_value, Value};

#[derive(Serialize, Deserialize)]
pub struct D3JsDataControl {
    id: String,
    val: f32,
}

#[derive(Serialize, Deserialize)]
pub struct D3JsDataThreat {
    id: String,
    controls: Vec<D3JsDataControl>,
}

#[derive(Serialize, Deserialize)]
pub struct D3JsData {
    threats: Vec<D3JsDataThreat>,
}

pub fn scenario_to_d3js(data: &str, t: Vec<&str>) -> Result<Value> {
    let json: serde_json::Value = serde_json::from_str(data)?;
    let mut ddd = D3JsData { threats: vec![] };
    for s in json["simulation_result"]
        .as_array()
        .ok_or(Error::SimulationResultNotFound)?
    {
        for tt in &t {
            let mut threat = D3JsDataThreat {
                id: format!(
                    "{} {}",
                    s["threat"].as_str().ok_or(Error::ThreatsFieldNotFound)?,
                    tt
                ),
                controls: vec![],
            };
            for c in s["simulation_result"]
                .as_array()
                .ok_or(Error::SimulationResultNotFound)?
            {
                let control = D3JsDataControl {
                    id: c["control"]
                        .as_str()
                        .ok_or_else(|| {
                            Error::InvalidChartControl(
                                c["control"].as_str().unwrap_or_default().to_string(),
                            )
                        })?
                        .to_string(),
                    val: c[tt]["mean"].as_f64().ok_or_else(|| {
                        Error::InvalidChartMean(c[tt]["mean"].as_f64().unwrap_or_default())
                    })? as f32,
                };
                threat.controls.push(control);
            }
            ddd.threats.push(threat);
        }
    }
    Ok(to_value(ddd)?)
}

pub fn model_set_to_d3js(data: &str) -> Result<Value> {
    let mut json: serde_json::Value = serde_json::from_str(data)?;
    let mut res = json!({
        "breaf_table": [],
        "risk_distribution": [],
        "exceedence_probability_curve": [],
        "loss_exceedence_curve": [],
        "component_and_aggregate_risk": [],
        "dependency_trees": []
    });
    //breaf_table
    for m in json["metamodels"]
        .as_array()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        res["breaf_table"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "mean": m["mean"],
                "min": m["min"],
                "max": m["max"],
                "deviation": m["dev"],
            }));
    }
    for m in json["models"]
        .as_array()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        res["breaf_table"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "mean": m["mean"],
                "min": m["min"],
                "max": m["max"],
                "deviation": m["dev"],
            }));
    }
    //risk_distribution
    for m in json["metamodels"]
        .as_array()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        let mut v: Vec<Value> = vec![];
        let mut val: Vec<(f32, f32)> = vec![];
        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
        for i in 0..41 {
            val.push((0.0 + (i as f32) * (vxmax / 40.0), 0.0));
        }
        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
            val[((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 40.0))
                as usize]
                .1 += 1.0;
        }

        for vvv in val {
            v.push(json!({
                "x": vvv.0,
                "y": vvv.1
            }));
        }
        res["risk_distribution"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "values": v
            }));
    }
    for m in json["models"]
        .as_array()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        let mut v: Vec<Value> = vec![];
        let mut val: Vec<(f32, f32)> = vec![];
        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
        for i in 0..41 {
            val.push((0.0 + (i as f32) * (vxmax / 40.0), 0.0));
        }
        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
            val[((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 40.0))
                as usize]
                .1 += 1.0;
        }

        for vvv in val {
            v.push(json!({
                "x": vvv.0,
                "y": vvv.1
            }));
        }
        res["risk_distribution"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "values": v
            }));
    }

    //exceedence_probability_curve
    for m in json["metamodels"]
        .as_array()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        let mut v: Vec<Value> = vec![];
        let mut val: Vec<(f32, f32)> = vec![];
        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
        for i in 0..101 {
            val.push((0.0, 0.0 + (i as f32) * (vxmax / 40.0)));
        }
        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
            for i in (((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 100.0))
                as u32)..101
            {
                val[i as usize].0 += 1.0;
            }
        }
        for vvv in val {
            v.push(json!({
                "x": vvv.0,
                "y": vvv.1
            }));
        }
        res["exceedence_probability_curve"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "values": v
            }));
    }
    for m in json["models"]
        .as_array()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        let mut v: Vec<Value> = vec![];
        let mut val: Vec<(f32, f32)> = vec![];
        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
        for i in 0..101 {
            val.push((0.0, 0.0 + (i as f32) * (vxmax / 40.0)));
        }
        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
            for i in (((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 100.0))
                as u32)..101
            {
                val[i as usize].0 += 1.0;
            }
        }
        for vvv in val {
            v.push(json!({
                "x": vvv.0,
                "y": vvv.1
            }));
        }
        res["exceedence_probability_curve"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "values": v
            }));
    }

    //loss_exceedence_curve
    for m in json["metamodels"]
        .as_array()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        let mut v: Vec<Value> = vec![];
        let mut val: Vec<(f32, f32)> = vec![];
        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
        for i in 0..101 {
            val.push((0.0 + (i as f32) * (vxmax / 100.0), 0.0));
        }
        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
            for i in 0..(((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32)
                / (vxmax / 100.0)) as u32)
            {
                val[i as usize].1 += 1.0;
            }
        }
        for vvv in val {
            v.push(json!({
                "x": vvv.0,
                "y": vvv.1
            }));
        }
        res["loss_exceedence_curve"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "values": v
            }));
    }
    for m in json["models"]
        .as_array()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        let mut v: Vec<Value> = vec![];
        let mut val: Vec<(f32, f32)> = vec![];
        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
        for i in 0..101 {
            val.push((0.0 + (i as f32) * (vxmax / 100.0), 0.0));
        }
        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
            for i in 0..(((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32)
                / (vxmax / 100.0)) as u32)
            {
                val[i as usize].1 += 1.0;
            }
        }
        for vvv in val {
            v.push(json!({
                "x": vvv.0,
                "y": vvv.1
            }));
        }
        res["loss_exceedence_curve"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "values": v
            }));
    }

    //component_and_aggregate_risk
    for m in json["metamodels"]
        .as_array()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        res["component_and_aggregate_risk"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "mean": m["mean"],
                "min": m["min"],
                "max": m["max"],
                "deviation": m["dev"],
            }));
    }
    for m in json["models"]
        .as_array()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        res["component_and_aggregate_risk"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "mean": m["mean"],
                "min": m["min"],
                "max": m["max"],
                "deviation": m["dev"],
            }));
    }

    //dependency_trees
    for m in json["models"]
        .as_array_mut()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        clean_json_tree(&mut m["tree"]["root"])?;
        res["dependency_trees"]
            .as_array_mut()
            .ok_or(Error::ThreatsFieldNotFound)?
            .push(json!({
                "name": m["name"],
                "tree": [m["tree"]["root"]]
            }));
    }
    Ok(res)
}

fn clean_json_tree(v: &mut Value) -> Result<()> {
    let obj = v.as_object_mut().ok_or(Error::ThreatsFieldNotFound)?;
    obj.remove("values").ok_or(Error::ThreatsFieldNotFound)?;
    obj.remove("min").ok_or(Error::ThreatsFieldNotFound)?;
    obj.remove("max").ok_or(Error::ThreatsFieldNotFound)?;
    obj.remove("mean").ok_or(Error::ThreatsFieldNotFound)?;
    obj.remove("dev").ok_or(Error::ThreatsFieldNotFound)?;
    obj.remove("gen_params")
        .ok_or(Error::ThreatsFieldNotFound)?;
    for c in obj["children"]
        .as_array_mut()
        .ok_or(Error::ThreatsFieldNotFound)?
    {
        clean_json_tree(c)?
    }
    Ok(())
}