Skip to main content

verifyos_cli/core/
engine.rs

1use std::path::Path;
2use crate::parsers::plist_reader::{InfoPlist, PlistError};
3use crate::parsers::zip_extractor::{extract_ipa, ExtractionError};
4use crate::rules::core::{AppStoreRule, ArtifactContext, RuleError, RuleResult, Severity};
5
6#[derive(Debug, thiserror::Error)]
7pub enum OrchestratorError {
8    #[error("Extraction failed: {0}")]
9    Extraction(#[from] ExtractionError),
10    #[error("Failed to parse Info.plist: {0}")]
11    PlistParse(#[from] PlistError),
12    #[error("Could not locate App Bundle (.app) inside IPAPayload")]
13    AppBundleNotFound,
14}
15
16pub struct EngineResult {
17    pub rule_id: &'static str,
18    pub rule_name: &'static str,
19    pub severity: Severity,
20    pub result: Result<RuleResult, RuleError>,
21}
22
23pub struct Engine {
24    rules: Vec<Box<dyn AppStoreRule>>,
25}
26
27impl Engine {
28    pub fn new() -> Self {
29        Self { rules: Vec::new() }
30    }
31
32    pub fn register_rule(&mut self, rule: Box<dyn AppStoreRule>) {
33        self.rules.push(rule);
34    }
35
36    pub fn run<P: AsRef<Path>>(&self, ipa_path: P) -> Result<Vec<EngineResult>, OrchestratorError> {
37        let extracted_ipa = extract_ipa(ipa_path)?;
38
39        let app_bundle_path = extracted_ipa
40            .get_app_bundle_path()
41            .map_err(|e| OrchestratorError::Extraction(ExtractionError::Io(e)))?
42            .ok_or(OrchestratorError::AppBundleNotFound)?;
43
44        let info_plist_path = app_bundle_path.join("Info.plist");
45        let info_plist = if info_plist_path.exists() {
46            Some(InfoPlist::from_file(&info_plist_path)?)
47        } else {
48            None
49        };
50
51        let context = ArtifactContext {
52            app_bundle_path: &app_bundle_path,
53            info_plist: info_plist.as_ref(),
54        };
55
56        let mut results = Vec::new();
57
58        for rule in &self.rules {
59            let res = rule.evaluate(&context);
60            results.push(EngineResult {
61                rule_id: rule.id(),
62                rule_name: rule.name(),
63                severity: rule.severity(),
64                result: res,
65            });
66        }
67
68        Ok(results)
69    }
70}
71
72impl Default for Engine {
73    fn default() -> Self {
74        Self::new()
75    }
76}