Skip to main content

verifyos_cli/
profiles.rs

1use crate::core::engine::Engine;
2use crate::rules::ats::{AtsAuditRule, AtsExceptionsGranularityRule};
3use crate::rules::bundle_leakage::BundleResourceLeakageRule;
4use crate::rules::bundle_metadata::BundleMetadataConsistencyRule;
5use crate::rules::core::AppStoreRule;
6use crate::rules::core::{RuleCategory, Severity};
7use crate::rules::entitlements::{EntitlementsMismatchRule, EntitlementsProvisioningMismatchRule};
8use crate::rules::export_compliance::ExportComplianceRule;
9use crate::rules::extensions::ExtensionEntitlementsCompatibilityRule;
10use crate::rules::info_plist::{
11    InfoPlistCapabilitiesRule, InfoPlistRequiredKeysRule, InfoPlistVersionConsistencyRule,
12    LSApplicationQueriesSchemesAuditRule, UIRequiredDeviceCapabilitiesAuditRule,
13    UsageDescriptionsRule, UsageDescriptionsValueRule,
14};
15use crate::rules::permissions::CameraUsageDescriptionRule;
16use crate::rules::privacy::MissingPrivacyManifestRule;
17use crate::rules::privacy_manifest::PrivacyManifestCompletenessRule;
18use crate::rules::privacy_sdk::PrivacyManifestSdkCrossCheckRule;
19use crate::rules::private_api::PrivateApiRule;
20use crate::rules::signing::EmbeddedCodeSignatureTeamRule;
21use serde::Serialize;
22use std::collections::BTreeMap;
23use std::collections::HashSet;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum ScanProfile {
27    Basic,
28    Full,
29}
30
31#[derive(Debug, Clone, Default)]
32pub struct RuleSelection {
33    pub include: HashSet<String>,
34    pub exclude: HashSet<String>,
35}
36
37#[derive(Debug, Clone, Serialize)]
38pub struct RuleInventoryItem {
39    pub rule_id: String,
40    pub name: String,
41    pub severity: Severity,
42    pub category: RuleCategory,
43    pub default_profiles: Vec<String>,
44}
45
46impl RuleSelection {
47    pub fn allows(&self, rule_id: &str) -> bool {
48        let normalized = normalize_rule_id(rule_id);
49        let included = self.include.is_empty() || self.include.contains(&normalized);
50        let excluded = self.exclude.contains(&normalized);
51        included && !excluded
52    }
53}
54
55pub fn register_rules(engine: &mut Engine, profile: ScanProfile, selection: &RuleSelection) {
56    for rule in profile_rules(profile) {
57        if selection.allows(rule.id()) {
58            engine.register_rule(rule);
59        }
60    }
61}
62
63pub fn available_rule_ids(profile: ScanProfile) -> Vec<String> {
64    let mut ids: Vec<String> = profile_rules(profile)
65        .into_iter()
66        .map(|rule| normalize_rule_id(rule.id()))
67        .collect();
68    ids.sort();
69    ids.dedup();
70    ids
71}
72
73pub fn normalize_rule_id(rule_id: &str) -> String {
74    rule_id.trim().to_ascii_uppercase()
75}
76
77pub fn rule_inventory() -> Vec<RuleInventoryItem> {
78    let mut items: BTreeMap<String, RuleInventoryItem> = BTreeMap::new();
79
80    for (profile_name, profile) in [("basic", ScanProfile::Basic), ("full", ScanProfile::Full)] {
81        for rule in profile_rules(profile) {
82            let rule_id = normalize_rule_id(rule.id());
83            let entry = items
84                .entry(rule_id.clone())
85                .or_insert_with(|| RuleInventoryItem {
86                    rule_id,
87                    name: rule.name().to_string(),
88                    severity: rule.severity(),
89                    category: rule.category(),
90                    default_profiles: Vec::new(),
91                });
92
93            if !entry
94                .default_profiles
95                .iter()
96                .any(|name| name == profile_name)
97            {
98                entry.default_profiles.push(profile_name.to_string());
99            }
100        }
101    }
102
103    items.into_values().collect()
104}
105
106fn profile_rules(profile: ScanProfile) -> Vec<Box<dyn AppStoreRule>> {
107    match profile {
108        ScanProfile::Basic => basic_rules(),
109        ScanProfile::Full => full_rules(),
110    }
111}
112
113fn basic_rules() -> Vec<Box<dyn AppStoreRule>> {
114    vec![
115        Box::new(MissingPrivacyManifestRule),
116        Box::new(UsageDescriptionsRule),
117        Box::new(UsageDescriptionsValueRule),
118        Box::new(CameraUsageDescriptionRule),
119        Box::new(AtsAuditRule),
120        Box::new(AtsExceptionsGranularityRule),
121        Box::new(EntitlementsMismatchRule),
122        Box::new(EntitlementsProvisioningMismatchRule),
123        Box::new(EmbeddedCodeSignatureTeamRule),
124    ]
125}
126
127fn full_rules() -> Vec<Box<dyn AppStoreRule>> {
128    vec![
129        Box::new(MissingPrivacyManifestRule),
130        Box::new(PrivacyManifestCompletenessRule),
131        Box::new(PrivacyManifestSdkCrossCheckRule),
132        Box::new(CameraUsageDescriptionRule),
133        Box::new(UsageDescriptionsRule),
134        Box::new(UsageDescriptionsValueRule),
135        Box::new(InfoPlistRequiredKeysRule),
136        Box::new(InfoPlistCapabilitiesRule),
137        Box::new(LSApplicationQueriesSchemesAuditRule),
138        Box::new(UIRequiredDeviceCapabilitiesAuditRule),
139        Box::new(InfoPlistVersionConsistencyRule),
140        Box::new(ExportComplianceRule),
141        Box::new(AtsAuditRule),
142        Box::new(AtsExceptionsGranularityRule),
143        Box::new(EntitlementsMismatchRule),
144        Box::new(EntitlementsProvisioningMismatchRule),
145        Box::new(BundleMetadataConsistencyRule),
146        Box::new(BundleResourceLeakageRule),
147        Box::new(ExtensionEntitlementsCompatibilityRule),
148        Box::new(PrivateApiRule),
149        Box::new(EmbeddedCodeSignatureTeamRule),
150    ]
151}