Skip to main content

verifyos_cli/rules/
entitlements.rs

1use crate::parsers::macho_parser::MachOExecutable;
2use crate::rules::core::{AppStoreRule, ArtifactContext, RuleError, RuleResult, Severity};
3
4#[derive(Debug, thiserror::Error, miette::Diagnostic)]
5pub enum EntitlementsError {
6    #[error("Failed to parse Mach-O executable for entitlements")]
7    #[diagnostic(
8        code(verifyos::entitlements::parse_failure),
9        help("The executable could not be parsed as a valid Mach-O binary.")
10    )]
11    ParseFailure,
12
13    #[error("App contains `get-task-allow` entitlement")]
14    #[diagnostic(
15        code(verifyos::entitlements::debug_build),
16        help("The `get-task-allow` entitlement is present and set to true. This indicates a debug build which will be rejected by the App Store.")
17    )]
18    DebugEntitlement,
19
20    #[error("Mach-O Parsing Error: {0}")]
21    #[diagnostic(code(verifyos::entitlements::macho_error))]
22    MachO(#[from] crate::parsers::macho_parser::MachOError),
23}
24
25pub struct EntitlementsMismatchRule;
26
27impl AppStoreRule for EntitlementsMismatchRule {
28    fn id(&self) -> &'static str {
29        "RULE_ENTITLEMENTS_MISMATCH"
30    }
31
32    fn name(&self) -> &'static str {
33        "Entitlements Mismatch"
34    }
35
36    fn severity(&self) -> Severity {
37        Severity::Error
38    }
39
40    fn evaluate(&self, artifact: &ArtifactContext) -> Result<RuleResult, RuleError> {
41        let app_name = artifact
42            .app_bundle_path
43            .file_name()
44            .and_then(|n| n.to_str())
45            .unwrap_or("")
46            .trim_end_matches(".app");
47
48        let executable_path = artifact.app_bundle_path.join(app_name);
49
50        if executable_path.exists() {
51            let macho =
52                MachOExecutable::from_file(&executable_path).map_err(EntitlementsError::MachO)?;
53
54            if let Some(entitlements_xml) = macho.entitlements {
55                // Parse the XML using the plist reader since entitlements are a plist
56                let plist = crate::parsers::plist_reader::InfoPlist::from_bytes(
57                    entitlements_xml.as_bytes(),
58                )
59                .map_err(|_| EntitlementsError::ParseFailure)?;
60
61                // For App Store submission, get-task-allow must NOT be true.
62                if let Some(true) = plist.get_bool("get-task-allow") {
63                    return Err(RuleError::Entitlements(EntitlementsError::DebugEntitlement));
64                }
65            }
66        }
67
68        Ok(RuleResult { success: true })
69    }
70}