openfair 0.1.1

Factor Analysis of Information Risk (OpenFAIR)
Documentation
use crate::{
    error::Error,
    simulations::{
        model::{nodes::NodeNames, params::GenParams, Model, ModelTrait},
        results::{
            ModelSimulationNodeResult, ModelSimulationResult, SimulationResult, SimulationResults,
        },
    },
    Result,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Serialize, Deserialize)]
pub struct Threat {
    id: String,
    tef: (f32, f32, f32),
    tc: (f32, f32, f32),
    lm: (f32, f32, f32),
}

impl Threat {
    pub fn from_json(name: &str, v: &Value) -> Result<Threat> {
        let threats = match &v["threats"] {
            Value::Null => return Err(Error::ThreatsFieldNotFound),
            _ => &v["threats"],
        };
        let params = match &v["params"] {
            Value::Null => return Err(Error::ParamsFieldNotFound),
            _ => &v["params"],
        };
        let params_tef = match params["expected_frequency"] {
            Value::Null => return Err(Error::ExpectedFrequencyFieldNotFound),
            _ => &params["expected_frequency"],
        };
        let params_tc = match params["capability"] {
            Value::Null => return Err(Error::CapabilityFieldNotFound),
            _ => &params["capability"],
        };
        let params_lm = match params["loss_magnitude"] {
            Value::Null => return Err(Error::LossMagnitudeFieldNotFound),
            _ => &params["loss_magnitude"],
        };
        let threat = match &threats[name] {
            Value::Null => return Err(Error::ThreatNotFound(name.to_string())),
            _ => &threats[name],
        };
        let tef_str = threat["expected_frequency"]
            .as_str()
            .ok_or_else(|| Error::InvalidExpectedFrequency(name.to_string()))?;
        let c_str = threat["capability"]
            .as_str()
            .ok_or_else(|| Error::InvalidCapability(name.to_string()))?;
        let lm_str = threat["loss_magnitude"]
            .as_str()
            .ok_or_else(|| Error::InvalidLossMagnitude(name.to_string()))?;
        let s = Threat {
            id: name.to_string(),
            tef: (
                params_tef[tef_str][0]
                    .as_f64()
                    .ok_or(Error::ExpectedFrequencyMinMeanMaxNotFound)? as f32,
                params_tef[tef_str][1]
                    .as_f64()
                    .ok_or(Error::ExpectedFrequencyMinMeanMaxNotFound)? as f32,
                params_tef[tef_str][2]
                    .as_f64()
                    .ok_or(Error::ExpectedFrequencyMinMeanMaxNotFound)? as f32,
            ),
            tc: (
                params_tc[c_str][0]
                    .as_f64()
                    .ok_or(Error::CapabilityMinMeanMaxNotFound)? as f32,
                params_tc[c_str][1]
                    .as_f64()
                    .ok_or(Error::CapabilityMinMeanMaxNotFound)? as f32,
                params_tc[c_str][2]
                    .as_f64()
                    .ok_or(Error::CapabilityMinMeanMaxNotFound)? as f32,
            ),
            lm: (
                params_lm[lm_str][0]
                    .as_f64()
                    .ok_or(Error::LossMagnitudeMinMeanMaxNotFound)? as f32,
                params_lm[lm_str][1]
                    .as_f64()
                    .ok_or(Error::LossMagnitudeMinMeanMaxNotFound)? as f32,
                params_lm[lm_str][2]
                    .as_f64()
                    .ok_or(Error::LossMagnitudeMinMeanMaxNotFound)? as f32,
            ),
        };
        Ok(s)
    }
}

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

impl Control {
    pub fn from_json(name: &str, v: &Value) -> Result<Control> {
        let controls = match &v["controls"] {
            Value::Null => return Err(Error::ControlsFieldNotFound),
            _ => &v["controls"],
        };
        let params = match &v["params"] {
            Value::Null => return Err(Error::ParamsFieldNotFound),
            _ => &v["params"],
        };
        let params_diff = match params["difficulty"] {
            Value::Null => return Err(Error::DifficultyFieldNotFound),
            _ => &params["difficulty"],
        };
        let control = match &controls[name] {
            Value::Null => return Err(Error::InvalidControl(name.to_string())),
            _ => &controls[name],
        };
        let diff_str = control["difficulty"]
            .as_str()
            .ok_or_else(|| Error::InvalidDifficulty(name.to_string()))?;
        let c = Control {
            id: name.to_string(),
            diff: (
                params_diff[diff_str][0]
                    .as_f64()
                    .ok_or(Error::DifficultyMinMeanMaxNotFound)? as f32,
                params_diff[diff_str][1]
                    .as_f64()
                    .ok_or(Error::DifficultyMinMeanMaxNotFound)? as f32,
                params_diff[diff_str][2]
                    .as_f64()
                    .ok_or(Error::DifficultyMinMeanMaxNotFound)? as f32,
            ),
        };
        Ok(c)
    }
}

#[derive(Serialize, Deserialize)]
pub struct Scenario {
    models: Vec<Model>,
    threat: Threat,
    controls: Vec<Control>,
}

impl Scenario {
    pub fn new(threat: Threat, controls: Vec<Control>) -> Scenario {
        let mut s = Scenario {
            models: vec![], //Model::new(&format!("{} + {}", &threat.id, &control.id)),
            threat,
            controls,
        };
        let mut ss = Model::new(&format!("{} + {}", &s.threat.id, "NoControl"));
        ss.input(
            NodeNames::ThreatEventFrequency,
            GenParams::PertParams {
                min: s.threat.tef.0,
                mean: s.threat.tef.1,
                max: s.threat.tef.2,
            },
        )
        .unwrap();
        ss.input(
            NodeNames::ThreatCapability,
            GenParams::PertParams {
                min: s.threat.tc.0,
                mean: s.threat.tc.1,
                max: s.threat.tc.2,
            },
        )
        .unwrap();
        ss.input(
            NodeNames::LossMagnitude,
            GenParams::PertParams {
                min: s.threat.lm.0,
                mean: s.threat.lm.1,
                max: s.threat.lm.2,
            },
        )
        .unwrap();
        ss.input(
            NodeNames::ControlStrength,
            GenParams::ConstParams { val: 0.0 },
        )
        .unwrap();
        s.models.push(ss);
        for c in &s.controls {
            let mut ss = Model::new(&format!("{} + {}", &s.threat.id, &c.id));
            ss.input(
                NodeNames::ThreatEventFrequency,
                GenParams::PertParams {
                    min: s.threat.tef.0,
                    mean: s.threat.tef.1,
                    max: s.threat.tef.2,
                },
            )
            .unwrap();
            ss.input(
                NodeNames::ThreatCapability,
                GenParams::PertParams {
                    min: s.threat.tc.0,
                    mean: s.threat.tc.1,
                    max: s.threat.tc.2,
                },
            )
            .unwrap();
            ss.input(
                NodeNames::LossMagnitude,
                GenParams::PertParams {
                    min: s.threat.lm.0,
                    mean: s.threat.lm.1,
                    max: s.threat.lm.2,
                },
            )
            .unwrap();
            ss.input(
                NodeNames::ControlStrength,
                GenParams::PertParams {
                    min: c.diff.0,
                    mean: c.diff.1,
                    max: c.diff.2,
                },
            )
            .unwrap();
            s.models.push(ss);
        }
        s
    }

    pub fn from_json(threat_name: &str, control_names: Vec<&str>, v: &Value) -> Result<Scenario> {
        let mut cs: Vec<Control> = vec![];
        for cc in &control_names {
            cs.push(Control::from_json(cc, v)?)
        }
        Ok(Scenario::new(Threat::from_json(threat_name, v)?, cs))
    }

    pub fn simulate(&mut self, n: u32) -> Result<Vec<(f32, f32, f32, f32)>> {
        let mut res: Vec<(f32, f32, f32, f32)> = vec![];
        for m in &mut self.models {
            res.push(m.simulate(n)?);
        }
        Ok(res)
    }
}

#[derive(Serialize, Deserialize)]
pub struct Scenarios {
    simulation_count: u32,
    scenarios: Vec<Scenario>,
}

impl Scenarios {
    pub fn from_json(v: &Value) -> Result<Scenarios> {
        let mut sc = Scenarios {
            scenarios: vec![],
            simulation_count: v["simulation_count"]
                .as_u64()
                .ok_or(Error::ThreatsFieldNotFound)? as u32,
        };
        for s in v["scenarios"]
            .as_array()
            .ok_or(Error::ScenariosFieldNotFound)?
        {
            let mut cc: Vec<&str> = vec![];
            for c in s["controlIds"]
                .as_array()
                .ok_or(Error::ControlIdsNotFound)?
            {
                cc.push(c.as_str().ok_or_else(|| {
                    Error::InvalidControlId(c.as_str().unwrap_or_default().to_string())
                })?);
            }
            sc.scenarios.push(Scenario::from_json(
                s["threatId"].as_str().ok_or_else(|| {
                    Error::InvalidThreatId(s["threatId"].as_str().unwrap_or_default().to_string())
                })?,
                cc,
                v,
            )?);
        }
        Ok(sc)
    }

    pub fn simulate(&mut self, n: u32) -> Result<SimulationResults> {
        let mut res = SimulationResults {
            simulation_result: vec![],
        };
        for s in &mut self.scenarios {
            let sim_res = s.simulate(n)?;
            let mut sr = SimulationResult {
                threat: s.threat.id.clone(),
                simulation_result: vec![],
            };
            for i in 0..sim_res.len() {
                let msr = ModelSimulationResult {
                    control: match &i {
                        0 => "None".to_string(),
                        _ => s.controls[i - 1].id.clone(),
                    },
                    risk: ModelSimulationNodeResult::new(s.models[i].get(&NodeNames::Risk)?),
                    lef: ModelSimulationNodeResult::new(
                        s.models[i].get(&NodeNames::LossEventFrequency)?,
                    ),
                    v: ModelSimulationNodeResult::new(s.models[i].get(&NodeNames::Vulnerability)?),
                };
                sr.simulation_result.push(msr);
            }
            res.simulation_result.push(sr);
        }
        Ok(res)
    }

    pub fn simulate_all(&mut self) -> Result<SimulationResults> {
        self.simulate(self.simulation_count)
    }
}

pub fn simulate_scenario(input_json: &Value) -> Result<Value> {
    let scenario = input_json
        .as_object()
        .ok_or(Error::InvalidInput)?
        .get("scenario")
        .ok_or(Error::ScenarioNotFound)?;

    match Scenarios::from_json(&scenario) {
        Ok(mut s) => Ok(serde_json::to_value(&s.simulate_all()?)?),
        Err(e) => Err(e),
    }
}