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}