diff --git a/build.rs b/build.rs index c47ea2142..2482d9252 100644 --- a/build.rs +++ b/build.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use std::env; use clap::Shell; -#[path="src/cli.rs"] +#[path = "src/cli.rs"] mod cli; fn main() { diff --git a/src/cli.rs b/src/cli.rs index f74c81f89..c554c595f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,79 +5,111 @@ pub fn generate() -> App<'static, 'static> { .version(crate_version!()) .author("SUPER Team ") .about("Audits Android apps (.apk files) for vulnerabilities") - .arg(Arg::with_name("package") - .help("The package string of the application to test") - .value_name("package") - .required_unless("test-all") - .conflicts_with("test-all") - .takes_value(true)) - .arg(Arg::with_name("test-all") - .short("a") - .long("test-all") - .conflicts_with("package") - .conflicts_with("open") - .help("Test all .apk files in the downloads directory")) - .arg(Arg::with_name("verbose") - .short("v") - .long("verbose") - .conflicts_with("quiet") - .help("If you'd like the auditor to talk more than necessary")) - .arg(Arg::with_name("force") - .long("force") - .help("If you'd like to force the auditor to do everything from the beginning")) - .arg(Arg::with_name("bench") - .long("bench") - .help("Show benchmarks for the analysis")) - .arg(Arg::with_name("quiet") - .short("q") - .long("quiet") - .conflicts_with("verbose") - .help("If you'd like a zen auditor that won't output anything in stdout")) - .arg(Arg::with_name("open") - .long("open") - .conflicts_with("test-all") - .help("Open the report in a browser once it is complete")) - .arg(Arg::with_name("json") - .long("json") - .help("Generates the reults in JSON format")) - .arg(Arg::with_name("html") - .long("html") - .help("Generates the reults in HTML format")) - .arg(Arg::with_name("min_criticality") - .long("min-criticality") - .help("Set a minimum criticality to analyze (Critical, High, Medium, Low)") - .takes_value(true)) - .arg(Arg::with_name("threads") - .short("t") - .long("threads") - .help("Number of threads to use") - .takes_value(true)) - .arg(Arg::with_name("downloads") - .long("downloads") - .help("Folder where the downloads are stored") - .takes_value(true)) - .arg(Arg::with_name("dist") - .long("dist") - .help("Folder where distribution files will be extracted") - .takes_value(true)) - .arg(Arg::with_name("results") - .long("results") - .help("Folder where to store the results") - .takes_value(true)) - .arg(Arg::with_name("dex2jar") - .long("dex2jar") - .help("Where to store the jar files") - .takes_value(true)) - .arg(Arg::with_name("jd-cmd") - .long("jd-cmd") - .help("Path to the jd-cmd file") - .takes_value(true)) - .arg(Arg::with_name("template") - .long("template") - .help("Path to a results template file") - .takes_value(true)) - .arg(Arg::with_name("rules") - .long("rules") - .help("Path to a JSON rules file") - .takes_value(true)) + .arg( + Arg::with_name("package") + .help("The package string of the application to test") + .value_name("package") + .required_unless("test-all") + .conflicts_with("test-all") + .takes_value(true), + ) + .arg( + Arg::with_name("test-all") + .short("a") + .long("test-all") + .conflicts_with("package") + .conflicts_with("open") + .help("Test all .apk files in the downloads directory"), + ) + .arg( + Arg::with_name("verbose") + .short("v") + .long("verbose") + .conflicts_with("quiet") + .help("If you'd like the auditor to talk more than necessary"), + ) + .arg(Arg::with_name("force").long("force").help( + "If you'd like to force the auditor to do everything from the beginning", + )) + .arg(Arg::with_name("bench").long("bench").help( + "Show benchmarks for the analysis", + )) + .arg( + Arg::with_name("quiet") + .short("q") + .long("quiet") + .conflicts_with("verbose") + .help( + "If you'd like a zen auditor that won't output anything in stdout", + ), + ) + .arg( + Arg::with_name("open") + .long("open") + .conflicts_with("test-all") + .help("Open the report in a browser once it is complete"), + ) + .arg(Arg::with_name("json").long("json").help( + "Generates the reults in JSON format", + )) + .arg(Arg::with_name("html").long("html").help( + "Generates the reults in HTML format", + )) + .arg( + Arg::with_name("min_criticality") + .long("min-criticality") + .help( + "Set a minimum criticality to analyze (Critical, High, Medium, Low)", + ) + .takes_value(true), + ) + .arg( + Arg::with_name("threads") + .short("t") + .long("threads") + .help("Number of threads to use") + .takes_value(true), + ) + .arg( + Arg::with_name("downloads") + .long("downloads") + .help("Folder where the downloads are stored") + .takes_value(true), + ) + .arg( + Arg::with_name("dist") + .long("dist") + .help("Folder where distribution files will be extracted") + .takes_value(true), + ) + .arg( + Arg::with_name("results") + .long("results") + .help("Folder where to store the results") + .takes_value(true), + ) + .arg( + Arg::with_name("dex2jar") + .long("dex2jar") + .help("Where to store the jar files") + .takes_value(true), + ) + .arg( + Arg::with_name("jd-cmd") + .long("jd-cmd") + .help("Path to the jd-cmd file") + .takes_value(true), + ) + .arg( + Arg::with_name("template") + .long("template") + .help("Path to a results template file") + .takes_value(true), + ) + .arg( + Arg::with_name("rules") + .long("rules") + .help("Path to a JSON rules file") + .takes_value(true), + ) } diff --git a/src/config.rs b/src/config.rs index e021c7fef..5c76cda6f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -94,26 +94,26 @@ impl Config { if cfg!(target_family = "unix") { let config_path = PathBuf::from("/etc/config.toml"); if config_path.exists() { - config - .load_from_file(&config_path) - .chain_err(|| "The config.toml file does not have the correct formatting.")?; + config.load_from_file(&config_path).chain_err( + || "The config.toml file does not have the correct formatting.", + )?; config.loaded_files.push(config_path); } } let config_path = PathBuf::from("config.toml"); if config_path.exists() { - config - .load_from_file(&config_path) - .chain_err(|| "The config.toml file does not have the correct formatting.")?; + config.load_from_file(&config_path).chain_err( + || "The config.toml file does not have the correct formatting.", + )?; config.loaded_files.push(config_path); } config.set_options(&cli); if cli.is_present("test-all") { - config - .read_apks() - .chain_err(|| "Error loading all the downloaded APKs")?; + config.read_apks().chain_err( + || "Error loading all the downloaded APKs", + )?; } else { config.add_app_package(cli.value_of("package").unwrap()); } @@ -128,13 +128,15 @@ impl Config { self.min_criticality = m; } else { - print_warning(format!("The min_criticality option must be one of {}, {}, {}, {} \ + print_warning(format!( + "The min_criticality option must be one of {}, {}, {}, {} \ or {}.\nUsing default.", - "warning".italic(), - "low".italic(), - "medium".italic(), - "high".italic(), - "critical".italic())); + "warning".italic(), + "low".italic(), + "medium".italic(), + "high".italic(), + "critical".italic() + )); } } if let Some(threads) = cli.value_of("threads") { @@ -143,9 +145,11 @@ impl Config { self.threads = t; } _ => { - print_warning(format!("The threads option must be an integer between 1 and \ + print_warning(format!( + "The threads option must be an integer between 1 and \ {}", - u8::MAX)); + u8::MAX + )); } } } @@ -181,19 +185,23 @@ impl Config { Ok(entry) => { if let Some(ext) = entry.path().extension() { if ext == "apk" { - self.add_app_package(entry - .path() - .file_stem() - .unwrap() - .to_string_lossy() - .into_owned()) + self.add_app_package( + entry + .path() + .file_stem() + .unwrap() + .to_string_lossy() + .into_owned(), + ) } } } Err(e) => { - print_warning(format!("There was an error when reading the \ + print_warning(format!( + "There was an error when reading the \ downloads folder: {}", - e.description())); + e.description() + )); } } } @@ -204,9 +212,8 @@ impl Config { /// Checks if all the needed folders and files exist. pub fn check(&self) -> bool { let check = self.downloads_folder.exists() && self.dex2jar_folder.exists() && - self.jd_cmd_file.exists() && - self.get_template_path().exists() && - self.rules_json.exists(); + self.jd_cmd_file.exists() && self.get_template_path().exists() && + self.rules_json.exists(); if check { for package in &self.app_packages { if !package.exists() { @@ -223,34 +230,49 @@ impl Config { pub fn get_errors(&self) -> Vec { let mut errors = Vec::new(); if !self.downloads_folder.exists() { - errors.push(format!("The downloads folder `{}` does not exist", - self.downloads_folder.display())); + errors.push(format!( + "The downloads folder `{}` does not exist", + self.downloads_folder.display() + )); } for package in &self.app_packages { if !package.exists() { - errors.push(format!("The APK file `{}` does not exist", package.display())); + errors.push(format!( + "The APK file `{}` does not exist", + package.display() + )); } } if !self.dex2jar_folder.exists() { - errors.push(format!("The Dex2Jar folder `{}` does not exist", - self.dex2jar_folder.display())); + errors.push(format!( + "The Dex2Jar folder `{}` does not exist", + self.dex2jar_folder.display() + )); } if !self.jd_cmd_file.exists() { - errors.push(format!("The jd-cmd file `{}` does not exist", - self.jd_cmd_file.display())); + errors.push(format!( + "The jd-cmd file `{}` does not exist", + self.jd_cmd_file.display() + )); } if !self.templates_folder.exists() { - errors.push(format!("the templates folder `{}` does not exist", - self.templates_folder.display())); + errors.push(format!( + "the templates folder `{}` does not exist", + self.templates_folder.display() + )); } if !self.get_template_path().exists() { - errors.push(format!("the template `{}` does not exist in `{}`", - self.template, - self.templates_folder.display())); + errors.push(format!( + "the template `{}` does not exist in `{}`", + self.template, + self.templates_folder.display() + )); } if !self.rules_json.exists() { - errors.push(format!("The `{}` rule file does not exist", - self.rules_json.display())); + errors.push(format!( + "The `{}` rule file does not exist", + self.rules_json.display() + )); } errors } @@ -401,8 +423,10 @@ impl Config { // Parse the configuration file. let toml = if let Value::Table(toml) = - toml.parse::() - .chain_err(|| "there was an error parsing the config.toml file")? { + toml.parse::().chain_err( + || "there was an error parsing the config.toml file", + )? + { toml } else { return Err(ErrorKind::Parse.into()); @@ -438,9 +462,11 @@ impl Config { self.threads = t as u8; } } else { - print_warning(format!("The 'threads' option in config.toml must be an integer \ + print_warning(format!( + "The 'threads' option in config.toml must be an integer \ between 1 and {}.\nUsing default.", - MAX_THREADS)) + MAX_THREADS + )) } } @@ -449,8 +475,10 @@ impl Config { if let Value::String(s) = value { self.downloads_folder = s.into(); } else { - print_warning("The 'downloads_folder' option in config.toml must be an string.\nUsing \ - default.") + print_warning( + "The 'downloads_folder' option in config.toml must be an string.\nUsing \ + default.", + ) } } @@ -459,8 +487,10 @@ impl Config { if let Value::String(s) = value { self.dist_folder = s.into(); } else { - print_warning("The 'dist_folder' option in config.toml must be an string.\nUsing \ - default."); + print_warning( + "The 'dist_folder' option in config.toml must be an string.\nUsing \ + default.", + ); } } @@ -469,8 +499,10 @@ impl Config { if let Value::String(s) = value { self.results_folder = s.into(); } else { - print_warning("The 'results_folder' option in config.toml must be an string.\nUsing \ - default."); + print_warning( + "The 'results_folder' option in config.toml must be an string.\nUsing \ + default.", + ); } } @@ -479,8 +511,10 @@ impl Config { if let Value::String(s) = value { self.dex2jar_folder = s.into(); } else { - print_warning("The 'dex2jar_folder' option in config.toml should be an string.\nUsing \ - default.") + print_warning( + "The 'dex2jar_folder' option in config.toml should be an string.\nUsing \ + default.", + ) } } @@ -494,8 +528,10 @@ impl Config { print_warning("The JD-CMD file must be a JAR file.\nUsing default."); } } else { - print_warning("The 'jd_cmd_file' option in config.toml must be an string.\nUsing \ - default.") + print_warning( + "The 'jd_cmd_file' option in config.toml must be an string.\nUsing \ + default.", + ) } } @@ -504,8 +540,10 @@ impl Config { if let Value::String(ref s) = value { self.templates_folder = s.into(); } else { - print_warning("The 'templates_folder' option in config.toml should be an string.\n\ - Using default.") + print_warning( + "The 'templates_folder' option in config.toml should be an string.\n\ + Using default.", + ) } } @@ -514,8 +552,10 @@ impl Config { if let Value::String(s) = value { self.template = s; } else { - print_warning("The 'template' option in config.toml should be an string.\nUsing \ - default.") + print_warning( + "The 'template' option in config.toml should be an string.\nUsing \ + default.", + ) } } @@ -526,24 +566,30 @@ impl Config { if extension.is_some() && extension.unwrap() == "json" { self.rules_json = PathBuf::from(s.clone()); } else { - print_warning("The rules.json file must be a JSON \ - file.\nUsing default.") + print_warning( + "The rules.json file must be a JSON \ + file.\nUsing default.", + ) } } else { - print_warning("The 'rules_json' option in config.toml must be an string.\nUsing \ - default.") + print_warning( + "The 'rules_json' option in config.toml must be an string.\nUsing \ + default.", + ) } } /// Loads permissions from the TOML configuration vector. fn load_permissions(&mut self, value: Value) { if let Value::Array(permissions) = value { - let format_warning = format!("The permission configuration format must be the following:\n{}\nUsing \ + let format_warning = format!( + "The permission configuration format must be the following:\n{}\nUsing \ default.", - "[[permissions]]\nname=\"unknown|permission.name\"\ncriticality = \ + "[[permissions]]\nname=\"unknown|permission.name\"\ncriticality = \ \"warning|low|medium|high|critical\"\nlabel = \"Permission \ label\"\ndescription = \"Long description to explain the vulnerability\"" - .italic()); + .italic() + ); for cfg in permissions { let cfg = if let Some(t) = cfg.as_table() { @@ -564,13 +610,15 @@ impl Config { if let Ok(c) = Criticality::from_str(c) { c } else { - print_warning(format!("Criticality must be one of {}, {}, {}, {} or \ + print_warning(format!( + "Criticality must be one of {}, {}, {}, {} or \ {}.\nUsing default.", - "warning".italic(), - "low".italic(), - "medium".italic(), - "high".italic(), - "critical".italic())); + "warning".italic(), + "low".italic(), + "medium".italic(), + "high".italic(), + "critical".italic() + )); break; } } else { @@ -587,14 +635,16 @@ impl Config { if name == "unknown" { if cfg.len() != 3 { - print_warning(format!("The format for the unknown \ + print_warning(format!( + "The format for the unknown \ permissions is the following:\n{}\nUsing default.", - "[[permissions]]\nname = \ + "[[permissions]]\nname = \ \"unknown\"\ncriticality = \ \"warning|low|medium|high|criticality\"\n\ description = \"Long description to explain \ the vulnerability\"" - .italic())); + .italic() + )); break; } @@ -608,13 +658,15 @@ impl Config { let permission = if let Ok(p) = Permission::from_str(name) { p } else { - print_warning(format!("Unknown permission: {}\nTo set the default \ + print_warning(format!( + "Unknown permission: {}\nTo set the default \ vulnerability level for an unknown permission, \ please, use the {} permission name, under the {} \ section.", - name.italic(), - "unknown".italic(), - "[[permissions]]".italic())); + name.italic(), + "unknown".italic(), + "[[permissions]]".italic() + )); break; }; @@ -624,12 +676,18 @@ impl Config { print_warning(format_warning); break; }; - self.permissions - .insert(PermissionConfig::new(permission, criticality, label, description)); + self.permissions.insert(PermissionConfig::new( + permission, + criticality, + label, + description, + )); } } } else { - print_warning("You must specify the permissions you want to select as vulnerable."); + print_warning( + "You must specify the permissions you want to select as vulnerable.", + ); } } @@ -638,8 +696,10 @@ impl Config { if let Value::Boolean(b) = value { self.html = b } else { - print_warning("The 'html_report' option in config.toml should be a boolean.\nUsing \ - default."); + print_warning( + "The 'html_report' option in config.toml should be a boolean.\nUsing \ + default.", + ); } } @@ -648,8 +708,10 @@ impl Config { if let Value::Boolean(b) = value { self.json = b; } else { - print_warning("The 'json_report' option in config.toml should be a boolean.\nUsing \ - default."); + print_warning( + "The 'json_report' option in config.toml should be a boolean.\nUsing \ + default.", + ); } } @@ -675,10 +737,14 @@ impl Config { templates_folder: PathBuf::from("templates"), template: String::from("super"), rules_json: PathBuf::from("rules.json"), - unknown_permission: (Criticality::Low, - String::from("Even if the application can create its own \ + unknown_permission: ( + Criticality::Low, + String::from( + "Even if the application can create its own \ permissions, it's discouraged, since it can \ - lead to missunderstanding between developers.")), + lead to missunderstanding between developers.", + ), + ), permissions: BTreeSet::new(), loaded_files: Vec::new(), } @@ -695,10 +761,10 @@ impl Default for Config { config.rules_json = etc_rules; } let share_path = Path::new(if cfg!(target_os = "macos") { - "/usr/local/super-analyzer" - } else { - "/usr/share/super-analyzer" - }); + "/usr/local/super-analyzer" + } else { + "/usr/share/super-analyzer" + }); if share_path.exists() { config.dex2jar_folder = share_path.join("vendor/dex2jar-2.1-SNAPSHOT"); config.jd_cmd_file = share_path.join("vendor/jd-cmd.jar"); @@ -750,11 +816,12 @@ impl PartialOrd for PermissionConfig { impl PermissionConfig { /// Creates a new `PermissionConfig`. - fn new, D: Into>(permission: Permission, - criticality: Criticality, - label: L, - description: D) - -> PermissionConfig { + fn new, D: Into>( + permission: Permission, + criticality: Criticality, + label: L, + description: D, + ) -> PermissionConfig { PermissionConfig { permission: permission, criticality: criticality, @@ -814,8 +881,8 @@ mod tests { assert_eq!(config.get_results_folder(), Path::new("results")); assert_eq!(config.get_template_name(), "super"); let share_path = Path::new(if cfg!(target_os = "macos") { - "/usr/local/super-analyzer" - } else if cfg!(target_family = "windows") { + "/usr/local/super-analyzer" + } else if cfg!(target_family = "windows") { "" } else { "/usr/share/super-analyzer" @@ -825,24 +892,36 @@ mod tests { } else { Path::new("") }; - assert_eq!(config.get_dex2jar_folder(), - share_path.join("vendor").join("dex2jar-2.1-SNAPSHOT")); - assert_eq!(config.get_jd_cmd_file(), - share_path.join("vendor").join("jd-cmd.jar")); + assert_eq!( + config.get_dex2jar_folder(), + share_path.join("vendor").join("dex2jar-2.1-SNAPSHOT") + ); + assert_eq!( + config.get_jd_cmd_file(), + share_path.join("vendor").join("jd-cmd.jar") + ); assert_eq!(config.get_templates_folder(), share_path.join("templates")); - assert_eq!(config.get_template_path(), - share_path.join("templates").join("super")); + assert_eq!( + config.get_template_path(), + share_path.join("templates").join("super") + ); if cfg!(target_family = "unix") && Path::new("/etc/super-analyzer/rules.json").exists() { - assert_eq!(config.get_rules_json(), - Path::new("/etc/super-analyzer/rules.json")); + assert_eq!( + config.get_rules_json(), + Path::new("/etc/super-analyzer/rules.json") + ); } else { assert_eq!(config.get_rules_json(), Path::new("rules.json")); } - assert_eq!(config.get_unknown_permission_criticality(), - Criticality::Low); - assert_eq!(config.get_unknown_permission_description(), - "Even if the application can create its own permissions, it's discouraged, \ - since it can lead to missunderstanding between developers."); + assert_eq!( + config.get_unknown_permission_criticality(), + Criticality::Low + ); + assert_eq!( + config.get_unknown_permission_description(), + "Even if the application can create its own permissions, it's discouraged, \ + since it can lead to missunderstanding between developers." + ); assert_eq!(config.get_permissions().next(), None); if !config.downloads_folder.exists() { @@ -906,33 +985,51 @@ mod tests { assert_eq!(config.downloads_folder, Path::new("downloads")); assert_eq!(config.get_dist_folder(), Path::new("dist")); assert_eq!(config.get_results_folder(), Path::new("results")); - assert_eq!(config.get_dex2jar_folder(), - Path::new("/usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT")); - assert_eq!(config.get_jd_cmd_file(), - Path::new("/usr/share/super-analyzer/vendor/jd-cmd.jar")); - assert_eq!(config.get_templates_folder(), - Path::new("/usr/share/super-analyzer/templates")); - assert_eq!(config.get_template_path(), - Path::new("/usr/share/super-analyzer/templates/super")); + assert_eq!( + config.get_dex2jar_folder(), + Path::new("/usr/share/super-analyzer/vendor/dex2jar-2.1-SNAPSHOT") + ); + assert_eq!( + config.get_jd_cmd_file(), + Path::new("/usr/share/super-analyzer/vendor/jd-cmd.jar") + ); + assert_eq!( + config.get_templates_folder(), + Path::new("/usr/share/super-analyzer/templates") + ); + assert_eq!( + config.get_template_path(), + Path::new("/usr/share/super-analyzer/templates/super") + ); assert_eq!(config.get_template_name(), "super"); - assert_eq!(config.get_rules_json(), - Path::new("/etc/super-analyzer/rules.json")); - assert_eq!(config.get_unknown_permission_criticality(), - Criticality::Low); - assert_eq!(config.get_unknown_permission_description(), - "Even if the application can create its own permissions, it's discouraged, \ - since it can lead to missunderstanding between developers."); + assert_eq!( + config.get_rules_json(), + Path::new("/etc/super-analyzer/rules.json") + ); + assert_eq!( + config.get_unknown_permission_criticality(), + Criticality::Low + ); + assert_eq!( + config.get_unknown_permission_description(), + "Even if the application can create its own permissions, it's discouraged, \ + since it can lead to missunderstanding between developers." + ); let permission = config.get_permissions().next().unwrap(); - assert_eq!(permission.get_permission(), - Permission::AndroidPermissionInternet); + assert_eq!( + permission.get_permission(), + Permission::AndroidPermissionInternet + ); assert_eq!(permission.get_criticality(), Criticality::Warning); assert_eq!(permission.get_label(), "Internet permission"); - assert_eq!(permission.get_description(), - "Allows the app to create network sockets and use custom network protocols. \ + assert_eq!( + permission.get_description(), + "Allows the app to create network sockets and use custom network protocols. \ The browser and other applications provide means to send data to the \ internet, so this permission is not required to send data to the internet. \ - Check if the permission is actually needed."); + Check if the permission is actually needed." + ); } /// Test to check a valid jd cmd file section is loaded @@ -943,8 +1040,10 @@ mod tests { final_config.load_jd_cmd_file_section(value); - assert_eq!(PathBuf::from("/some/path/to/jd-cmd.jar"), - final_config.jd_cmd_file) + assert_eq!( + PathBuf::from("/some/path/to/jd-cmd.jar"), + final_config.jd_cmd_file + ) } /// Test to check an invalid jd cmd file section is not loaded @@ -953,8 +1052,10 @@ mod tests { let default_config = Config::default(); let mut final_config = Config::default(); - let values = vec![Value::String("/some/invalid/js_cmd.jpg".to_string()), - Value::Integer(20)]; + let values = vec![ + Value::String("/some/invalid/js_cmd.jpg".to_string()), + Value::Integer(20), + ]; for value in values { final_config.load_jd_cmd_file_section(value); @@ -1001,15 +1102,23 @@ mod tests { final_config.load_templates_folder_section(Value::String(str_value.clone())); final_config.load_template_section(Value::String(str_value.clone())); - assert_eq!(final_config.downloads_folder, - PathBuf::from(str_value.clone())); + assert_eq!( + final_config.downloads_folder, + PathBuf::from(str_value.clone()) + ); assert_eq!(final_config.dist_folder, PathBuf::from(str_value.clone())); - assert_eq!(final_config.results_folder, - PathBuf::from(str_value.clone())); - assert_eq!(final_config.dex2jar_folder, - PathBuf::from(str_value.clone())); - assert_eq!(final_config.templates_folder, - PathBuf::from(str_value.clone())); + assert_eq!( + final_config.results_folder, + PathBuf::from(str_value.clone()) + ); + assert_eq!( + final_config.dex2jar_folder, + PathBuf::from(str_value.clone()) + ); + assert_eq!( + final_config.templates_folder, + PathBuf::from(str_value.clone()) + ); assert_eq!(final_config.template, str_value.clone()); } @@ -1033,8 +1142,10 @@ mod tests { final_config.load_rules_section(value); - assert_eq!(PathBuf::from("/some/path/to/rules.json"), - final_config.rules_json) + assert_eq!( + PathBuf::from("/some/path/to/rules.json"), + final_config.rules_json + ) } /// Test to check an invalid rules section is not loaded @@ -1043,8 +1154,10 @@ mod tests { let default_config = Config::default(); let mut final_config = Config::default(); - let values = vec![Value::String("/some/invalid/rules.jpg".to_string()), - Value::Integer(20)]; + let values = vec![ + Value::String("/some/invalid/rules.jpg".to_string()), + Value::Integer(20), + ]; for value in values { final_config.load_rules_section(value); @@ -1061,31 +1174,41 @@ mod tests { let mut permission_invalid_criticality: BTreeMap = BTreeMap::new(); permission_invalid_criticality - .insert("name".to_string(), - Value::String("permission_name".to_string())) + .insert( + "name".to_string(), + Value::String("permission_name".to_string()), + ) .is_some(); permission_invalid_criticality - .insert("criticality".to_string(), - Value::String("invalid_level".to_string())) + .insert( + "criticality".to_string(), + Value::String("invalid_level".to_string()), + ) .is_some(); let mut permission_without_criticality: BTreeMap = BTreeMap::new(); permission_without_criticality - .insert("name".to_string(), - Value::String("permission_name".to_string())) + .insert( + "name".to_string(), + Value::String("permission_name".to_string()), + ) .is_some(); let mut permission_without_description: BTreeMap = BTreeMap::new(); permission_without_description - .insert("name".to_string(), - Value::String("permission_name".to_string())) + .insert( + "name".to_string(), + Value::String("permission_name".to_string()), + ) .is_some(); permission_without_description .insert("criticality".to_string(), Value::String("low".to_string())) .is_some(); permission_without_description - .insert("description".to_string(), - Value::String("permission description".to_string())) + .insert( + "description".to_string(), + Value::String("permission description".to_string()), + ) .is_some(); let mut permission_unknown_too_much_values: BTreeMap = BTreeMap::new(); @@ -1096,45 +1219,59 @@ mod tests { .insert("criticality".to_string(), Value::String("low".to_string())) .is_some(); permission_unknown_too_much_values - .insert("description".to_string(), - Value::String("permission description".to_string())) + .insert( + "description".to_string(), + Value::String("permission description".to_string()), + ) .is_some(); permission_unknown_too_much_values - .insert("additional_field".to_string(), - Value::String("additional field data".to_string())) + .insert( + "additional_field".to_string(), + Value::String("additional field data".to_string()), + ) .is_some(); let mut permission_known_too_much_values: BTreeMap = BTreeMap::new(); permission_known_too_much_values - .insert("name".to_string(), - Value::String("android.permission.ACCESS_ALL_EXTERNAL_STORAGE".to_string())) + .insert( + "name".to_string(), + Value::String("android.permission.ACCESS_ALL_EXTERNAL_STORAGE".to_string()), + ) .is_some(); permission_known_too_much_values .insert("criticality".to_string(), Value::String("low".to_string())) .is_some(); permission_known_too_much_values - .insert("description".to_string(), - Value::String("permission description".to_string())) + .insert( + "description".to_string(), + Value::String("permission description".to_string()), + ) .is_some(); permission_known_too_much_values .insert("label".to_string(), Value::String("label".to_string())) .is_some(); permission_known_too_much_values - .insert("additional_field".to_string(), - Value::String("additional field data".to_string())) + .insert( + "additional_field".to_string(), + Value::String("additional field data".to_string()), + ) .is_some(); let mut permission_known_name_not_found: BTreeMap = BTreeMap::new(); permission_known_name_not_found - .insert("name".to_string(), - Value::String("invalid name".to_string())) + .insert( + "name".to_string(), + Value::String("invalid name".to_string()), + ) .is_some(); permission_known_name_not_found .insert("criticality".to_string(), Value::String("low".to_string())) .is_some(); permission_known_name_not_found - .insert("description".to_string(), - Value::String("permission description".to_string())) + .insert( + "description".to_string(), + Value::String("permission description".to_string()), + ) .is_some(); permission_known_name_not_found .insert("label".to_string(), Value::String("label".to_string())) @@ -1142,37 +1279,47 @@ mod tests { let mut permission_without_label: BTreeMap = BTreeMap::new(); permission_without_label - .insert("name".to_string(), - Value::String("invalid name".to_string())) + .insert( + "name".to_string(), + Value::String("invalid name".to_string()), + ) .is_some(); permission_without_label .insert("criticality".to_string(), Value::String("low".to_string())) .is_some(); permission_without_label - .insert("description".to_string(), - Value::String("permission description".to_string())) + .insert( + "description".to_string(), + Value::String("permission description".to_string()), + ) .is_some(); permission_without_label - .insert("additional_field".to_string(), - Value::String("additional field data".to_string())) + .insert( + "additional_field".to_string(), + Value::String("additional field data".to_string()), + ) .is_some(); - let permissions = vec![Value::Integer(20), - Value::Table(permission_without_name), - Value::Table(permission_invalid_criticality), - Value::Table(permission_without_criticality), - Value::Table(permission_without_description), - Value::Table(permission_unknown_too_much_values), - Value::Table(permission_known_too_much_values), - Value::Table(permission_known_name_not_found), - Value::Table(permission_without_label)]; + let permissions = vec![ + Value::Integer(20), + Value::Table(permission_without_name), + Value::Table(permission_invalid_criticality), + Value::Table(permission_without_criticality), + Value::Table(permission_without_description), + Value::Table(permission_unknown_too_much_values), + Value::Table(permission_known_too_much_values), + Value::Table(permission_known_name_not_found), + Value::Table(permission_without_label), + ]; for p in permissions { final_config.load_permissions(Value::Array(vec![p])); assert_eq!(default_config.permissions, final_config.permissions); - assert_eq!(default_config.unknown_permission, - final_config.unknown_permission); + assert_eq!( + default_config.unknown_permission, + final_config.unknown_permission + ); } } @@ -1189,15 +1336,21 @@ mod tests { .insert("criticality".to_string(), Value::String("low".to_string())) .is_some(); unknown_permission - .insert("description".to_string(), - Value::String("permission description".to_string())) + .insert( + "description".to_string(), + Value::String("permission description".to_string()), + ) .is_some(); final_config.load_permissions(Value::Array(vec![Value::Table(unknown_permission)])); - assert_eq!(final_config.get_unknown_permission_criticality(), - Criticality::from_str("low").unwrap()); - assert_eq!(final_config.get_unknown_permission_description(), - "permission description"); + assert_eq!( + final_config.get_unknown_permission_criticality(), + Criticality::from_str("low").unwrap() + ); + assert_eq!( + final_config.get_unknown_permission_description(), + "permission description" + ); } #[test] @@ -1206,15 +1359,19 @@ mod tests { let mut unknown_permission: BTreeMap = BTreeMap::new(); unknown_permission - .insert("name".to_string(), - Value::String("android.permission.ACCESS_ALL_EXTERNAL_STORAGE".to_string())) + .insert( + "name".to_string(), + Value::String("android.permission.ACCESS_ALL_EXTERNAL_STORAGE".to_string()), + ) .is_some(); unknown_permission .insert("criticality".to_string(), Value::String("low".to_string())) .is_some(); unknown_permission - .insert("description".to_string(), - Value::String("permission description".to_string())) + .insert( + "description".to_string(), + Value::String("permission description".to_string()), + ) .is_some(); unknown_permission .insert("label".to_string(), Value::String("label".to_string())) diff --git a/src/decompilation.rs b/src/decompilation.rs index 79181677a..32e1bb5b7 100644 --- a/src/decompilation.rs +++ b/src/decompilation.rs @@ -15,9 +15,12 @@ use {Config, print_warning, get_package_name}; /// Decompresses the application using _Apktool_. pub fn decompress>(config: &mut Config, package: P) -> Result<()> { - let path = config - .get_dist_folder() - .join(package.as_ref().file_stem().unwrap()); + let path = config.get_dist_folder().join( + package + .as_ref() + .file_stem() + .unwrap(), + ); if !path.exists() || config.is_force() { if path.exists() { if config.is_verbose() { @@ -25,9 +28,11 @@ pub fn decompress>(config: &mut Config, package: P) -> Result<()> } if let Err(e) = fs::remove_dir_all(&path) { - print_warning(format!("There was an error when removing the decompression \ + print_warning(format!( + "There was an error when removing the decompression \ folder: {}", - e.description())); + e.description() + )); } } config.set_force(); @@ -37,22 +42,29 @@ pub fn decompress>(config: &mut Config, package: P) -> Result<()> println!("Decompressing the application…"); } - let mut apk = Apk::new(package.as_ref()) - .chain_err(|| "error loading apk file")?; - apk.export(&path, true) - .chain_err(|| "could not decompress the apk file")?; + let mut apk = Apk::new(package.as_ref()).chain_err( + || "error loading apk file", + )?; + apk.export(&path, true).chain_err( + || "could not decompress the apk file", + )?; if config.is_verbose() { - println!("{}", - format!("The application has been decompressed in {}.", - path.display()) - .green()); + println!( + "{}", + format!( + "The application has been decompressed in {}.", + path.display() + ).green() + ); } else if !config.is_quiet() { println!("Application decompressed."); } } else if config.is_verbose() { - println!("Seems that the application has already been decompressed. There is no need to \ - do it again."); + println!( + "Seems that the application has already been decompressed. There is no need to \ + do it again." + ); } else { println!("Skipping decompression."); } @@ -63,31 +75,34 @@ pub fn decompress>(config: &mut Config, package: P) -> Result<()> /// Converts _.dex_ files to _.jar_ using _Dex2jar_. pub fn dex_to_jar>(config: &mut Config, package: P) -> Result<()> { let package_name = get_package_name(package.as_ref()); - let classes = config - .get_dist_folder() - .join(&package_name) - .join("classes.jar"); + let classes = config.get_dist_folder().join(&package_name).join( + "classes.jar", + ); if config.is_force() || !classes.exists() { config.set_force(); // Command to convert .dex to .jar. using dex2jar. // "-o path" to specify an output file - let output = Command::new(config.get_dex2jar_folder() - .join(if cfg!(target_family = "windows") { - "d2j-dex2jar.bat" - } else { - "d2j-dex2jar.sh" - })).arg(config.get_dist_folder() - .join(&package_name) - .join("classes.dex")) + let output = Command::new(config.get_dex2jar_folder().join(if cfg!( + target_family = "windows" + ) + { + "d2j-dex2jar.bat" + } else { + "d2j-dex2jar.sh" + })).arg(config.get_dist_folder().join(&package_name).join( + "classes.dex", + )) .arg("-f") .arg("-o") .arg(&classes) .output() .chain_err(|| { - format!("There was an error when executing the {} to {} conversion command", - ".dex".italic(), - ".jar".italic()) + format!( + "There was an error when executing the {} to {} conversion command", + ".dex".italic(), + ".jar".italic() + ) })?; let stderr = String::from_utf8_lossy(&output.stderr); @@ -96,30 +111,38 @@ pub fn dex_to_jar>(config: &mut Config, package: P) -> Result<()> // exception that was produced. But, the thing is that in some cases it does not return an // exception, so we have to check if errors such as "use certain option" occur. if !output.status.success() || stderr.find('\n') != Some(stderr.len() - 1) || - stderr.contains("use") { - let message = format!("The {} to {} conversion command returned an error. More info: \ + stderr.contains("use") + { + let message = format!( + "The {} to {} conversion command returned an error. More info: \ {}", - ".dex".italic(), - ".jar".italic(), - stderr); + ".dex".italic(), + ".jar".italic(), + stderr + ); return Err(message.into()); } if config.is_verbose() { - println!("{}", - format!("The application {} {} {}", - ".jar".italic(), - "file has been generated in".green(), - format!("{}", classes.display()).green()) - .green()); + println!( + "{}", + format!( + "The application {} {} {}", + ".jar".italic(), + "file has been generated in".green(), + format!("{}", classes.display()).green() + ).green() + ); } else if !config.is_quiet() { println!("Jar file generated."); } } else if config.is_verbose() { - println!("Seems that there is already a {} file for the application. There is no need to \ + println!( + "Seems that there is already a {} file for the application. There is no need to \ create it again.", - ".jar".italic()); + ".jar".italic() + ); } else { println!("Skipping {} file generation.", ".jar".italic()); } @@ -130,10 +153,7 @@ pub fn dex_to_jar>(config: &mut Config, package: P) -> Result<()> /// Decompiles the application using _jd\_cmd_. pub fn decompile>(config: &mut Config, package: P) -> Result<()> { let package_name = get_package_name(package.as_ref()); - let out_path = config - .get_dist_folder() - .join(&package_name) - .join("classes"); + let out_path = config.get_dist_folder().join(&package_name).join("classes"); if config.is_force() || !out_path.exists() { config.set_force(); @@ -142,30 +162,35 @@ pub fn decompile>(config: &mut Config, package: P) -> Result<()> let output = Command::new("java") .arg("-jar") .arg(config.get_jd_cmd_file()) - .arg(config - .get_dist_folder() - .join(&package_name) - .join("classes.jar")) + .arg(config.get_dist_folder().join(&package_name).join( + "classes.jar", + )) .arg("-od") .arg(&out_path) .output() .chain_err(|| "There was an unknown error decompiling the application")?; if !output.status.success() { - let message = format!("The decompilation command returned an error. More info:\n{}", - String::from_utf8_lossy(&output.stdout)); + let message = format!( + "The decompilation command returned an error. More info:\n{}", + String::from_utf8_lossy(&output.stdout) + ); return Err(message.into()); } if config.is_verbose() { - println!("{}", - "The application has been succesfully decompiled!".green()); + println!( + "{}", + "The application has been succesfully decompiled!".green() + ); } else if !config.is_quiet() { println!("Application decompiled."); } } else if config.is_verbose() { - println!("Seems that there is already a source folder for the application. There is no \ - need to decompile it again."); + println!( + "Seems that there is already a source folder for the application. There is no \ + need to decompile it again." + ); } else { println!("Skipping decompilation."); } diff --git a/src/main.rs b/src/main.rs index d39240454..eeb7b079e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,8 +85,10 @@ fn main() { } if !log_enabled!(LogLevel::Debug) { - println!("If you need more information, try to run the program again with the {} flag.", - "-v".bold()); + println!( + "If you need more information, try to run the program again with the {} flag.", + "-v".bold() + ); } if let Some(backtrace) = e.backtrace() { @@ -108,8 +110,10 @@ fn run() -> Result<()> { let mut config = match Config::from_cli(cli) { Ok(c) => c, Err(e) => { - print_warning(format!("There was an error when reading the config.toml file: {}", - e.description())); + print_warning(format!( + "There was an error when reading the config.toml file: {}", + e.description() + )); Config::default() } @@ -121,8 +125,10 @@ fn run() -> Result<()> { error_string.push_str(&error); error_string.push('\n'); } - error_string.push_str("The configuration was loaded, in order, from the following files: \ - \n\t- Default built-in configuration\n"); + error_string.push_str( + "The configuration was loaded, in order, from the following files: \ + \n\t- Default built-in configuration\n", + ); for file in config.get_loaded_config_files() { error_string.push_str(&format!("\t- {}\n", file.display())); } @@ -136,10 +142,14 @@ fn run() -> Result<()> { io::stdout().flush().unwrap(); sleep(Duration::from_millis(3)); } - println!("Welcome to the SUPER Android Analyzer. We will now try to audit the given \ - application."); - println!("You activated the verbose mode. {}", - "May Tux be with you!".bold()); + println!( + "Welcome to the SUPER Android Analyzer. We will now try to audit the given \ + application." + ); + println!( + "You activated the verbose mode. {}", + "May Tux be with you!".bold() + ); println!(); sleep(Duration::from_millis(1250)); } @@ -171,10 +181,11 @@ fn run() -> Result<()> { } /// Analyzes the given package with the given config. -fn analyze_package>(package: P, - config: &mut Config, - benchmarks: &mut BTreeMap>) - -> Result<()> { +fn analyze_package>( + package: P, + config: &mut Config, + benchmarks: &mut BTreeMap>, +) -> Result<()> { let package_name = get_package_name(&package); if config.is_bench() { let _ = benchmarks.insert(package_name.clone(), Vec::with_capacity(4)); @@ -186,47 +197,59 @@ fn analyze_package>(package: P, let start_time = Instant::now(); // Apk decompression - decompress(config, &package) - .chain_err(|| "apk decompression failed")?; + decompress(config, &package).chain_err( + || "apk decompression failed", + )?; if config.is_bench() { - benchmarks - .get_mut(&package_name) - .unwrap() - .push(Benchmark::new("Apk decompression", start_time.elapsed())); + benchmarks.get_mut(&package_name).unwrap().push( + Benchmark::new( + "Apk decompression", + start_time + .elapsed(), + ), + ); } let dex_jar_time = Instant::now(); // Converting the .dex to .jar. - dex_to_jar(config, &package) - .chain_err(|| "Conversion from DEX to JAR failed")?; + dex_to_jar(config, &package).chain_err( + || "Conversion from DEX to JAR failed", + )?; if config.is_bench() { - benchmarks - .get_mut(&package_name) - .unwrap() - .push(Benchmark::new("Dex to Jar decompilation (dex2jar Java dependency)", - dex_jar_time.elapsed())); + benchmarks.get_mut(&package_name).unwrap().push( + Benchmark::new( + "Dex to Jar decompilation (dex2jar Java dependency)", + dex_jar_time + .elapsed(), + ), + ); } if config.is_verbose() { println!(); - println!("Now it's time for the actual decompilation of the source code. We'll translate - Android JVM bytecode to Java, so that we can check the code afterwards."); + println!( + "Now it's time for the actual decompilation of the source code. We'll translate + Android JVM bytecode to Java, so that we can check the code afterwards." + ); } let decompile_start = Instant::now(); // Decompiling the app - decompile(config, &package) - .chain_err(|| "JAR decompression failed")?; + decompile(config, &package).chain_err( + || "JAR decompression failed", + )?; if config.is_bench() { - benchmarks - .get_mut(&package_name) - .unwrap() - .push(Benchmark::new("Decompilation (jd-cli Java dependency)", - decompile_start.elapsed())); + benchmarks.get_mut(&package_name).unwrap().push( + Benchmark::new( + "Decompilation (jd-cli Java dependency)", + decompile_start + .elapsed(), + ), + ); } let mut results = Results::init(config, &package)?; @@ -235,10 +258,13 @@ fn analyze_package>(package: P, static_analysis(config, &package_name, &mut results); if config.is_bench() { - benchmarks - .get_mut(&package_name) - .unwrap() - .push(Benchmark::new("Total static analysis", static_start.elapsed())); + benchmarks.get_mut(&package_name).unwrap().push( + Benchmark::new( + "Total static analysis", + static_start + .elapsed(), + ), + ); } // TODO dynamic analysis @@ -248,29 +274,39 @@ fn analyze_package>(package: P, } let report_start = Instant::now(); - results - .generate_report(config, &package_name) - .chain_err(|| "There was an error generating the results report")?; + results.generate_report(config, &package_name).chain_err( + || "There was an error generating the results report", + )?; if config.is_verbose() { println!("Everything went smoothly, now you can check all the results."); println!(); println!("I will now analyze myself for vulnerabilities…"); sleep(Duration::from_millis(1500)); - println!("Nah, just kidding, I've been developed in {}!", - "Rust".bold().green()) + println!( + "Nah, just kidding, I've been developed in {}!", + "Rust".bold().green() + ) } if config.is_bench() { - benchmarks - .get_mut(&package_name) - .unwrap() - .push(Benchmark::new("Report generation", report_start.elapsed())); - benchmarks - .get_mut(&package_name) - .unwrap() - .push(Benchmark::new(format!("Total time for {}", package_name), - start_time.elapsed())); + benchmarks.get_mut(&package_name).unwrap().push( + Benchmark::new( + "Report generation", + report_start + .elapsed(), + ), + ); + benchmarks.get_mut(&package_name).unwrap().push( + Benchmark::new( + format!( + "Total time for {}", + package_name + ), + start_time + .elapsed(), + ), + ); } if config.is_open() { @@ -286,11 +322,14 @@ fn analyze_package>(package: P, .join("results.json") }; - let status = open::that(open_path) - .chain_err(|| "Report could not be opened automatically")?; + let status = open::that(open_path).chain_err( + || "Report could not be opened automatically", + )?; if !status.success() { - return Err(format!("Report opening errored with status code: {}", status).into()); + return Err( + format!("Report opening errored with status code: {}", status).into(), + ); } } @@ -321,7 +360,8 @@ impl Display for Criticality { impl Serialize for Criticality { fn serialize(&self, serializer: S) -> result::Result - where S: Serializer + where + S: Serializer, { serializer.serialize_str(format!("{}", self).as_str()) } @@ -365,14 +405,18 @@ pub fn copy_folder>(from: P, to: P) -> Result<()> { fn initialize_logger(is_verbose: bool) { let format = |record: &LogRecord| match record.level() { LogLevel::Warn => { - format!("{}{}", - "Warning: ".bold().yellow(), - record.args().to_string().yellow()) + format!( + "{}{}", + "Warning: ".bold().yellow(), + record.args().to_string().yellow() + ) } LogLevel::Error => { - format!("{}{}", - "Error: ".bold().red(), - record.args().to_string().red()) + format!( + "{}{}", + "Error: ".bold().red(), + record.args().to_string().red() + ) } LogLevel::Debug => format!("{}{}", "Debug: ".bold(), record.args().to_string().bold()), LogLevel::Info => format!("{}", record.args()), @@ -408,34 +452,52 @@ mod tests { #[test] fn it_criticality() { - assert_eq!(Criticality::from_str("warning").unwrap(), - Criticality::Warning); - assert_eq!(Criticality::from_str("Warning").unwrap(), - Criticality::Warning); - assert_eq!(Criticality::from_str("WARNING").unwrap(), - Criticality::Warning); + assert_eq!( + Criticality::from_str("warning").unwrap(), + Criticality::Warning + ); + assert_eq!( + Criticality::from_str("Warning").unwrap(), + Criticality::Warning + ); + assert_eq!( + Criticality::from_str("WARNING").unwrap(), + Criticality::Warning + ); assert_eq!(Criticality::from_str("low").unwrap(), Criticality::Low); assert_eq!(Criticality::from_str("Low").unwrap(), Criticality::Low); assert_eq!(Criticality::from_str("LOW").unwrap(), Criticality::Low); - assert_eq!(Criticality::from_str("medium").unwrap(), - Criticality::Medium); - assert_eq!(Criticality::from_str("Medium").unwrap(), - Criticality::Medium); - assert_eq!(Criticality::from_str("MEDIUM").unwrap(), - Criticality::Medium); + assert_eq!( + Criticality::from_str("medium").unwrap(), + Criticality::Medium + ); + assert_eq!( + Criticality::from_str("Medium").unwrap(), + Criticality::Medium + ); + assert_eq!( + Criticality::from_str("MEDIUM").unwrap(), + Criticality::Medium + ); assert_eq!(Criticality::from_str("high").unwrap(), Criticality::High); assert_eq!(Criticality::from_str("High").unwrap(), Criticality::High); assert_eq!(Criticality::from_str("HIGH").unwrap(), Criticality::High); - assert_eq!(Criticality::from_str("critical").unwrap(), - Criticality::Critical); - assert_eq!(Criticality::from_str("Critical").unwrap(), - Criticality::Critical); - assert_eq!(Criticality::from_str("CRITICAL").unwrap(), - Criticality::Critical); + assert_eq!( + Criticality::from_str("critical").unwrap(), + Criticality::Critical + ); + assert_eq!( + Criticality::from_str("Critical").unwrap(), + Criticality::Critical + ); + assert_eq!( + Criticality::from_str("CRITICAL").unwrap(), + Criticality::Critical + ); assert!(Criticality::Warning < Criticality::Low); assert!(Criticality::Warning < Criticality::Medium); diff --git a/src/results/handlebars_helpers.rs b/src/results/handlebars_helpers.rs index 560cc2009..6e4fb2755 100644 --- a/src/results/handlebars_helpers.rs +++ b/src/results/handlebars_helpers.rs @@ -11,19 +11,23 @@ use super::utils::{split_indent, html_escape}; /// An optional line separator can be added that will be used at the end of each line. By default, /// this separator will be `
`. pub fn line_numbers(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> { - let vulnerability = h.param(0) - .and_then(|v| v.value().as_object()) - .ok_or_else(|| { - RenderError::new("to generate the vulnerability index, the first parameter must be a \ - vulnerability") - })?; + let vulnerability = h.param(0).and_then(|v| v.value().as_object()).ok_or_else( + || { + RenderError::new( + "to generate the vulnerability index, the first parameter must be a \ + vulnerability", + ) + }, + )?; let line_separator = match h.param(1) { Some(s) => { if let Value::String(ref s) = *s.value() { s } else { - return Err(RenderError::new("the provided line separator for the code lines was \ - not a string")); + return Err(RenderError::new( + "the provided line separator for the code lines was \ + not a string", + )); } } None => "
", @@ -32,24 +36,17 @@ pub fn line_numbers(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Resul let line = l.as_i64().unwrap(); (line, line) } else { - let start_line = vulnerability - .get("start_line") - .unwrap() - .as_i64() - .unwrap(); - let end_line = vulnerability - .get("end_line") - .unwrap() - .as_i64() - .unwrap(); + let start_line = vulnerability.get("start_line").unwrap().as_i64().unwrap(); + let end_line = vulnerability.get("end_line").unwrap().as_i64().unwrap(); (start_line, end_line) }; let iter_start = if start_line > 5 { start_line - 4 } else { 1 }; let iter_end = end_line + 5; - let mut rendered = String::with_capacity((line_separator.len() + 1) * - (iter_end - iter_start) as usize); + let mut rendered = String::with_capacity( + (line_separator.len() + 1) * (iter_end - iter_start) as usize, + ); for l in iter_start..iter_end { rendered.push_str(&format!("{}", l)); rendered.push_str(line_separator); @@ -64,16 +61,18 @@ pub fn line_numbers(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Resul /// An optional line separator can be added that will be used at the end of each line. By default, /// this separator will be `
`. pub fn all_lines(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> { - let code = h.param(0) - .and_then(|v| v.value().as_str()) - .ok_or_else(|| RenderError::new("the code must be a string"))?; + let code = h.param(0).and_then(|v| v.value().as_str()).ok_or_else(|| { + RenderError::new("the code must be a string") + })?; let line_separator = match h.param(1) { Some(s) => { if let Value::String(ref s) = *s.value() { s } else { - return Err(RenderError::new("the provided line separator for the code lines was \ - not a string")); + return Err(RenderError::new( + "the provided line separator for the code lines was \ + not a string", + )); } } None => "
", @@ -95,16 +94,18 @@ pub fn all_lines(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<( /// An optional line separator can be added that will be used at the end of each line. By default, /// this separator will be `
`. pub fn all_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> { - let code = h.param(0) - .and_then(|v| v.value().as_str()) - .ok_or_else(|| RenderError::new("the code must be a string"))?; + let code = h.param(0).and_then(|v| v.value().as_str()).ok_or_else(|| { + RenderError::new("the code must be a string") + })?; let line_separator = match h.param(1) { Some(s) => { if let Value::String(ref s) = *s.value() { s } else { - return Err(RenderError::new("the provided line separator for the code lines was \ - not a string")); + return Err(RenderError::new( + "the provided line separator for the code lines was \ + not a string", + )); } } None => "
", @@ -112,12 +113,14 @@ pub fn all_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<() for (i, line) in code.lines().enumerate() { let (indent, line) = split_indent(line); - let line = format!("{}{}{}{}", - i + 1, - indent, - html_escape(line), - line_separator); + i + 1, + indent, + html_escape(line), + line_separator + ); let _ = rc.writer.write(line.as_bytes())?; } @@ -136,19 +139,23 @@ pub fn all_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<() /// /// This enables easy styling of the code in templates. pub fn html_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> { - let vulnerability = h.param(0) - .and_then(|v| v.value().as_object()) - .ok_or_else(|| { - RenderError::new("to generate the vulnerability index, the first parameter must be a \ - vulnerability") - })?; + let vulnerability = h.param(0).and_then(|v| v.value().as_object()).ok_or_else( + || { + RenderError::new( + "to generate the vulnerability index, the first parameter must be a \ + vulnerability", + ) + }, + )?; let line_separator = match h.param(1) { Some(s) => { if let Value::String(ref s) = *s.value() { s } else { - return Err(RenderError::new("the provided line separator for the code lines was \ - not a string")); + return Err(RenderError::new( + "the provided line separator for the code lines was \ + not a string", + )); } } None => "
", @@ -157,42 +164,33 @@ pub fn html_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<( let line = l.as_i64().unwrap(); (line, line) } else { - let start_line = vulnerability - .get("start_line") - .unwrap() - .as_i64() - .unwrap(); - let end_line = vulnerability - .get("end_line") - .unwrap() - .as_i64() - .unwrap(); + let start_line = vulnerability.get("start_line").unwrap().as_i64().unwrap(); + let end_line = vulnerability.get("end_line").unwrap().as_i64().unwrap(); (start_line, end_line) }; let iter_start = if start_line > 5 { start_line - 4 } else { 1 }; for (i, line) in vulnerability - .get("code") - .unwrap() - .as_str() - .unwrap() - .lines() - .enumerate() { + .get("code") + .unwrap() + .as_str() + .unwrap() + .lines() + .enumerate() + { let line_number = i + iter_start as usize; let rendered = if line_number >= start_line as usize && line_number <= end_line as usize { let (indent, code) = split_indent(line); - format!("{}{}{}{}", - vulnerability - .get("criticality") - .unwrap() - .as_str() - .unwrap(), - indent, - html_escape(code), - line_separator) + vulnerability.get("criticality").unwrap().as_str().unwrap(), + indent, + html_escape(code), + line_separator + ) } else { format!("{}{}", html_escape(line), line_separator) }; @@ -208,18 +206,20 @@ pub fn html_code(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<( /// E.g.: for a critical vulnerability in an application with between 100 and 200 vulnerability, /// for the critical vulnerability number 12 it would produce `C012`. pub fn report_index(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> { - let vulnerability = h.param(0) - .and_then(|v| v.value().as_object()) - .ok_or_else(|| { - RenderError::new("to generate the vulnerability index, the first parameter must be a \ - vulnerability") - })?; - let index = h.param(1) - .and_then(|v| v.value().as_u64()) - .ok_or_else(|| { - RenderError::new("the index of the vulnerability in the current list must be the \ - second parameter") - })? as usize + 1; + let vulnerability = h.param(0).and_then(|v| v.value().as_object()).ok_or_else( + || { + RenderError::new( + "to generate the vulnerability index, the first parameter must be a \ + vulnerability", + ) + }, + )?; + let index = h.param(1).and_then(|v| v.value().as_u64()).ok_or_else(|| { + RenderError::new( + "the index of the vulnerability in the current list must be the \ + second parameter", + ) + })? as usize + 1; let list_len = h.param(2).unwrap().value().as_u64().unwrap(); let char_index = vulnerability @@ -246,15 +246,18 @@ pub fn report_index(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Resul /// /// It will generaten unordered HTML list (`
    ...
`) where all files and folders of the given /// menu object. -pub fn generate_menu(h: &Helper, - _: &Handlebars, - rc: &mut RenderContext) - -> Result<(), RenderError> { - let menu = h.param(0) - .and_then(|m| m.value().as_array()) - .ok_or_else(|| { - RenderError::new("to generate the menu, the first parameter must be a menu array") - })?; +pub fn generate_menu( + h: &Helper, + _: &Handlebars, + rc: &mut RenderContext, +) -> Result<(), RenderError> { + let menu = h.param(0).and_then(|m| m.value().as_array()).ok_or_else( + || { + RenderError::new( + "to generate the menu, the first parameter must be a menu array", + ) + }, + )?; let _ = rc.writer.write(b"
    ")?; render_menu(menu, &mut rc.writer)?; let _ = rc.writer.write(b"
")?; @@ -265,34 +268,38 @@ fn render_menu(menu: &[Value], renderer: &mut W) -> Result<(), RenderE for value in menu { if let Value::Object(ref item) = *value { let _ = renderer.write(b"
  • ")?; - let name = item.get("name") - .and_then(|n| n.as_str()) - .ok_or_else(|| RenderError::new("invalid menu object type"))?; + let name = item.get("name").and_then(|n| n.as_str()).ok_or_else(|| { + RenderError::new("invalid menu object type") + })?; if let Some(&Value::Array(ref menu)) = item.get("menu") { - let _ = renderer - .write(format!("{0}", - name) - .as_bytes())?; + name + ).as_bytes(), + )?; let _ = renderer.write(b"
      ")?; render_menu(menu, renderer)?; let _ = renderer.write(b"
    ")?; } else { - let path = item.get("path") - .and_then(|n| n.as_str()) - .ok_or_else(|| RenderError::new("invalid menu object type"))?; - let file_type = item.get("type") - .and_then(|n| n.as_str()) - .ok_or_else(|| RenderError::new("invalid menu object type"))?; - let _ = renderer - .write(format!("{0}", - name, - path, - file_type) - .as_bytes())?; + name, + path, + file_type + ).as_bytes(), + )?; } let _ = renderer.write(b"
  • ")?; } else { diff --git a/src/results/mod.rs b/src/results/mod.rs index 27516c69a..f707794aa 100644 --- a/src/results/mod.rs +++ b/src/results/mod.rs @@ -43,16 +43,20 @@ impl Results { let fingerprint = match FingerPrint::new(package) { Ok(f) => f, Err(e) => { - print_warning(format!("An error occurred when trying to fingerprint the \ + print_warning(format!( + "An error occurred when trying to fingerprint the \ application: {}", - e)); + e + )); return Err(e)?; } }; if config.is_verbose() { - println!("The results struct has been created. All the vulnerabilitis will now \ + println!( + "The results struct has been created. All the vulnerabilitis will now \ be recorded and when the analysis ends, they will be written to result \ - files."); + files." + ); } else if !config.is_quiet() { println!("Results struct created."); } @@ -60,40 +64,40 @@ impl Results { #[cfg(feature = "certificate")] { Ok(Results { - app_package: String::new(), - app_label: String::new(), - app_description: String::new(), - app_version: String::new(), - app_version_num: 0, - app_min_sdk: 0, - app_target_sdk: None, - app_fingerprint: fingerprint, - certificate: String::new(), - warnings: BTreeSet::new(), - low: BTreeSet::new(), - medium: BTreeSet::new(), - high: BTreeSet::new(), - critical: BTreeSet::new(), - }) + app_package: String::new(), + app_label: String::new(), + app_description: String::new(), + app_version: String::new(), + app_version_num: 0, + app_min_sdk: 0, + app_target_sdk: None, + app_fingerprint: fingerprint, + certificate: String::new(), + warnings: BTreeSet::new(), + low: BTreeSet::new(), + medium: BTreeSet::new(), + high: BTreeSet::new(), + critical: BTreeSet::new(), + }) } #[cfg(not(feature = "certificate"))] { Ok(Results { - app_package: String::new(), - app_label: String::new(), - app_description: String::new(), - app_version: String::new(), - app_version_num: 0, - app_min_sdk: 0, - app_target_sdk: None, - app_fingerprint: fingerprint, - warnings: BTreeSet::new(), - low: BTreeSet::new(), - medium: BTreeSet::new(), - high: BTreeSet::new(), - critical: BTreeSet::new(), - }) + app_package: String::new(), + app_label: String::new(), + app_description: String::new(), + app_version: String::new(), + app_version_num: 0, + app_min_sdk: 0, + app_target_sdk: None, + app_fingerprint: fingerprint, + warnings: BTreeSet::new(), + low: BTreeSet::new(), + medium: BTreeSet::new(), + high: BTreeSet::new(), + critical: BTreeSet::new(), + }) } } @@ -178,9 +182,11 @@ impl Results { } if let Err(e) = fs::remove_file(&path) { - print_warning(format!("There was an error when removing the JSON results \ + print_warning(format!( + "There was an error when removing the JSON results \ file: {}", - e.description())); + e.description() + )); } } let mut json_reporter = Json::new(); @@ -194,8 +200,10 @@ impl Results { } } else { if config.is_verbose() { - println!("Seems that the JSON report has already been generated. There is no \ - need to do it again."); + println!( + "Seems that the JSON report has already been generated. There is no \ + need to do it again." + ); } else { println!("Skipping JSON report generation."); } @@ -211,23 +219,26 @@ impl Results { println!("The application HTML resulsts exist. But no more…"); } - for f in - fs::read_dir(path) - .chain_err(|| "there was an error when removing the HTML results")? { + for f in fs::read_dir(path).chain_err( + || "there was an error when removing the HTML results", + )? + { let f = f?; if f.file_type()?.is_dir() { - fs::remove_dir_all(f.path()) - .chain_err(|| "there was an error when removing the HTML results")?; + fs::remove_dir_all(f.path()).chain_err( + || "there was an error when removing the HTML results", + )?; } else if &f.file_name() != "results.json" { - fs::remove_file(f.path()) - .chain_err(|| "there was an error when removing the HTML results")?; + fs::remove_file(f.path()).chain_err( + || "there was an error when removing the HTML results", + )?; } } } - let handelbars_report_result = HandlebarsReport::new(config.get_template_path(), - package.as_ref().to_owned()); + let handelbars_report_result = + HandlebarsReport::new(config.get_template_path(), package.as_ref().to_owned()); if let Ok(mut handlebars_reporter) = handelbars_report_result { if let Err(e) = handlebars_reporter.generate(config, self) { @@ -240,8 +251,10 @@ impl Results { } } else { if config.is_verbose() { - println!("Seems that the HTML report has already been generated. There is no - need to do it again."); + println!( + "Seems that the HTML report has already been generated. There is no + need to do it again." + ); } else { println!("Skipping HTML report generation."); } @@ -254,63 +267,73 @@ impl Results { impl Serialize for Results { fn serialize(&self, serializer: S) -> StdResult - where S: Serializer + where + S: Serializer, { let now = Local::now(); - let mut ser_struct = serializer - .serialize_struct("Results", - if cfg!(feature = "certificate") { - 22 - } else { - 21 - })?; - - ser_struct - .serialize_field("super_version", crate_version!())?; + let mut ser_struct = serializer.serialize_struct( + "Results", + if cfg!(feature = "certificate") { + 22 + } else { + 21 + }, + )?; + + ser_struct.serialize_field( + "super_version", + crate_version!(), + )?; ser_struct.serialize_field("now", &now)?; - ser_struct - .serialize_field("now_rfc2822", &now.to_rfc2822())?; - ser_struct - .serialize_field("now_rfc3339", &now.to_rfc3339())?; - - ser_struct - .serialize_field("app_package", &self.app_package)?; - ser_struct - .serialize_field("app_version", &self.app_version)?; - ser_struct - .serialize_field("app_version_number", &self.app_version_num)?; - ser_struct - .serialize_field("app_fingerprint", &self.app_fingerprint)?; + ser_struct.serialize_field("now_rfc2822", &now.to_rfc2822())?; + ser_struct.serialize_field("now_rfc3339", &now.to_rfc3339())?; + + ser_struct.serialize_field("app_package", &self.app_package)?; + ser_struct.serialize_field("app_version", &self.app_version)?; + ser_struct.serialize_field( + "app_version_number", + &self.app_version_num, + )?; + ser_struct.serialize_field( + "app_fingerprint", + &self.app_fingerprint, + )?; #[cfg(feature = "certificate")] { - ser_struct - .serialize_field("certificate", &self.certificate)?; + ser_struct.serialize_field("certificate", &self.certificate)?; } - ser_struct - .serialize_field("app_min_sdk", &self.app_min_sdk)?; - ser_struct - .serialize_field("app_target_sdk", &self.app_target_sdk)?; - - ser_struct - .serialize_field("total_vulnerabilities", - &(self.low.len() + self.medium.len() + self.high.len() + - self.critical.len()))?; + ser_struct.serialize_field("app_min_sdk", &self.app_min_sdk)?; + ser_struct.serialize_field( + "app_target_sdk", + &self.app_target_sdk, + )?; + + ser_struct.serialize_field( + "total_vulnerabilities", + &(self.low.len() + self.medium.len() + self.high.len() + + self.critical.len()), + )?; ser_struct.serialize_field("criticals", &self.critical)?; - ser_struct - .serialize_field("criticals_len", &self.critical.len())?; + ser_struct.serialize_field( + "criticals_len", + &self.critical.len(), + )?; ser_struct.serialize_field("highs", &self.high)?; - ser_struct - .serialize_field("highs_len", &self.high.len())?; + ser_struct.serialize_field("highs_len", &self.high.len())?; ser_struct.serialize_field("mediums", &self.medium)?; - ser_struct - .serialize_field("mediums_len", &self.medium.len())?; + ser_struct.serialize_field( + "mediums_len", + &self.medium.len(), + )?; ser_struct.serialize_field("lows", &self.low)?; ser_struct.serialize_field("lows_len", &self.low.len())?; ser_struct.serialize_field("warnings", &self.warnings)?; - ser_struct - .serialize_field("warnings_len", &self.warnings.len())?; + ser_struct.serialize_field( + "warnings_len", + &self.warnings.len(), + )?; ser_struct.end() } diff --git a/src/results/report/handlebars.rs b/src/results/report/handlebars.rs index 7610c443c..c80da660b 100644 --- a/src/results/report/handlebars.rs +++ b/src/results/report/handlebars.rs @@ -23,8 +23,9 @@ pub struct HandlebarsReport { impl HandlebarsReport { pub fn new(template_path: PathBuf, package: String) -> Result { - let handlebars_handler = Self::load_templates(template_path) - .chain_err(|| "Could not load handlebars templates")?; + let handlebars_handler = Self::load_templates(template_path).chain_err( + || "Could not load handlebars templates", + )?; let report = HandlebarsReport { handler: handlebars_handler, @@ -50,13 +51,15 @@ impl HandlebarsReport { let path = dir_entry.path(); let template_file = path.file_stem() .ok_or_else(|| { - ErrorKind::TemplateName("template files must have a file name" - .to_string()) + ErrorKind::TemplateName( + "template files must have a file name".to_string(), + ) }) .and_then(|stem| { stem.to_str().ok_or_else(|| { - ErrorKind::TemplateName("template names must be unicode" - .to_string()) + ErrorKind::TemplateName( + "template names must be unicode".to_string(), + ) }) })?; @@ -69,12 +72,15 @@ impl HandlebarsReport { } if handlebars.get_template("report").is_none() || - handlebars.get_template("src").is_none() || - handlebars.get_template("code").is_none() { - let message = format!("templates must include {}, {} and {} templates", - "report".italic(), - "src".italic(), - "code".italic()); + handlebars.get_template("src").is_none() || + handlebars.get_template("code").is_none() + { + let message = format!( + "templates must include {}, {} and {} templates", + "report".italic(), + "src".italic(), + "code".italic() + ); Err(ErrorKind::TemplateName(message).into()) } else { @@ -85,11 +91,13 @@ impl HandlebarsReport { fn generate_code_html_files(&self, config: &Config, results: &Results) -> Result<()> { let menu = Value::Array(self.generate_code_html_folder("", config, results)?); - let mut f = File::create(config - .get_results_folder() - .join(&results.get_app_package()) - .join("src") - .join("index.html"))?; + let mut f = File::create( + config + .get_results_folder() + .join(&results.get_app_package()) + .join("src") + .join("index.html"), + )?; let mut data = BTreeMap::new(); let _ = data.insert("menu", menu); @@ -98,26 +106,29 @@ impl HandlebarsReport { Ok(()) } - fn generate_code_html_folder>(&self, - path: P, - config: &Config, - results: &Results) - -> Result> { + fn generate_code_html_folder>( + &self, + path: P, + config: &Config, + results: &Results, + ) -> Result> { if path.as_ref() == Path::new("classes/android") || - path.as_ref() == Path::new("classes/com/google/android/gms") || - path.as_ref() == Path::new("smali") { + path.as_ref() == Path::new("classes/com/google/android/gms") || + path.as_ref() == Path::new("smali") + { return Ok(Vec::new()); } - let dir_iter = fs::read_dir(config - .get_dist_folder() - .join(&self.package) - .join(path.as_ref()))?; + let dir_iter = fs::read_dir(config.get_dist_folder().join(&self.package).join( + path.as_ref(), + ))?; - fs::create_dir_all(config - .get_results_folder() - .join(&results.get_app_package()) - .join("src") - .join(path.as_ref()))?; + fs::create_dir_all( + config + .get_results_folder() + .join(&results.get_app_package()) + .join("src") + .join(path.as_ref()), + )?; let mut menu = Vec::new(); for entry in dir_iter { @@ -141,10 +152,7 @@ impl HandlebarsReport { } } else { let mut object = Map::with_capacity(2); - let name = path.file_name() - .unwrap() - .to_string_lossy() - .into_owned(); + let name = path.file_name().unwrap().to_string_lossy().into_owned(); let _ = object.insert("name".to_owned(), Value::String(name)); let _ = object.insert("menu".to_owned(), Value::Array(inner_menu)); @@ -154,19 +162,23 @@ impl HandlebarsReport { } else { match path.extension() { Some(e) if e == "xml" || e == "java" => { - self.generate_code_html_for(&stripped, config, results, &self.package)?; - let name = path.file_name() - .unwrap() - .to_string_lossy() - .into_owned(); + self.generate_code_html_for( + &stripped, + config, + results, + &self.package, + )?; + let name = path.file_name().unwrap().to_string_lossy().into_owned(); let mut data = Map::with_capacity(3); let _ = data.insert("name".to_owned(), Value::String(name)); - let _ = - data.insert("path".to_owned(), - Value::String(format!("{}", stripped.display()))); - let _ = - data.insert("type".to_owned(), - Value::String(e.to_string_lossy().into_owned())); + let _ = data.insert( + "path".to_owned(), + Value::String(format!("{}", stripped.display())), + ); + let _ = data.insert( + "type".to_owned(), + Value::String(e.to_string_lossy().into_owned()), + ); menu.push(Value::Object(data)); } _ => {} @@ -177,23 +189,28 @@ impl HandlebarsReport { Ok(menu) } - fn generate_code_html_for, S: AsRef>(&self, - path: P, - config: &Config, - results: &Results, - cli_package_name: S) - -> Result<()> { - let mut f_in = File::open(config - .get_dist_folder() - .join(cli_package_name.as_ref()) - .join(path.as_ref()))?; - let mut f_out = File::create(format!("{}.html", - config - .get_results_folder() - .join(&results.get_app_package()) - .join("src") - .join(path.as_ref()) - .display()))?; + fn generate_code_html_for, S: AsRef>( + &self, + path: P, + config: &Config, + results: &Results, + cli_package_name: S, + ) -> Result<()> { + let mut f_in = File::open( + config + .get_dist_folder() + .join(cli_package_name.as_ref()) + .join(path.as_ref()), + )?; + let mut f_out = File::create(format!( + "{}.html", + config + .get_results_folder() + .join(&results.get_app_package()) + .join("src") + .join(path.as_ref()) + .display() + ))?; let mut code = String::new(); let _ = f_in.read_to_string(&mut code)?; @@ -204,13 +221,16 @@ impl HandlebarsReport { } let mut data = BTreeMap::new(); - let _ = data.insert(String::from("path"), - Value::String(format!("{}", path.as_ref().display()))); + let _ = data.insert( + String::from("path"), + Value::String(format!("{}", path.as_ref().display())), + ); let _ = data.insert(String::from("code"), Value::String(code)); let _ = data.insert(String::from("back_path"), Value::String(back_path)); - f_out - .write_all(self.handler.render("code", &data)?.as_bytes())?; + f_out.write_all( + self.handler.render("code", &data)?.as_bytes(), + )?; Ok(()) } @@ -221,34 +241,40 @@ impl Report for HandlebarsReport { if config.is_verbose() { println!("Starting HTML report generation. First we create the file.") } - let mut f = File::create(config - .get_results_folder() - .join(&results.app_package) - .join("index.html"))?; + let mut f = File::create( + config + .get_results_folder() + .join(&results.app_package) + .join("index.html"), + )?; if config.is_verbose() { println!("The report file has been created. Now it's time to fill it.") } - f.write_all(self.handler.render("report", results)?.as_bytes())?; + f.write_all( + self.handler.render("report", results)?.as_bytes(), + )?; for entry in fs::read_dir(config.get_template_path())? { let entry = entry?; let entry_path = entry.path(); if entry.file_type()?.is_dir() { - copy_folder(&entry_path, - &config - .get_results_folder() - .join(&results.get_app_package()) - .join(entry_path.file_name().unwrap()))?; + copy_folder( + &entry_path, + &config + .get_results_folder() + .join(&results.get_app_package()) + .join(entry_path.file_name().unwrap()), + )?; } else { match entry_path.as_path().extension() { Some(e) if e == "hbs" => {} None => {} _ => { - let _ = fs::copy(&entry_path, - &config - .get_results_folder() - .join(&results.get_app_package()))?; + let _ = fs::copy( + &entry_path, + &config.get_results_folder().join(&results.get_app_package()), + )?; } } } diff --git a/src/results/report/json.rs b/src/results/report/json.rs index 05b7cbb24..7dc150a4d 100644 --- a/src/results/report/json.rs +++ b/src/results/report/json.rs @@ -20,10 +20,12 @@ impl Report for Json { if config.is_verbose() { println!("Starting JSON report generation. First we create the file.") } - let mut f = BufWriter::new(File::create(config - .get_results_folder() - .join(&results.get_app_package()) - .join("results.json"))?); + let mut f = BufWriter::new(File::create( + config + .get_results_folder() + .join(&results.get_app_package()) + .join("results.json"), + )?); if config.is_verbose() { println!("The report file has been created. Now it's time to fill it.") } diff --git a/src/results/utils.rs b/src/results/utils.rs index dbd1c677e..9a8835f5b 100644 --- a/src/results/utils.rs +++ b/src/results/utils.rs @@ -39,15 +39,15 @@ pub struct Vulnerability { impl Vulnerability { /// Creates a new vulnerability. - pub fn new, D: Into, P: AsRef, C: Into> - (criticality: Criticality, - name: N, - description: D, - file: Option

    , - start_line: Option, - end_line: Option, - code: Option) - -> Vulnerability { + pub fn new, D: Into, P: AsRef, C: Into>( + criticality: Criticality, + name: N, + description: D, + file: Option

    , + start_line: Option, + end_line: Option, + code: Option, + ) -> Vulnerability { Vulnerability { criticality: criticality, name: name.into(), @@ -73,42 +73,52 @@ impl Vulnerability { impl Serialize for Vulnerability { fn serialize(&self, serializer: S) -> StdResult - where S: Serializer + where + S: Serializer, { - let mut ser_struct = serializer - .serialize_struct("Vulnerability", - if self.code.is_some() { - if self.start_line == self.end_line { - 7 - } else { - 8 - } - } else { - 4 - })?; - ser_struct - .serialize_field("criticality", &self.criticality)?; + let mut ser_struct = serializer.serialize_struct( + "Vulnerability", + if self.code.is_some() { + if self.start_line == self.end_line { + 7 + } else { + 8 + } + } else { + 4 + }, + )?; + ser_struct.serialize_field("criticality", &self.criticality)?; ser_struct.serialize_field("name", self.name.as_str())?; - ser_struct - .serialize_field("description", self.description.as_str())?; + ser_struct.serialize_field( + "description", + self.description.as_str(), + )?; ser_struct.serialize_field("file", &self.file)?; if self.code.is_some() { - ser_struct - .serialize_field("language", - &self.file - .as_ref() - .unwrap() - .extension() - .unwrap() - .to_string_lossy())?; + ser_struct.serialize_field( + "language", + &self.file + .as_ref() + .unwrap() + .extension() + .unwrap() + .to_string_lossy(), + )?; if self.start_line == self.end_line { - ser_struct - .serialize_field("line", &(self.start_line.unwrap() + 1))?; + ser_struct.serialize_field( + "line", + &(self.start_line.unwrap() + 1), + )?; } else { - ser_struct - .serialize_field("start_line", &(self.start_line.unwrap() + 1))?; - ser_struct - .serialize_field("end_line", &(self.end_line.unwrap() + 1))?; + ser_struct.serialize_field( + "start_line", + &(self.start_line.unwrap() + 1), + )?; + ser_struct.serialize_field( + "end_line", + &(self.end_line.unwrap() + 1), + )?; } ser_struct.serialize_field("code", &self.code)?; } @@ -118,12 +128,21 @@ impl Serialize for Vulnerability { impl PartialOrd for Vulnerability { fn partial_cmp(&self, other: &Vulnerability) -> Option { - Some((&self.criticality, &self.file, &self.start_line, &self.end_line, &self.name) - .cmp(&(&other.criticality, - &other.file, - &other.start_line, - &other.end_line, - &other.name))) + Some( + ( + &self.criticality, + &self.file, + &self.start_line, + &self.end_line, + &self.name, + ).cmp(&( + &other.criticality, + &other.file, + &other.start_line, + &other.end_line, + &other.name, + )), + ) } } @@ -157,24 +176,25 @@ impl FingerPrint { let mut sha256_res = [0_u8; 32]; sha256_res.clone_from_slice(&sha256.result()[..]); Ok(FingerPrint { - md5: md5::compute(&buffer), - sha1: sha1.digest(), - sha256: sha256_res, - }) + md5: md5::compute(&buffer), + sha1: sha1.digest(), + sha256: sha256_res, + }) } } impl Serialize for FingerPrint { fn serialize(&self, serializer: S) -> StdResult - where S: Serializer + where + S: Serializer, { let mut ser_struct = serializer.serialize_struct("fingerprint", 3)?; - ser_struct - .serialize_field("md5", &format!("{:x}", self.md5))?; - ser_struct - .serialize_field("sha1", &self.sha1.to_string())?; - ser_struct - .serialize_field("sha256", &self.sha256.to_hex())?; + ser_struct.serialize_field( + "md5", + &format!("{:x}", self.md5), + )?; + ser_struct.serialize_field("sha1", &self.sha1.to_string())?; + ser_struct.serialize_field("sha256", &self.sha256.to_hex())?; ser_struct.end() } } diff --git a/src/static_analysis/certificate.rs b/src/static_analysis/certificate.rs index c35b969a6..6d8efd817 100644 --- a/src/static_analysis/certificate.rs +++ b/src/static_analysis/certificate.rs @@ -36,10 +36,11 @@ fn parse_month>(month_str: S) -> u32 { /// Performs the certificate analysis. /// /// *Note: This requires OpenSSL.* -pub fn certificate_analysis>(config: &Config, - package: S, - results: &mut Results) - -> Result<()> { +pub fn certificate_analysis>( + config: &Config, + package: S, + results: &mut Results, +) -> Result<()> { if config.is_verbose() { println!("Reading and analyzing the certificates…") } @@ -56,11 +57,13 @@ pub fn certificate_analysis>(config: &Config, let f = match f { Ok(f) => f, Err(e) => { - print_warning(format!("An error occurred when reading the \ + print_warning(format!( + "An error occurred when reading the \ {} dir searching certificates. \ Certificate analysis will be skipped. More info: {}", - path.display(), - e.description())); + path.display(), + e.description() + )); break; } }; @@ -96,15 +99,20 @@ pub fn certificate_analysis>(config: &Config, })?; if !output.status.success() { - return Err(format!("The openssl command returned an error. More info: {}", - String::from_utf8_lossy(&output.stderr[..])) - .into()); + return Err( + format!( + "The openssl command returned an error. More info: {}", + String::from_utf8_lossy(&output.stderr[..]) + ).into(), + ); }; let cmd = String::from_utf8_lossy(&output.stdout); if config.is_verbose() { - println!("The application is signed with the following certificate: {}", - path_file.bold()); + println!( + "The application is signed with the following certificate: {}", + path_file.bold() + ); println!("{}", cmd); } @@ -136,13 +144,15 @@ pub fn certificate_analysis>(config: &Config, let description = "The application is signed with the Android Debug Certificate. \ This certificate should never be used for publishing an app."; - let vuln = Vulnerability::new(criticality, - "Android Debug Certificate", - description, - None::, - None, - None, - None::); + let vuln = Vulnerability::new( + criticality, + "Android Debug Certificate", + description, + None::, + None, + None, + None::, + ); results.add_vulnerability(vuln); print_vulnerability(description, criticality); } @@ -165,19 +175,22 @@ pub fn certificate_analysis>(config: &Config, }; if year > cert_year || (year == cert_year && month > cert_month) || - (year == cert_year && month == cert_month && day > cert_day) { + (year == cert_year && month == cert_month && day > cert_day) + { let criticality = Criticality::High; let description = "The certificate of the application has expired. You should not \ use applications with expired certificates since the app is \ not secure anymore."; - let vuln = Vulnerability::new(criticality, - "Expired certificate", - description, - None::, - None, - None, - None::); + let vuln = Vulnerability::new( + criticality, + "Expired certificate", + description, + None::, + None, + None, + None::, + ); results.add_vulnerability(vuln); print_vulnerability(description, criticality); } diff --git a/src/static_analysis/code.rs b/src/static_analysis/code.rs index 8aba87ef0..97781c1ac 100644 --- a/src/static_analysis/code.rs +++ b/src/static_analysis/code.rs @@ -19,24 +19,30 @@ use results::{Results, Vulnerability}; use super::manifest::{Permission, Manifest}; use error::*; -pub fn analysis>(manifest: Option, - config: &Config, - package: S, - results: &mut Results) { +pub fn analysis>( + manifest: Option, + config: &Config, + package: S, + results: &mut Results, +) { let rules = match load_rules(config) { Ok(r) => r, Err(e) => { - print_warning(format!("An error occurred when loading code analysis rules. Error: {}", - e.description())); + print_warning(format!( + "An error occurred when loading code analysis rules. Error: {}", + e.description() + )); return; } }; let mut files: Vec = Vec::new(); if let Err(e) = add_files_to_vec("", &mut files, package.as_ref(), config) { - print_warning(format!("An error occurred when reading files for analysis, the results \ + print_warning(format!( + "An error occurred when reading files for analysis, the results \ might be incomplete. Error: {}", - e.description())); + e.description() + )); } let total_files = files.len(); @@ -47,9 +53,11 @@ pub fn analysis>(manifest: Option, let dist_folder = Arc::new(config.get_dist_folder().join(package.as_ref())); if config.is_verbose() { - println!("Starting analysis of the code with {} threads. {} files to go!", - format!("{}", config.get_threads()).bold(), - format!("{}", total_files).bold()); + println!( + "Starting analysis of the code with {} threads. {} files to go!", + format!("{}", config.get_threads()).bold(), + format!("{}", total_files).bold() + ); } let handles: Vec<_> = (0..config.get_threads()) @@ -67,15 +75,20 @@ pub fn analysis>(manifest: Option, }; match f { Some(f) => { - if let Err(e) = analyze_file(f.path(), - &*thread_dist_folder, - &thread_rules, - &thread_manifest, - &thread_vulns) { - print_warning(format!("Error analyzing file {}. The analysis will \ + if let Err(e) = analyze_file( + f.path(), + &*thread_dist_folder, + &thread_rules, + &thread_manifest, + &thread_vulns, + ) + { + print_warning(format!( + "Error analyzing file {}. The analysis will \ continue, though. Error: {}", - f.path().display(), - e.description())) + f.path().display(), + e.description() + )) } } None => break, @@ -88,9 +101,10 @@ pub fn analysis>(manifest: Option, let mut last_print = 0; while match files.lock() { - Ok(f) => f.len(), - Err(_) => 1, - } > 0 { + Ok(f) => f.len(), + Err(_) => 1, + } > 0 + { let left = match files.lock() { Ok(f) => f.len(), @@ -107,15 +121,14 @@ pub fn analysis>(manifest: Option, for t in handles { if let Err(e) = t.join() { #[allow(use_debug)] - print_warning(format!("An error occurred when joining analysis threads: Error: {:?}", - e)); + print_warning(format!( + "An error occurred when joining analysis threads: Error: {:?}", + e + )); } } - for vuln in Arc::try_unwrap(found_vulns) - .unwrap() - .into_inner() - .unwrap() { + for vuln in Arc::try_unwrap(found_vulns).unwrap().into_inner().unwrap() { results.add_vulnerability(vuln); } @@ -127,19 +140,21 @@ pub fn analysis>(manifest: Option, } } -fn analyze_file, T: AsRef>(path: P, - dist_folder: T, - rules: &[Rule], - manifest: &Option, - results: &Mutex>) - -> Result<()> { +fn analyze_file, T: AsRef>( + path: P, + dist_folder: T, + rules: &[Rule], + manifest: &Option, + results: &Mutex>, +) -> Result<()> { let mut f = File::open(&path)?; let mut code = String::new(); let _ = f.read_to_string(&mut code)?; 'check: for rule in rules { if manifest.is_some() && rule.get_max_sdk().is_some() && - rule.get_max_sdk().unwrap() < manifest.as_ref().unwrap().get_min_sdk() { + rule.get_max_sdk().unwrap() < manifest.as_ref().unwrap().get_min_sdk() + { continue 'check; } @@ -153,11 +168,12 @@ fn analyze_file, T: AsRef>(path: P, for permission in rule.get_permissions() { if manifest.is_none() || - !manifest + !manifest .as_ref() .unwrap() .get_permission_checklist() - .needs_permission(*permission) { + .needs_permission(*permission) + { continue 'check; } } @@ -173,17 +189,15 @@ fn analyze_file, T: AsRef>(path: P, let start_line = get_line_for(m.start(), code.as_str()); let end_line = get_line_for(m.end(), code.as_str()); let mut results = results.lock().unwrap(); - results.push(Vulnerability::new(rule.get_criticality(), - rule.get_label(), - rule.get_description(), - Some(path.as_ref() - .strip_prefix(&dist_folder) - .unwrap()), - Some(start_line), - Some(end_line), - Some(get_code(code.as_str(), - start_line, - end_line)))); + results.push(Vulnerability::new( + rule.get_criticality(), + rule.get_label(), + rule.get_description(), + Some(path.as_ref().strip_prefix(&dist_folder).unwrap()), + Some(start_line), + Some(end_line), + Some(get_code(code.as_str(), start_line, end_line)), + )); print_vulnerability(rule.get_description(), rule.get_criticality()); } @@ -207,11 +221,13 @@ fn analyze_file, T: AsRef>(path: P, let regex = match Regex::new(r.as_str()) { Ok(r) => r, Err(e) => { - print_warning(format!("There was an error creating the \ + print_warning(format!( + "There was an error creating the \ forward_check '{}'. The rule will be \ skipped. {}", - r, - e.description())); + r, + e.description() + )); break 'rule; } }; @@ -220,17 +236,15 @@ fn analyze_file, T: AsRef>(path: P, let start_line = get_line_for(m.start(), code.as_str()); let end_line = get_line_for(m.end(), code.as_str()); let mut results = results.lock().unwrap(); - results.push(Vulnerability::new(rule.get_criticality(), - rule.get_label(), - rule.get_description(), - Some(path.as_ref() - .strip_prefix(&dist_folder) - .unwrap()), - Some(start_line), - Some(end_line), - Some(get_code(code.as_str(), - start_line, - end_line)))); + results.push(Vulnerability::new( + rule.get_criticality(), + rule.get_label(), + rule.get_description(), + Some(path.as_ref().strip_prefix(&dist_folder).unwrap()), + Some(start_line), + Some(end_line), + Some(get_code(code.as_str(), start_line, end_line)), + )); print_vulnerability(rule.get_description(), rule.get_criticality()); } @@ -255,27 +269,28 @@ fn get_line_for>(index: usize, text: S) -> usize { line } -fn add_files_to_vec, S: AsRef>(path: P, - vec: &mut Vec, - package: S, - config: &Config) - -> Result<()> { +fn add_files_to_vec, S: AsRef>( + path: P, + vec: &mut Vec, + package: S, + config: &Config, +) -> Result<()> { if path.as_ref() == Path::new("classes/android") || - path.as_ref() == Path::new("classes/com/google/android/gms") || - path.as_ref() == Path::new("smali") { + path.as_ref() == Path::new("classes/com/google/android/gms") || + path.as_ref() == Path::new("smali") + { return Ok(()); } - let real_path = config - .get_dist_folder() - .join(package.as_ref()) - .join(path); + let real_path = config.get_dist_folder().join(package.as_ref()).join(path); for f in fs::read_dir(&real_path)? { let f = match f { Ok(f) => f, Err(e) => { - print_warning(format!("There was an error reading the directory {}: {}", - real_path.display(), - e.description())); + print_warning(format!( + "There was an error reading the directory {}: {}", + real_path.display(), + e.description() + )); return Err(e.into()); } }; @@ -283,18 +298,19 @@ fn add_files_to_vec, S: AsRef>(path: P, let f_path = f.path(); let f_ext = f_path.extension(); if f_type.is_dir() && f_path != real_path.join("original") { - add_files_to_vec(f.path() - .strip_prefix(&config - .get_dist_folder() - .join(package.as_ref())) - .unwrap(), - vec, - package.as_ref(), - config)?; + add_files_to_vec( + f.path() + .strip_prefix(&config.get_dist_folder().join(package.as_ref())) + .unwrap(), + vec, + package.as_ref(), + config, + )?; } else if f_ext.is_some() { let filename = f_path.file_name().unwrap().to_string_lossy(); if filename != "AndroidManifest.xml" && filename != "R.java" && - !filename.starts_with("R$") { + !filename.starts_with("R$") + { match f_ext.unwrap().to_string_lossy().borrow() { "xml" | "java" => vec.push(f), _ => {} @@ -384,7 +400,8 @@ fn load_rules(config: &Config) -> Result> { }; for rule in rules_json { - let format_warning = format!("Rules must be objects with the following structure:\n{}\nAn optional {} \ + let format_warning = format!( + "Rules must be objects with the following structure:\n{}\nAn optional {} \ attribute can be added: an array of regular expressions that if matched, \ the found match will be discarded. You can also include an optional {} \ attribute: an array of the permissions needed for this rule to be checked. \ @@ -393,19 +410,20 @@ fn load_rules(config: &Config) -> Result> { You can add one or two capture groups with name from the match to this \ check, with names {} and {}. To use them you have to include {} or {} in \ the forward check.", - "{\n\t\"label\": \"Label for the rule\",\n\t\"description\": \"Long \ + "{\n\t\"label\": \"Label for the rule\",\n\t\"description\": \"Long \ description for this rule\"\n\t\"criticality\": \ \"warning|low|medium|high|critical\"\n\t\"regex\": \ \"regex_to_find_vulnerability\"\n}" - .italic(), - "whitelist".italic(), - "permissions".italic(), - "forward_check".italic(), - "regex".italic(), - "fc1".italic(), - "fc2".italic(), - "{fc1}".italic(), - "{fc2}".italic()); + .italic(), + "whitelist".italic(), + "permissions".italic(), + "forward_check".italic(), + "regex".italic(), + "fc1".italic(), + "fc2".italic(), + "{fc1}".italic(), + "{fc2}".italic() + ); let rule = if let Some(o) = rule.as_object() { o } else { @@ -422,9 +440,11 @@ fn load_rules(config: &Config) -> Result> { match Regex::new(r) { Ok(r) => r, Err(e) => { - print_warning(format!("An error occurred when compiling the regular \ + print_warning(format!( + "An error occurred when compiling the regular \ expresion: {}", - e.description())); + e.description() + )); return Err(ErrorKind::Parse.into()); } } @@ -447,17 +467,16 @@ fn load_rules(config: &Config) -> Result> { let mut list = Vec::with_capacity(v.len()); for p in v { list.push(if let Value::String(ref p) = *p { - if let Ok(p) = Permission::from_str(p) { - p - } else { - print_warning(format!("the permission {} is unknown", - p.italic())); - return Err(ErrorKind::Parse.into()); - } - } else { - print_warning(format_warning); - return Err(ErrorKind::Parse.into()); - }); + if let Ok(p) = Permission::from_str(p) { + p + } else { + print_warning(format!("the permission {} is unknown", p.italic())); + return Err(ErrorKind::Parse.into()); + } + } else { + print_warning(format_warning); + return Err(ErrorKind::Parse.into()); + }); } list } @@ -475,17 +494,21 @@ fn load_rules(config: &Config) -> Result> { match cap { Some("fc1") => { if !s.contains("{fc1}") { - print_warning("You must provide the '{fc1}' string where you \ + print_warning( + "You must provide the '{fc1}' string where you \ want the 'fc1' capture to be inserted in the \ - forward check."); + forward check.", + ); return Err(ErrorKind::Parse.into()); } } Some("fc2") => { if !s.contains("{fc2}") { - print_warning("You must provide the '{fc2}' string where you \ + print_warning( + "You must provide the '{fc2}' string where you \ want the 'fc2' capture to be inserted in the \ - forward check."); + forward check.", + ); return Err(ErrorKind::Parse.into()); } } @@ -495,9 +518,12 @@ fn load_rules(config: &Config) -> Result> { let mut capture_names = regex.capture_names(); if capture_names.any(|c| c.is_some() && c.unwrap() == "fc2") && - !capture_names.any(|c| c.is_some() && c.unwrap() == "fc1") { - print_warning("You must have a capture group named fc1 to use the capture \ - fc2."); + !capture_names.any(|c| c.is_some() && c.unwrap() == "fc1") + { + print_warning( + "You must have a capture group named fc1 to use the capture \ + fc2.", + ); return Err(ErrorKind::Parse.into()); } @@ -528,12 +554,14 @@ fn load_rules(config: &Config) -> Result> { match Criticality::from_str(c) { Ok(c) => c, Err(e) => { - print_warning(format!("Criticality must be one of {}, {}, {}, {} or {}.", - "warning".italic(), - "low".italic(), - "medium".italic(), - "high".italic(), - "critical".italic())); + print_warning(format!( + "Criticality must be one of {}, {}, {}, {} or {}.", + "warning".italic(), + "low".italic(), + "medium".italic(), + "high".italic(), + "critical".italic() + )); return Err(e); } } @@ -547,19 +575,21 @@ fn load_rules(config: &Config) -> Result> { let mut list = Vec::with_capacity(v.len()); for r in v { list.push(if let Value::String(ref r) = *r { - match Regex::new(r) { - Ok(r) => r, - Err(e) => { - print_warning(format!("An error occurred when compiling the \ + match Regex::new(r) { + Ok(r) => r, + Err(e) => { + print_warning(format!( + "An error occurred when compiling the \ regular expresion: {}", - e.description())); - return Err(ErrorKind::Parse.into()); + e.description() + )); + return Err(ErrorKind::Parse.into()); + } } - } - } else { - print_warning(format_warning); - return Err(ErrorKind::Parse.into()); - }); + } else { + print_warning(format_warning); + return Err(ErrorKind::Parse.into()); + }); } list } @@ -577,9 +607,11 @@ fn load_rules(config: &Config) -> Result> { match include_regex { Ok(regex) => Some(regex), Err(e) => { - print_warning(format!("An error ocurred when compiling the inclusion \ + print_warning(format!( + "An error ocurred when compiling the inclusion \ regular expresion: {}", - e.description())); + e.description() + )); None } } @@ -592,9 +624,11 @@ fn load_rules(config: &Config) -> Result> { match exclude_regex { Ok(regex) => Some(regex), Err(e) => { - print_warning(format!("An error ocurred when compiling the exclusion \ + print_warning(format!( + "An error ocurred when compiling the exclusion \ regular expresion: {}", - e.description())); + e.description() + )); None } } @@ -602,17 +636,17 @@ fn load_rules(config: &Config) -> Result> { if criticality >= config.get_min_criticality() { rules.push(Rule { - regex: regex, - permissions: permissions, - forward_check: forward_check, - max_sdk: max_sdk, - label: label.clone(), - description: description.clone(), - criticality: criticality, - whitelist: whitelist, - include_file_regex: inclusion_regex, - exclude_file_regex: exclusion_regex, - }) + regex: regex, + permissions: permissions, + forward_check: forward_check, + max_sdk: max_sdk, + label: label.clone(), + description: description.clone(), + criticality: criticality, + whitelist: whitelist, + include_file_regex: inclusion_regex, + exclude_file_regex: exclusion_regex, + }) } } @@ -631,20 +665,24 @@ mod tests { for white in rule.get_whitelist() { if white.is_match(text.as_ref()) { let m = white.find(text.as_ref()).unwrap(); - println!("Whitelist '{}' matches the text '{}' in '{}'", - white.as_str(), - text.as_ref(), - &text.as_ref()[m.start()..m.end()]); + println!( + "Whitelist '{}' matches the text '{}' in '{}'", + white.as_str(), + text.as_ref(), + &text.as_ref()[m.start()..m.end()] + ); return false; } } match rule.get_forward_check() { None => { let m = rule.get_regex().find(text.as_ref()).unwrap(); - println!("The regular expression '{}' matches the text '{}' in '{}'", - rule.get_regex(), - text.as_ref(), - &text.as_ref()[m.start()..m.end()]); + println!( + "The regular expression '{}' matches the text '{}' in '{}'", + rule.get_regex(), + text.as_ref(), + &text.as_ref()[m.start()..m.end()] + ); true } Some(check) => { @@ -665,23 +703,29 @@ mod tests { let regex = Regex::new(r.as_str()).unwrap(); if regex.is_match(text.as_ref()) { let m = regex.find(text.as_ref()).unwrap(); - println!("The forward check '{}' matches the text '{}' in '{}'", - regex.as_str(), - text.as_ref(), - &text.as_ref()[m.start()..m.end()]); + println!( + "The forward check '{}' matches the text '{}' in '{}'", + regex.as_str(), + text.as_ref(), + &text.as_ref()[m.start()..m.end()] + ); true } else { - println!("The forward check '{}' does not match the text '{}'", - regex.as_str(), - text.as_ref()); + println!( + "The forward check '{}' does not match the text '{}'", + regex.as_str(), + text.as_ref() + ); false } } } } else { - println!("The regular expression '{}' does not match the text '{}'", - rule.get_regex(), - text.as_ref()); + println!( + "The regular expression '{}' does not match the text '{}'", + rule.get_regex(), + text.as_ref() + ); false } } @@ -692,15 +736,19 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(0).unwrap(); - let should_match = &["\"http://www.razican.com\"", - "\"https://razican.com\"", - "\"http://www.razican.com/hello\"", - "\"//www.razican.com/hello\"", - "\"ftp://ftp.razican.com/hello\""]; - let should_not_match = &["\"android.intent.extra.EMAIL\"", - "\"hello\"", - "\"http://schemas.android.com/apk/res/android\"", - "\"http://www.w3.org/2005/Atom\""]; + let should_match = &[ + "\"http://www.razican.com\"", + "\"https://razican.com\"", + "\"http://www.razican.com/hello\"", + "\"//www.razican.com/hello\"", + "\"ftp://ftp.razican.com/hello\"", + ]; + let should_not_match = &[ + "\"android.intent.extra.EMAIL\"", + "\"hello\"", + "\"http://schemas.android.com/apk/res/android\"", + "\"http://www.w3.org/2005/Atom\"", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -717,17 +765,21 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(1).unwrap(); - let should_match = &["catch (Exception e) {", - "catch (Exception hello) {", - "catch( Exception e ){", - "catch (IOException|Exception e) {", - "catch (Exception|IOException e) {", - "catch (IOException | Exception e) {", - "catch (IOException|Exception|PepeException e) {", - "catch (SystemException|ApplicationException|PepeException e) {", - "catch (IOException|Exception | PepeException e) {"]; - let should_not_match = &["catch (IOException e) {", - "catch (IOException|PepeException e) {"]; + let should_match = &[ + "catch (Exception e) {", + "catch (Exception hello) {", + "catch( Exception e ){", + "catch (IOException|Exception e) {", + "catch (Exception|IOException e) {", + "catch (IOException | Exception e) {", + "catch (IOException|Exception|PepeException e) {", + "catch (SystemException|ApplicationException|PepeException e) {", + "catch (IOException|Exception | PepeException e) {", + ]; + let should_not_match = &[ + "catch (IOException e) {", + "catch (IOException|PepeException e) {", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -744,16 +796,20 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(2).unwrap(); - let should_match = &["throws Exception {", - "throws Exception, IOException {", - "throws IOException, Exception {", - "throws Exception,IOException{", - "throws IOException,Exception{", - "throws SystemException,Exception{", - "throws ApplicationException,Exception{", - "throws PepeException, Exception, IOException {"]; - let should_not_match = &["throws IOException {", - "throws PepeException, IOException {"]; + let should_match = &[ + "throws Exception {", + "throws Exception, IOException {", + "throws IOException, Exception {", + "throws Exception,IOException{", + "throws IOException,Exception{", + "throws SystemException,Exception{", + "throws ApplicationException,Exception{", + "throws PepeException, Exception, IOException {", + ]; + let should_not_match = &[ + "throws IOException {", + "throws PepeException, IOException {", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -770,12 +826,14 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(3).unwrap(); - let should_match = &["setVisible(View.INVISIBLE)", - "setVisible ( View.invisible )", - "android:visibility = \"invisible\"", - "android:background = \"NULL\"", - "android:background=\"null\"", - "android:background = \"@null\""]; + let should_match = &[ + "setVisible(View.INVISIBLE)", + "setVisible ( View.invisible )", + "android:visibility = \"invisible\"", + "android:background = \"NULL\"", + "android:background=\"null\"", + "android:background = \"@null\"", + ]; let should_not_match = &["android:background = \"@color/red\""]; for m in should_match { @@ -793,20 +851,24 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(4).unwrap(); - let should_match = &[" 192.168.1.1", - " 0.0.0.0", - " 255.255.255.255", - " 13.0.130.23.52"]; - let should_not_match = &["0000.000.000.000", - "256.140.123.154", - "135.260.120.0", - "50.75.300.35", - "60.35.59.300", - ".5.6.7", - "115..35.5", - "155.232..576", - "123.132.123.", - "123.124.123"]; + let should_match = &[ + " 192.168.1.1", + " 0.0.0.0", + " 255.255.255.255", + " 13.0.130.23.52", + ]; + let should_not_match = &[ + "0000.000.000.000", + "256.140.123.154", + "135.260.120.0", + "50.75.300.35", + "60.35.59.300", + ".5.6.7", + "115..35.5", + "155.232..576", + "123.132.123.", + "123.124.123", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -824,11 +886,13 @@ mod tests { let rule = rules.get(5).unwrap(); let should_match = &["Math.random()", "Random()", "Math . random ()"]; - let should_not_match = &["math.random()", - "MATH.random()", - "Math.Randomize()", - "Mathrandom()", - "Math.random"]; + let should_not_match = &[ + "math.random()", + "MATH.random()", + "Math.Randomize()", + "Mathrandom()", + "Math.random", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -845,21 +909,25 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(6).unwrap(); - let should_match = &["Log.d(\"Diva-sqli\", \"Error occurred while searching in database: \ + let should_match = &[ + "Log.d(\"Diva-sqli\", \"Error occurred while searching in database: \ \" + messageToShow);", - " Log.d(\"Diva-sqli\", \"Error occurred while searching in \ + " Log.d(\"Diva-sqli\", \"Error occurred while searching in \ + database: \" + messageToShow + msg1 + msg2 + msg3);", + " Log.d(\"Diva-sqli\", \"Error occurred while searching in \ database: \" + messageToShow + msg1 + msg2 + msg3);", - " Log.d(\"Diva-sqli\", \"Error occurred while searching in \ + " Log.d(\"Diva-sqli\", \"Error occurred while searching in \ database: \" + messageToShow + msg1 + msg2 + msg3);", - " Log.d(\"Diva-sqli\", \"Error occurred while searching in \ - database: \" + messageToShow + msg1 + msg2 + msg3);"]; + ]; - let should_not_match = &["Log.e(\"Hello!\")", - "Log.e(\"Hello: \" + var)", - "Log.e(\"Hello: \" +var)", - "Log.wtf(\"Hello: \"+var)", - "Log.i(var)", - "Log.println(\"Hello: \" + var + \" goodbye\")"]; + let should_not_match = &[ + "Log.e(\"Hello!\")", + "Log.e(\"Hello: \" + var)", + "Log.e(\"Hello: \" +var)", + "Log.wtf(\"Hello: \"+var)", + "Log.i(var)", + "Log.println(\"Hello: \" + var + \" goodbye\")", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -876,10 +944,12 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(7).unwrap(); - let should_match = &["C:\\", - "C:\\Programs\\password.txt", - "D:\\", - "H:\\P\\o\\password.txt"]; + let should_match = &[ + "C:\\", + "C:\\Programs\\password.txt", + "D:\\", + "H:\\P\\o\\password.txt", + ]; let should_not_match = &["ome\\password.txt", "at:\\", "\\\\home\\sharedfile", "\\n"]; @@ -898,20 +968,22 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(8).unwrap(); - let should_match = &["DESKeySpec", - "getInstance(MD5)", - "getInstance(\"MD5\")", - "getInstance(SHA-1)", - "getInstance(\"SHA-1\")", - "getInstance(\"MD4\")", - "getInstance(\"RC2\")", - "getInstance(\"md4\")", - "getInstance(\"rc2\")", - "getInstance(\"rc4\")", - "getInstance(\"RC4\")", - "getInstance(\"AES/ECB\")", - "getInstance(\"RSA/ECB/nopadding\")", - "getInstance(\"rsa/ECB/nopadding\")"]; + let should_match = &[ + "DESKeySpec", + "getInstance(MD5)", + "getInstance(\"MD5\")", + "getInstance(SHA-1)", + "getInstance(\"SHA-1\")", + "getInstance(\"MD4\")", + "getInstance(\"RC2\")", + "getInstance(\"md4\")", + "getInstance(\"rc2\")", + "getInstance(\"rc4\")", + "getInstance(\"RC4\")", + "getInstance(\"AES/ECB\")", + "getInstance(\"RSA/ECB/nopadding\")", + "getInstance(\"rsa/ECB/nopadding\")", + ]; let should_not_match = &["", "", "", ""]; @@ -930,17 +1002,21 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(9).unwrap(); - let should_match = &["Thread.sleep(Usertime+Variable+Variable);", - "Thread.sleep(Usertime+13+123+1+24);", - "Thread . sleep (200+asdad+adasasda );", - "Thread . sleep (200+asdad+adasasda+30 );", - "Thread.sleep(10 + 10 + 10241 + Usertime);", - "SystemClock.sleep(Usertime);"]; - - let should_not_match = &["Thread.sleep(2000);", - "Thread.sleep(“1000” + Usertime);", - "Thread.sleep();", - "SystemClock.sleep(1000);"]; + let should_match = &[ + "Thread.sleep(Usertime+Variable+Variable);", + "Thread.sleep(Usertime+13+123+1+24);", + "Thread . sleep (200+asdad+adasasda );", + "Thread . sleep (200+asdad+adasasda+30 );", + "Thread.sleep(10 + 10 + 10241 + Usertime);", + "SystemClock.sleep(Usertime);", + ]; + + let should_not_match = &[ + "Thread.sleep(2000);", + "Thread.sleep(“1000” + Usertime);", + "Thread.sleep();", + "SystemClock.sleep(1000);", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -957,16 +1033,20 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(10).unwrap(); - let should_match = &["MODE_WORLD_READABLE", - "openFileOutput(\"file.txt \", 1) ", - "openFileOutput(\"filename\", 1) ", - "openFileOutput(filepath, 1) ", - "openFileOutput(path_to_file, 1) "]; - - let should_not_match = &["openFileOutput(\"file.txt\", 0) ", - "openFileOutput(, 1) ", - "openFileOutput() ", - ""]; + let should_match = &[ + "MODE_WORLD_READABLE", + "openFileOutput(\"file.txt \", 1) ", + "openFileOutput(\"filename\", 1) ", + "openFileOutput(filepath, 1) ", + "openFileOutput(path_to_file, 1) ", + ]; + + let should_not_match = &[ + "openFileOutput(\"file.txt\", 0) ", + "openFileOutput(, 1) ", + "openFileOutput() ", + "", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -983,16 +1063,20 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(11).unwrap(); - let should_match = &["MODE_WORLD_WRITABLE", - "openFileOutput(\"file.txt \", 2) ", - "openFileOutput(\"filename\", 2) ", - "openFileOutput(filepath, 2) ", - "openFileOutput(path_to_file, 2) "]; - - let should_not_match = &["openFileOutput(\"file.txt\", 0) ", - "openFileOutput(, 2) ", - "openFileOutput() ", - ""]; + let should_match = &[ + "MODE_WORLD_WRITABLE", + "openFileOutput(\"file.txt \", 2) ", + "openFileOutput(\"filename\", 2) ", + "openFileOutput(filepath, 2) ", + "openFileOutput(path_to_file, 2) ", + ]; + + let should_not_match = &[ + "openFileOutput(\"file.txt\", 0) ", + "openFileOutput(, 2) ", + "openFileOutput() ", + "", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -1066,8 +1150,10 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(15).unwrap(); - let should_match = &["onReceivedSslError(WebView view, SslErrorHandler handler, SslError \ - error) .proceed();"]; + let should_match = &[ + "onReceivedSslError(WebView view, SslErrorHandler handler, SslError \ + error) .proceed();", + ]; let should_not_match = &["", "", "", ""]; @@ -1086,17 +1172,21 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(16).unwrap(); - let should_match = &["android.database.sqlite .execSQL(\"INSERT INTO myuser VALUES \ + let should_match = &[ + "android.database.sqlite .execSQL(\"INSERT INTO myuser VALUES \ ('\" + paramView.getText().toString() + \"', '\" + \ localEditText.getText().toString() + \"');\");", - "android.database.sqlite .rawQuery(\"INSERT INTO myuser VALUES \ + "android.database.sqlite .rawQuery(\"INSERT INTO myuser VALUES \ ('\" + paramView.getText().toString() + \"', '\" + \ - localEditText.getText().toString() + \"');\");"]; + localEditText.getText().toString() + \"');\");", + ]; - let should_not_match = &[".execSQL(\"INSERT INTO myuser VALUES\"';\");", - "rawQuery(\"INSERT INTO myuser VALUES\";\");", - "", - ""]; + let should_not_match = &[ + ".execSQL(\"INSERT INTO myuser VALUES\"';\");", + "rawQuery(\"INSERT INTO myuser VALUES\";\");", + "", + "", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -1113,17 +1203,21 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(17).unwrap(); - let should_match = &["javax.net.ssl TrustAllSSLSocket-Factory", - "javax.net.ssl AllTrustSSLSocketFactory", - "javax.net.ssl NonValidatingSSLSocketFactory", - "javax.net.ssl ALLOW_ALL_HOSTNAME_VERIFIER", - "javax.net.ssl .setDefaultHostnameVerifier()", - "javax.net.ssl NullHostnameVerifier(')"]; - - let should_not_match = &["NullHostnameVerifier(')", - "javax.net.ssl", - "AllTrustSSLSocketFactory", - ""]; + let should_match = &[ + "javax.net.ssl TrustAllSSLSocket-Factory", + "javax.net.ssl AllTrustSSLSocketFactory", + "javax.net.ssl NonValidatingSSLSocketFactory", + "javax.net.ssl ALLOW_ALL_HOSTNAME_VERIFIER", + "javax.net.ssl .setDefaultHostnameVerifier()", + "javax.net.ssl NullHostnameVerifier(')", + ]; + + let should_not_match = &[ + "NullHostnameVerifier(')", + "javax.net.ssl", + "AllTrustSSLSocketFactory", + "", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -1140,22 +1234,26 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(18).unwrap(); - let should_match = &["telephony.SmsManager sendMultipartTextMessage(String destinationAddress, String \ + let should_match = &[ + "telephony.SmsManager sendMultipartTextMessage(String destinationAddress, String \ scAddress, ArrayList parts, ArrayList sentIntents, \ ArrayList deliveryIntents)", - "telephony.SmsManager sendTextMessage(String destinationAddress, String \ + "telephony.SmsManager sendTextMessage(String destinationAddress, String \ scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)", - "telephony.SmsManager vnd.android-dir/mms-sms", - "telephony.SmsManager vnd.android-dir/mms-sms"]; + "telephony.SmsManager vnd.android-dir/mms-sms", + "telephony.SmsManager vnd.android-dir/mms-sms", + ]; - let should_not_match = &["vnd.android-dir/mms-sms", - "sendTextMessage(String destinationAddress, String scAddress, \ + let should_not_match = &[ + "vnd.android-dir/mms-sms", + "sendTextMessage(String destinationAddress, String scAddress, \ String text, PendingIntent sentIntent, PendingIntent \ deliveryIntent)", - " sendMultipartTextMessage(String destinationAddress, String \ + " sendMultipartTextMessage(String destinationAddress, String \ scAddress, ArrayList parts, ArrayList \ sentIntents, ArrayList deliveryIntents)", - "telephony.SmsManager "]; + "telephony.SmsManager ", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -1172,11 +1270,13 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(19).unwrap(); - let should_match = &["com.noshufou.android.su", - "com.thirdparty.superuser", - "eu.chainfire.supersu", - "com.koushikdutta.superuser", - "eu.chainfire."]; + let should_match = &[ + "com.noshufou.android.su", + "com.thirdparty.superuser", + "eu.chainfire.supersu", + "com.koushikdutta.superuser", + "eu.chainfire.", + ]; let should_not_match = &["", "", "", ""]; @@ -1195,13 +1295,15 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(20).unwrap(); - let should_match = &[".contains(\"test-keys\")", - "/system/app/Superuser.apk", - "isDeviceRooted()", - "/system/bin/failsafe/su", - "/system/sd/xbin/su", - "RootTools.isAccessGiven()", - "RootTools.isAccessGiven()"]; + let should_match = &[ + ".contains(\"test-keys\")", + "/system/app/Superuser.apk", + "isDeviceRooted()", + "/system/bin/failsafe/su", + "/system/sd/xbin/su", + "RootTools.isAccessGiven()", + "RootTools.isAccessGiven()", + ]; let should_not_match = &["", "", "", ""]; @@ -1277,16 +1379,20 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(24).unwrap(); - let should_match = &["android.location getLastKnownLocation()", - "android.location requestLocationUpdates()", - "android.location getLatitude()", - "android.location getLongitude()"]; - - let should_not_match = &["getLastKnownLocation()", - "requestLocationUpdates()", - "getLatitude()", - "getLongitude()", - "android.location"]; + let should_match = &[ + "android.location getLastKnownLocation()", + "android.location requestLocationUpdates()", + "android.location getLatitude()", + "android.location getLongitude()", + ]; + + let should_not_match = &[ + "getLastKnownLocation()", + "requestLocationUpdates()", + "getLatitude()", + "getLongitude()", + "android.location", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -1303,8 +1409,10 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(25).unwrap(); - let should_match = &["android.util.Base64 .encodeToString()", - "android.util.Base64 .encode()"]; + let should_match = &[ + "android.util.Base64 .encodeToString()", + "android.util.Base64 .encode()", + ]; let should_not_match = &[".encodeToString()", ".encode()", "android.util.Base64"]; @@ -1361,10 +1469,12 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(28).unwrap(); - let should_match = &["super@super.es", - "android_analizer@dem.co.uk", - "foo@unadepatatas.com", - "android-rust69@tux.rox"]; + let should_match = &[ + "super@super.es", + "android_analizer@dem.co.uk", + "foo@unadepatatas.com", + "android-rust69@tux.rox", + ]; let should_not_match = &["@", "@strings/", "@id/user.id", "android:id=\"@id/userid\""]; @@ -1383,17 +1493,21 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(29).unwrap(); - let should_match = &["\"key.key ", - "\"cert.cert\"", - "\" key.pub ", - "\" cert.pub ", - " throw new IllegalArgumentException(\"translateAPI.key is not \ - specified\");"]; - - let should_not_match = &["Iterator localIterator = paramBundle.keySet().iterator();", - "import java.security.cert.X509Certificate;", - "", - ""]; + let should_match = &[ + "\"key.key ", + "\"cert.cert\"", + "\" key.pub ", + "\" cert.pub ", + " throw new IllegalArgumentException(\"translateAPI.key is not \ + specified\");", + ]; + + let should_not_match = &[ + "Iterator localIterator = paramBundle.keySet().iterator();", + "import java.security.cert.X509Certificate;", + "", + "", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -1448,15 +1562,19 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(32).unwrap(); - let should_match = &["android.utils.AESObfuscator getObfuscator();", - "android.utils.AESObfuscator obfuscation.getObfuscator();", - "utils.AESObfuscator getObfuscator();", - "utils.AESObfuscator obfuscation.getObfuscator();"]; + let should_match = &[ + "android.utils.AESObfuscator getObfuscator();", + "android.utils.AESObfuscator obfuscation.getObfuscator();", + "utils.AESObfuscator getObfuscator();", + "utils.AESObfuscator obfuscation.getObfuscator();", + ]; - let should_not_match = &["AESObfuscator getObfuscator();", - "android.utils.AESObfuscator obfuscation", - "getObfuscator();", - "android.utils.AESObfuscator"]; + let should_not_match = &[ + "AESObfuscator getObfuscator();", + "android.utils.AESObfuscator obfuscation", + "getObfuscator();", + "android.utils.AESObfuscator", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -1473,15 +1591,19 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(33).unwrap(); - let should_match = &["Runtime.getRuntime().exec(\"command\", options);", - "getRuntime().exec(\"ls -la\", options);", - "Runtime.getRuntime().exec(\"ls -la\", options);", - "getRuntime().exec(\"ps -l\", options);"]; + let should_match = &[ + "Runtime.getRuntime().exec(\"command\", options);", + "getRuntime().exec(\"ls -la\", options);", + "Runtime.getRuntime().exec(\"ls -la\", options);", + "getRuntime().exec(\"ps -l\", options);", + ]; - let should_not_match = &["Runtime.getRuntime()(\"\", options);", - "getRuntime()(\"\", options);", - "Runtime.getRuntime()(\"\", options);", - "getRuntime()(\"\", options);"]; + let should_not_match = &[ + "Runtime.getRuntime()(\"\", options);", + "getRuntime()(\"\", options);", + "Runtime.getRuntime()(\"\", options);", + "getRuntime()(\"\", options);", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -1498,13 +1620,17 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(34).unwrap(); - let should_match = &[" javax.net.ssl.SSLSocketFactory \ - SSLSocketFactory.getInsecure()"]; + let should_match = &[ + " javax.net.ssl.SSLSocketFactory \ + SSLSocketFactory.getInsecure()", + ]; - let should_not_match = &["getInsecure()", - "javax.net.ssl.SSL getInsecure();", - "javax.net.ssl.SSLSocketFactory", - "net.ssl.SSL getSecure();"]; + let should_not_match = &[ + "getInsecure()", + "javax.net.ssl.SSL getInsecure();", + "javax.net.ssl.SSLSocketFactory", + "net.ssl.SSL getSecure();", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -1521,12 +1647,16 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(35).unwrap(); - let should_match = &["finally { return;", - "finally { return;}"]; + let should_match = &[ + "finally { return;", + "finally { return;}", + ]; - let should_not_match = &["finally{}", - "finally{ var;}", - "finally { Printf (“Hello”); return true; }"]; + let should_not_match = &[ + "finally{}", + "finally{ var;}", + "finally { Printf (“Hello”); return true; }", + ]; for m in should_match { assert!(check_match(m, rule)); @@ -1543,13 +1673,17 @@ mod tests { let rules = load_rules(&config).unwrap(); let rule = rules.get(36).unwrap(); - let should_match = &["int var = EditText.getText Thread.sleep(100 + var);", - "var = .getText Thread.sleep(100 + var);"]; - - let should_not_match = &["int var4 = EditText.getText Thread.sleep(100 + var);", - "var = .getText Thread.sleep(100 + hola);", - "", - ""]; + let should_match = &[ + "int var = EditText.getText Thread.sleep(100 + var);", + "var = .getText Thread.sleep(100 + var);", + ]; + + let should_not_match = &[ + "int var4 = EditText.getText Thread.sleep(100 + var);", + "var = .getText Thread.sleep(100 + hola);", + "", + "", + ]; for m in should_match { assert!(check_match(m, rule)); diff --git a/src/static_analysis/manifest.rs b/src/static_analysis/manifest.rs index 8865fed43..c9eda5803 100644 --- a/src/static_analysis/manifest.rs +++ b/src/static_analysis/manifest.rs @@ -11,19 +11,24 @@ use colored::Colorize; use {Config, Criticality, print_warning, print_vulnerability, get_code, get_string, PARSER_CONFIG}; use results::{Results, Vulnerability}; -pub fn manifest_analysis>(config: &Config, - package: S, - results: &mut Results) - -> Option { +pub fn manifest_analysis>( + config: &Config, + package: S, + results: &mut Results, +) -> Option { if config.is_verbose() { - println!("Loading the manifest file. For this, we first parse the document and then we'll \ - analyze it.") + println!( + "Loading the manifest file. For this, we first parse the document and then we'll \ + analyze it." + ) } - let manifest = match Manifest::load(config.get_dist_folder().join(package.as_ref()), - config, - package.as_ref(), - results) { + let manifest = match Manifest::load( + config.get_dist_folder().join(package.as_ref()), + config, + package.as_ref(), + results, + ) { Ok(m) => { if config.is_verbose() { println!("{}", "The manifest was loaded successfully!".green()); @@ -32,29 +37,37 @@ pub fn manifest_analysis>(config: &Config, m } Err(e) => { - print_warning(format!("There was an error when loading the manifest: {}", - e.description())); + print_warning(format!( + "There was an error when loading the manifest: {}", + e.description() + )); if config.is_verbose() { - println!("The rest of the analysis will continue, but there will be no analysis \ + println!( + "The rest of the analysis will continue, but there will be no analysis \ of the AndroidManifest.xml file, and code analysis rules requiring \ - permissions will not run."); + permissions will not run." + ); } return None; } }; if manifest.get_package() != package.as_ref() { - print_warning(format!("Seems that the package in the AndroidManifest.xml is not the \ + print_warning(format!( + "Seems that the package in the AndroidManifest.xml is not the \ same as the application ID provided. Provided application id: \ {}, manifest package: {}", - package.as_ref(), - manifest.get_package())); + package.as_ref(), + manifest.get_package() + )); if config.is_verbose() { - println!("This does not mean that something went wrong, but it's supposed to have \ + println!( + "This does not mean that something went wrong, but it's supposed to have \ the application in the format {{package}}.apk in the {} folder and use the \ package as the application ID for this auditor.", - "downloads".italic()); + "downloads".italic() + ); } } @@ -83,13 +96,15 @@ pub fn manifest_analysis>(config: &Config, None => None, }; - let vuln = Vulnerability::new(criticality, - "Manifest Debug", - description, - Some("AndroidManifest.xml"), - line, - line, - code); + let vuln = Vulnerability::new( + criticality, + "Manifest Debug", + description, + Some("AndroidManifest.xml"), + line, + line, + code, + ); results.add_vulnerability(vuln); print_vulnerability(description, criticality); @@ -110,13 +125,15 @@ pub fn manifest_analysis>(config: &Config, None => None, }; - let vuln = Vulnerability::new(criticality, - "Large heap", - description, - Some("AndroidManifest.xml"), - line, - line, - code); + let vuln = Vulnerability::new( + criticality, + "Large heap", + description, + Some("AndroidManifest.xml"), + line, + line, + code, + ); results.add_vulnerability(vuln); print_vulnerability(description, criticality); } @@ -136,36 +153,41 @@ pub fn manifest_analysis>(config: &Config, None => None, }; - let vuln = Vulnerability::new(criticality, - "Allows Backup", - description, - Some("AndroidManifest.xml"), - line, - line, - code); + let vuln = Vulnerability::new( + criticality, + "Allows Backup", + description, + Some("AndroidManifest.xml"), + line, + line, + code, + ); results.add_vulnerability(vuln); print_vulnerability(description, criticality); } } for permission in config.get_permissions() { - if manifest - .get_permission_checklist() - .needs_permission(permission.get_permission()) && - permission.get_criticality() >= config.get_min_criticality() { + if manifest.get_permission_checklist().needs_permission( + permission + .get_permission(), + ) && permission.get_criticality() >= config.get_min_criticality() + { let line = get_line(manifest.get_code(), permission.get_permission().as_str()).ok(); let code = match line { Some(l) => Some(get_code(manifest.get_code(), l, l)), None => None, }; - let vuln = Vulnerability::new(permission.get_criticality(), - permission.get_label(), - permission.get_description(), - Some("AndroidManifest.xml"), - line, - line, - code); + let vuln = Vulnerability::new( + permission.get_criticality(), + permission.get_label(), + permission.get_description(), + Some("AndroidManifest.xml"), + line, + line, + code, + ); results.add_vulnerability(vuln); print_vulnerability(permission.get_description(), permission.get_criticality()); } @@ -200,11 +222,12 @@ pub struct Manifest { } impl Manifest { - pub fn load, S: AsRef>(path: P, - config: &Config, - package: S, - results: &mut Results) - -> Result { + pub fn load, S: AsRef>( + path: P, + config: &Config, + package: S, + results: &mut Results, + ) -> Result { let mut file = File::open(path.as_ref().join("AndroidManifest.xml"))?; let mut manifest = Manifest::default(); @@ -244,14 +267,16 @@ impl Manifest { match InstallLocation::from_str(attr.value.as_str()) { Ok(l) => l, Err(e) => { - print_warning(format!("An error occurred when \ + print_warning(format!( + "An error occurred when \ parsing the \ installLocation \ attribute in the \ manifest: {}.\nThe \ process will continue, \ though.", - e.description())); + e.description() + )); break; } }; @@ -310,13 +335,15 @@ impl Manifest { let debug: bool = match attr.value.as_str().parse() { Ok(b) => b, Err(e) => { - print_warning(format!("An error occurred \ + print_warning(format!( + "An error occurred \ when parsing the \ debuggable attribute in \ the manifest: \ {}.\nThe process \ will continue, though.", - e.description())); + e.description() + )); break; } }; @@ -348,13 +375,15 @@ impl Manifest { let has_code: bool = match attr.value.as_str().parse() { Ok(b) => b, Err(e) => { - print_warning(format!("An error occurred \ + print_warning(format!( + "An error occurred \ when parsing the \ hasCode attribute in \ the manifest: \ {}.\nThe process \ will continue, though.", - e.description())); + e.description() + )); break; } }; @@ -381,26 +410,30 @@ impl Manifest { } } "label" => { - manifest - .set_label(if attr.value.starts_with("@string/") { - match get_string(&attr.value[8..], - config, - package.as_ref()) { - Ok(s) => s, - Err(e) => { - print_warning(format!("An error occurred when\ + manifest.set_label( + if attr.value.starts_with("@string/") { + match get_string( + &attr.value[8..], + config, + package.as_ref(), + ) { + Ok(s) => s, + Err(e) => { + print_warning(format!( + "An error occurred when\ trying to get the string\ for the app label in the\ manifest: {}.\nThe process\ will continue, though.", - e.description())); - break; + e.description() + )); + break; + } } - } - } else { - attr.value - } - .as_str()) + } else { + attr.value + }.as_str(), + ) } _ => {} } @@ -409,8 +442,10 @@ impl Manifest { "uses-permission" => { for attr in attributes { if let "name" = attr.name.local_name.as_str() { - let permission = if let Ok(p) = - Permission::from_str(attr.value.as_str()) { + let permission = if let Ok(p) = Permission::from_str( + attr.value.as_str(), + ) + { p } else { let line = @@ -427,13 +462,15 @@ impl Manifest { let file = Some("AndroidManifest.xml"); if criticality > config.get_min_criticality() { - let vuln = Vulnerability::new(criticality, - "Unknown permission", - description, - file, - line, - line, - code); + let vuln = Vulnerability::new( + criticality, + "Unknown permission", + description, + file, + line, + line, + code, + ); results.add_vulnerability(vuln); print_vulnerability(description, criticality); @@ -467,11 +504,13 @@ impl Manifest { match exported { Some(true) | None => { if tag != "provider" || exported.is_some() || - manifest.get_min_sdk() < 17 { + manifest.get_min_sdk() < 17 + { - let line = get_line(manifest.get_code(), - &format!("android:name=\"{}\"", name)) - .ok(); + let line = get_line( + manifest.get_code(), + &format!("android:name=\"{}\"", name), + ).ok(); let code = match line { Some(l) => Some(get_code(manifest.get_code(), l, l)), None => None, @@ -480,25 +519,32 @@ impl Manifest { let criticality = Criticality::Warning; if criticality >= config.get_min_criticality() { - let vuln = Vulnerability::new(criticality, - format!("Exported {}", - tag), - format!("Exported {} was \ + let vuln = Vulnerability::new( + criticality, + format!("Exported {}", tag), + format!( + "Exported {} was \ found. It can be \ used by other \ applications.", - tag), - Some("AndroidManifest.xml"), - line, - line, - code); + tag + ), + Some("AndroidManifest.xml"), + line, + line, + code, + ); results.add_vulnerability(vuln); - print_vulnerability(format!("Exported {} was found. \ + print_vulnerability( + format!( + "Exported {} was found. \ It can be used by \ other applications.", - tag), - Criticality::Warning); + tag + ), + Criticality::Warning, + ); } } } @@ -510,10 +556,12 @@ impl Manifest { } Ok(_) => {} Err(e) => { - print_warning(format!("An error occurred when parsing the \ + print_warning(format!( + "An error occurred when parsing the \ AndroidManifest.xml file: {}.\nThe process will \ continue, though.", - e.description())); + e.description() + )); } } } @@ -710,12 +758,18 @@ mod tests { #[test] fn it_install_loc_from_str() { - assert_eq!(InstallLocation::InternalOnly, - InstallLocation::from_str("internalOnly").unwrap()); - assert_eq!(InstallLocation::Auto, - InstallLocation::from_str("auto").unwrap()); - assert_eq!(InstallLocation::PreferExternal, - InstallLocation::from_str("preferExternal").unwrap()); + assert_eq!( + InstallLocation::InternalOnly, + InstallLocation::from_str("internalOnly").unwrap() + ); + assert_eq!( + InstallLocation::Auto, + InstallLocation::from_str("auto").unwrap() + ); + assert_eq!( + InstallLocation::PreferExternal, + InstallLocation::from_str("preferExternal").unwrap() + ); assert!(InstallLocation::from_str("Razican").is_err()); } @@ -724,8 +778,12 @@ mod tests { let mut checklist: PermissionChecklist = Default::default(); checklist.set_needs_permission(Permission::AndroidPermissionInternet); - assert!(checklist.needs_permission(Permission::AndroidPermissionInternet)); - assert!(!checklist.needs_permission(Permission::AndroidPermissionWriteExternalStorage)); + assert!(checklist.needs_permission( + Permission::AndroidPermissionInternet, + )); + assert!(!checklist.needs_permission( + Permission::AndroidPermissionWriteExternalStorage, + )); } #[test] @@ -736,8 +794,10 @@ mod tests { assert_eq!(internet, Permission::AndroidPermissionInternet); assert_eq!(storage, Permission::AndroidPermissionWriteExternalStorage); assert_eq!(internet.as_str(), "android.permission.INTERNET"); - assert_eq!(storage.as_str(), - "android.permission.WRITE_EXTERNAL_STORAGE"); + assert_eq!( + storage.as_str(), + "android.permission.WRITE_EXTERNAL_STORAGE" + ); assert!(Permission::from_str("Razican").is_err()); } } @@ -2865,264 +2925,588 @@ pub enum Permission { impl Permission { pub fn as_str(&self) -> &str { match *self { - Permission::AndroidPermissionAccessAllExternalStorage => "android.permission.ACCESS_ALL_EXTERNAL_STORAGE", - Permission::AndroidPermissionAccessCheckinProperties => "android.permission.ACCESS_CHECKIN_PROPERTIES", - Permission::AndroidPermissionAccessCoarseLocation => "android.permission.ACCESS_COARSE_LOCATION", - Permission::AndroidPermissionAccessFineLocation => "android.permission.ACCESS_FINE_LOCATION", - Permission::AndroidPermissionAccessLocationExtraCommands => "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS", - Permission::AndroidPermissionAccessMockLocation => "android.permission.ACCESS_MOCK_LOCATION", + Permission::AndroidPermissionAccessAllExternalStorage => { + "android.permission.ACCESS_ALL_EXTERNAL_STORAGE" + } + Permission::AndroidPermissionAccessCheckinProperties => { + "android.permission.ACCESS_CHECKIN_PROPERTIES" + } + Permission::AndroidPermissionAccessCoarseLocation => { + "android.permission.ACCESS_COARSE_LOCATION" + } + Permission::AndroidPermissionAccessFineLocation => { + "android.permission.ACCESS_FINE_LOCATION" + } + Permission::AndroidPermissionAccessLocationExtraCommands => { + "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" + } + Permission::AndroidPermissionAccessMockLocation => { + "android.permission.ACCESS_MOCK_LOCATION" + } Permission::AndroidPermissionAccessMtp => "android.permission.ACCESS_MTP", - Permission::AndroidPermissionAccessNetworkState => "android.permission.ACCESS_NETWORK_STATE", - Permission::AndroidPermissionAccessNotificationPolicy => "android.permission.ACCESS_NOTIFICATION_POLICY", - Permission::AndroidPermissionAccessWimaxState => "android.permission.ACCESS_WIMAX_STATE", + Permission::AndroidPermissionAccessNetworkState => { + "android.permission.ACCESS_NETWORK_STATE" + } + Permission::AndroidPermissionAccessNotificationPolicy => { + "android.permission.ACCESS_NOTIFICATION_POLICY" + } + Permission::AndroidPermissionAccessWimaxState => { + "android.permission.ACCESS_WIMAX_STATE" + } Permission::AndroidPermissionAccessWifiState => "android.permission.ACCESS_WIFI_STATE", Permission::AndroidPermissionAccountManager => "android.permission.ACCOUNT_MANAGER", Permission::AndroidPermissionAsecAccess => "android.permission.ASEC_ACCESS", Permission::AndroidPermissionAsecCreate => "android.permission.ASEC_CREATE", Permission::AndroidPermissionAsecDestroy => "android.permission.ASEC_DESTROY", - Permission::AndroidPermissionAsecMountUnmount => "android.permission.ASEC_MOUNT_UNMOUNT", + Permission::AndroidPermissionAsecMountUnmount => { + "android.permission.ASEC_MOUNT_UNMOUNT" + } Permission::AndroidPermissionAsecRename => "android.permission.ASEC_RENAME", - Permission::AndroidPermissionAuthenticateAccounts => "android.permission.AUTHENTICATE_ACCOUNTS", + Permission::AndroidPermissionAuthenticateAccounts => { + "android.permission.AUTHENTICATE_ACCOUNTS" + } Permission::AndroidPermissionBatteryStats => "android.permission.BATTERY_STATS", - Permission::AndroidPermissionBindAccessibilityService => "android.permission.BIND_ACCESSIBILITY_SERVICE", + Permission::AndroidPermissionBindAccessibilityService => { + "android.permission.BIND_ACCESSIBILITY_SERVICE" + } Permission::AndroidPermissionBindAppwidget => "android.permission.BIND_APPWIDGET", Permission::AndroidPermissionBindCallService => "android.permission.BIND_CALL_SERVICE", - Permission::AndroidPermissionBindCarrierMessagingService => "android.permission.BIND_CARRIER_MESSAGING_SERVICE", - Permission::AndroidPermissionBindCarrierServices => "android.permission.BIND_CARRIER_SERVICES", - Permission::AndroidPermissionBindChooserTargetService => "android.permission.BIND_CHOOSER_TARGET_SERVICE", + Permission::AndroidPermissionBindCarrierMessagingService => { + "android.permission.BIND_CARRIER_MESSAGING_SERVICE" + } + Permission::AndroidPermissionBindCarrierServices => { + "android.permission.BIND_CARRIER_SERVICES" + } + Permission::AndroidPermissionBindChooserTargetService => { + "android.permission.BIND_CHOOSER_TARGET_SERVICE" + } Permission::AndroidPermissionBindDeviceAdmin => "android.permission.BIND_DEVICE_ADMIN", - Permission::AndroidPermissionBindDirectorySearch => "android.permission.BIND_DIRECTORY_SEARCH", - Permission::AndroidPermissionBindDreamService => "android.permission.BIND_DREAM_SERVICE", - Permission::AndroidPermissionBindIncallService => "android.permission.BIND_INCALL_SERVICE", + Permission::AndroidPermissionBindDirectorySearch => { + "android.permission.BIND_DIRECTORY_SEARCH" + } + Permission::AndroidPermissionBindDreamService => { + "android.permission.BIND_DREAM_SERVICE" + } + Permission::AndroidPermissionBindIncallService => { + "android.permission.BIND_INCALL_SERVICE" + } Permission::AndroidPermissionBindInputMethod => "android.permission.BIND_INPUT_METHOD", - Permission::AndroidPermissionBindKeyguardAppwidget => "android.permission.BIND_KEYGUARD_APPWIDGET", - Permission::AndroidPermissionBindMidiDeviceService => "android.permission.BIND_MIDI_DEVICE_SERVICE", + Permission::AndroidPermissionBindKeyguardAppwidget => { + "android.permission.BIND_KEYGUARD_APPWIDGET" + } + Permission::AndroidPermissionBindMidiDeviceService => { + "android.permission.BIND_MIDI_DEVICE_SERVICE" + } Permission::AndroidPermissionBindNfcService => "android.permission.BIND_NFC_SERVICE", - Permission::AndroidPermissionBindNotificationListenerService => "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE", - Permission::AndroidPermissionBindPrintService => "android.permission.BIND_PRINT_SERVICE", + Permission::AndroidPermissionBindNotificationListenerService => { + "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" + } + Permission::AndroidPermissionBindPrintService => { + "android.permission.BIND_PRINT_SERVICE" + } Permission::AndroidPermissionBindRemoteviews => "android.permission.BIND_REMOTEVIEWS", - Permission::AndroidPermissionBindTelecomConnectionService => "android.permission.BIND_TELECOM_CONNECTION_SERVICE", + Permission::AndroidPermissionBindTelecomConnectionService => { + "android.permission.BIND_TELECOM_CONNECTION_SERVICE" + } Permission::AndroidPermissionBindTextService => "android.permission.BIND_TEXT_SERVICE", Permission::AndroidPermissionBindTvInput => "android.permission.BIND_TV_INPUT", - Permission::AndroidPermissionBindVoiceInteraction => "android.permission.BIND_VOICE_INTERACTION", + Permission::AndroidPermissionBindVoiceInteraction => { + "android.permission.BIND_VOICE_INTERACTION" + } Permission::AndroidPermissionBindVpnService => "android.permission.BIND_VPN_SERVICE", Permission::AndroidPermissionBindWallpaper => "android.permission.BIND_WALLPAPER", Permission::AndroidPermissionBluetooth => "android.permission.BLUETOOTH", Permission::AndroidPermissionBluetoothAdmin => "android.permission.BLUETOOTH_ADMIN", - Permission::AndroidPermissionBluetoothPrivileged => "android.permission.BLUETOOTH_PRIVILEGED", + Permission::AndroidPermissionBluetoothPrivileged => { + "android.permission.BLUETOOTH_PRIVILEGED" + } Permission::AndroidPermissionBluetoothStack => "android.permission.BLUETOOTH_STACK", Permission::AndroidPermissionBodySensors => "android.permission.BODY_SENSORS", - Permission::AndroidPermissionBroadcastPackageRemoved => "android.permission.BROADCAST_PACKAGE_REMOVED", + Permission::AndroidPermissionBroadcastPackageRemoved => { + "android.permission.BROADCAST_PACKAGE_REMOVED" + } Permission::AndroidPermissionBroadcastSms => "android.permission.BROADCAST_SMS", Permission::AndroidPermissionBroadcastSticky => "android.permission.BROADCAST_STICKY", - Permission::AndroidPermissionBroadcastWapPush => "android.permission.BROADCAST_WAP_PUSH", + Permission::AndroidPermissionBroadcastWapPush => { + "android.permission.BROADCAST_WAP_PUSH" + } Permission::AndroidPermissionCallPhone => "android.permission.CALL_PHONE", Permission::AndroidPermissionCallPrivileged => "android.permission.CALL_PRIVILEGED", Permission::AndroidPermissionCamera => "android.permission.CAMERA", - Permission::AndroidPermissionCameraDisableTransmitLed => "android.permission.CAMERA_DISABLE_TRANSMIT_LED", - Permission::AndroidPermissionCaptureAudioOutput => "android.permission.CAPTURE_AUDIO_OUTPUT", - Permission::AndroidPermissionCaptureSecureVideoOutput => "android.permission.CAPTURE_SECURE_VIDEO_OUTPUT", - Permission::AndroidPermissionCaptureVideoOutput => "android.permission.CAPTURE_VIDEO_OUTPUT", - Permission::AndroidPermissionChangeBackgroundDataSetting => "android.permission.CHANGE_BACKGROUND_DATA_SETTING", - Permission::AndroidPermissionChangeComponentEnabledState => "android.permission.CHANGE_COMPONENT_ENABLED_STATE", - Permission::AndroidPermissionChangeConfiguration => "android.permission.CHANGE_CONFIGURATION", - Permission::AndroidPermissionChangeNetworkState => "android.permission.CHANGE_NETWORK_STATE", - Permission::AndroidPermissionChangeWimaxState => "android.permission.CHANGE_WIMAX_STATE", - Permission::AndroidPermissionChangeWifiMulticastState => "android.permission.CHANGE_WIFI_MULTICAST_STATE", + Permission::AndroidPermissionCameraDisableTransmitLed => { + "android.permission.CAMERA_DISABLE_TRANSMIT_LED" + } + Permission::AndroidPermissionCaptureAudioOutput => { + "android.permission.CAPTURE_AUDIO_OUTPUT" + } + Permission::AndroidPermissionCaptureSecureVideoOutput => { + "android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" + } + Permission::AndroidPermissionCaptureVideoOutput => { + "android.permission.CAPTURE_VIDEO_OUTPUT" + } + Permission::AndroidPermissionChangeBackgroundDataSetting => { + "android.permission.CHANGE_BACKGROUND_DATA_SETTING" + } + Permission::AndroidPermissionChangeComponentEnabledState => { + "android.permission.CHANGE_COMPONENT_ENABLED_STATE" + } + Permission::AndroidPermissionChangeConfiguration => { + "android.permission.CHANGE_CONFIGURATION" + } + Permission::AndroidPermissionChangeNetworkState => { + "android.permission.CHANGE_NETWORK_STATE" + } + Permission::AndroidPermissionChangeWimaxState => { + "android.permission.CHANGE_WIMAX_STATE" + } + Permission::AndroidPermissionChangeWifiMulticastState => { + "android.permission.CHANGE_WIFI_MULTICAST_STATE" + } Permission::AndroidPermissionChangeWifiState => "android.permission.CHANGE_WIFI_STATE", Permission::AndroidPermissionClearAppCache => "android.permission.CLEAR_APP_CACHE", - Permission::AndroidPermissionConnectivityInternal => "android.permission.CONNECTIVITY_INTERNAL", - Permission::AndroidPermissionControlLocationUpdates => "android.permission.CONTROL_LOCATION_UPDATES", - Permission::AndroidPermissionDeleteCacheFiles => "android.permission.DELETE_CACHE_FILES", + Permission::AndroidPermissionConnectivityInternal => { + "android.permission.CONNECTIVITY_INTERNAL" + } + Permission::AndroidPermissionControlLocationUpdates => { + "android.permission.CONTROL_LOCATION_UPDATES" + } + Permission::AndroidPermissionDeleteCacheFiles => { + "android.permission.DELETE_CACHE_FILES" + } Permission::AndroidPermissionDeletePackages => "android.permission.DELETE_PACKAGES", Permission::AndroidPermissionDiagnostic => "android.permission.DIAGNOSTIC", Permission::AndroidPermissionDisableKeyguard => "android.permission.DISABLE_KEYGUARD", - Permission::AndroidPermissionDownloadWithoutNotification => "android.permission.DOWNLOAD_WITHOUT_NOTIFICATION", + Permission::AndroidPermissionDownloadWithoutNotification => { + "android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" + } Permission::AndroidPermissionDump => "android.permission.DUMP", Permission::AndroidPermissionExpandStatusBar => "android.permission.EXPAND_STATUS_BAR", Permission::AndroidPermissionFactoryTest => "android.permission.FACTORY_TEST", Permission::AndroidPermissionFlashlight => "android.permission.FLASHLIGHT", - Permission::AndroidPermissionForceStopPackages => "android.permission.FORCE_STOP_PACKAGES", + Permission::AndroidPermissionForceStopPackages => { + "android.permission.FORCE_STOP_PACKAGES" + } Permission::AndroidPermissionGetAccounts => "android.permission.GET_ACCOUNTS", - Permission::AndroidPermissionGetAccountsPrivileged => "android.permission.GET_ACCOUNTS_PRIVILEGED", + Permission::AndroidPermissionGetAccountsPrivileged => { + "android.permission.GET_ACCOUNTS_PRIVILEGED" + } Permission::AndroidPermissionGetAppOpsStats => "android.permission.GET_APP_OPS_STATS", - Permission::AndroidPermissionGetDetailedTasks => "android.permission.GET_DETAILED_TASKS", + Permission::AndroidPermissionGetDetailedTasks => { + "android.permission.GET_DETAILED_TASKS" + } Permission::AndroidPermissionGetPackageSize => "android.permission.GET_PACKAGE_SIZE", Permission::AndroidPermissionGetTasks => "android.permission.GET_TASKS", Permission::AndroidPermissionGlobalSearch => "android.permission.GLOBAL_SEARCH", - Permission::AndroidPermissionGlobalSearchControl => "android.permission.GLOBAL_SEARCH_CONTROL", + Permission::AndroidPermissionGlobalSearchControl => { + "android.permission.GLOBAL_SEARCH_CONTROL" + } Permission::AndroidPermissionHardwareTest => "android.permission.HARDWARE_TEST", - Permission::AndroidPermissionInstallLocationProvider => "android.permission.INSTALL_LOCATION_PROVIDER", + Permission::AndroidPermissionInstallLocationProvider => { + "android.permission.INSTALL_LOCATION_PROVIDER" + } Permission::AndroidPermissionInstallPackages => "android.permission.INSTALL_PACKAGES", - Permission::AndroidPermissionInteractAcrossUsers => "android.permission.INTERACT_ACROSS_USERS", - Permission::AndroidPermissionInteractAcrossUsersFull => "android.permission.INTERACT_ACROSS_USERS_FULL", + Permission::AndroidPermissionInteractAcrossUsers => { + "android.permission.INTERACT_ACROSS_USERS" + } + Permission::AndroidPermissionInteractAcrossUsersFull => { + "android.permission.INTERACT_ACROSS_USERS_FULL" + } Permission::AndroidPermissionInternet => "android.permission.INTERNET", - Permission::AndroidPermissionKillBackgroundProcesses => "android.permission.KILL_BACKGROUND_PROCESSES", + Permission::AndroidPermissionKillBackgroundProcesses => { + "android.permission.KILL_BACKGROUND_PROCESSES" + } Permission::AndroidPermissionLocationHardware => "android.permission.LOCATION_HARDWARE", Permission::AndroidPermissionLoopRadio => "android.permission.LOOP_RADIO", Permission::AndroidPermissionManageAccounts => "android.permission.MANAGE_ACCOUNTS", - Permission::AndroidPermissionManageActivityStacks => "android.permission.MANAGE_ACTIVITY_STACKS", + Permission::AndroidPermissionManageActivityStacks => { + "android.permission.MANAGE_ACTIVITY_STACKS" + } Permission::AndroidPermissionManageDocuments => "android.permission.MANAGE_DOCUMENTS", Permission::AndroidPermissionManageUsb => "android.permission.MANAGE_USB", Permission::AndroidPermissionManageUsers => "android.permission.MANAGE_USERS", Permission::AndroidPermissionMasterClear => "android.permission.MASTER_CLEAR", - Permission::AndroidPermissionMediaContentControl => "android.permission.MEDIA_CONTENT_CONTROL", - Permission::AndroidPermissionModifyAppwidgetBindPermissions => "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS", - Permission::AndroidPermissionModifyAudioSettings => "android.permission.MODIFY_AUDIO_SETTINGS", - Permission::AndroidPermissionModifyPhoneState => "android.permission.MODIFY_PHONE_STATE", - Permission::AndroidPermissionMountFormatFilesystems => "android.permission.MOUNT_FORMAT_FILESYSTEMS", - Permission::AndroidPermissionMountUnmountFilesystems => "android.permission.MOUNT_UNMOUNT_FILESYSTEMS", + Permission::AndroidPermissionMediaContentControl => { + "android.permission.MEDIA_CONTENT_CONTROL" + } + Permission::AndroidPermissionModifyAppwidgetBindPermissions => { + "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS" + } + Permission::AndroidPermissionModifyAudioSettings => { + "android.permission.MODIFY_AUDIO_SETTINGS" + } + Permission::AndroidPermissionModifyPhoneState => { + "android.permission.MODIFY_PHONE_STATE" + } + Permission::AndroidPermissionMountFormatFilesystems => { + "android.permission.MOUNT_FORMAT_FILESYSTEMS" + } + Permission::AndroidPermissionMountUnmountFilesystems => { + "android.permission.MOUNT_UNMOUNT_FILESYSTEMS" + } Permission::AndroidPermissionNetAdmin => "android.permission.NET_ADMIN", Permission::AndroidPermissionNetTunneling => "android.permission.NET_TUNNELING", Permission::AndroidPermissionNfc => "android.permission.NFC", - Permission::AndroidPermissionPackageUsageStats => "android.permission.PACKAGE_USAGE_STATS", - Permission::AndroidPermissionPersistentActivity => "android.permission.PERSISTENT_ACTIVITY", - Permission::AndroidPermissionProcessOutgoingCalls => "android.permission.PROCESS_OUTGOING_CALLS", + Permission::AndroidPermissionPackageUsageStats => { + "android.permission.PACKAGE_USAGE_STATS" + } + Permission::AndroidPermissionPersistentActivity => { + "android.permission.PERSISTENT_ACTIVITY" + } + Permission::AndroidPermissionProcessOutgoingCalls => { + "android.permission.PROCESS_OUTGOING_CALLS" + } Permission::AndroidPermissionReadCalendar => "android.permission.READ_CALENDAR", Permission::AndroidPermissionReadCallLog => "android.permission.READ_CALL_LOG", - Permission::AndroidPermissionReadCellBroadcasts => "android.permission.READ_CELL_BROADCASTS", + Permission::AndroidPermissionReadCellBroadcasts => { + "android.permission.READ_CELL_BROADCASTS" + } Permission::AndroidPermissionReadContacts => "android.permission.READ_CONTACTS", Permission::AndroidPermissionReadDreamState => "android.permission.READ_DREAM_STATE", - Permission::AndroidPermissionReadExternalStorage => "android.permission.READ_EXTERNAL_STORAGE", + Permission::AndroidPermissionReadExternalStorage => { + "android.permission.READ_EXTERNAL_STORAGE" + } Permission::AndroidPermissionReadFrameBuffer => "android.permission.READ_FRAME_BUFFER", Permission::AndroidPermissionReadInputState => "android.permission.READ_INPUT_STATE", Permission::AndroidPermissionReadLogs => "android.permission.READ_LOGS", Permission::AndroidPermissionReadPhoneState => "android.permission.READ_PHONE_STATE", - Permission::AndroidPermissionReadPrivilegedPhoneState => "android.permission.READ_PRIVILEGED_PHONE_STATE", + Permission::AndroidPermissionReadPrivilegedPhoneState => { + "android.permission.READ_PRIVILEGED_PHONE_STATE" + } Permission::AndroidPermissionReadProfile => "android.permission.READ_PROFILE", Permission::AndroidPermissionReadSms => "android.permission.READ_SMS", - Permission::AndroidPermissionReadSocialStream => "android.permission.READ_SOCIAL_STREAM", - Permission::AndroidPermissionReadSyncSettings => "android.permission.READ_SYNC_SETTINGS", + Permission::AndroidPermissionReadSocialStream => { + "android.permission.READ_SOCIAL_STREAM" + } + Permission::AndroidPermissionReadSyncSettings => { + "android.permission.READ_SYNC_SETTINGS" + } Permission::AndroidPermissionReadSyncStats => "android.permission.READ_SYNC_STATS", - Permission::AndroidPermissionReadUserDictionary => "android.permission.READ_USER_DICTIONARY", + Permission::AndroidPermissionReadUserDictionary => { + "android.permission.READ_USER_DICTIONARY" + } Permission::AndroidPermissionReboot => "android.permission.REBOOT", - Permission::AndroidPermissionReceiveBootCompleted => "android.permission.RECEIVE_BOOT_COMPLETED", - Permission::AndroidPermissionReceiveDataActivityChange => "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE", - Permission::AndroidPermissionReceiveEmergencyBroadcast => "android.permission.RECEIVE_EMERGENCY_BROADCAST", + Permission::AndroidPermissionReceiveBootCompleted => { + "android.permission.RECEIVE_BOOT_COMPLETED" + } + Permission::AndroidPermissionReceiveDataActivityChange => { + "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE" + } + Permission::AndroidPermissionReceiveEmergencyBroadcast => { + "android.permission.RECEIVE_EMERGENCY_BROADCAST" + } Permission::AndroidPermissionReceiveMms => "android.permission.RECEIVE_MMS", Permission::AndroidPermissionReceiveSms => "android.permission.RECEIVE_SMS", Permission::AndroidPermissionReceiveWapPush => "android.permission.RECEIVE_WAP_PUSH", Permission::AndroidPermissionRecordAudio => "android.permission.RECORD_AUDIO", - Permission::AndroidPermissionRemoteAudioPlayback => "android.permission.REMOTE_AUDIO_PLAYBACK", + Permission::AndroidPermissionRemoteAudioPlayback => { + "android.permission.REMOTE_AUDIO_PLAYBACK" + } Permission::AndroidPermissionRemoveTasks => "android.permission.REMOVE_TASKS", Permission::AndroidPermissionReorderTasks => "android.permission.REORDER_TASKS", - Permission::AndroidPermissionRequestIgnoreBatteryOptimizations => "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", - Permission::AndroidPermissionRequestInstallPackages => "android.permission.REQUEST_INSTALL_PACKAGES", + Permission::AndroidPermissionRequestIgnoreBatteryOptimizations => { + "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" + } + Permission::AndroidPermissionRequestInstallPackages => { + "android.permission.REQUEST_INSTALL_PACKAGES" + } Permission::AndroidPermissionRestartPackages => "android.permission.RESTART_PACKAGES", - Permission::AndroidPermissionRetrieveWindowContent => "android.permission.RETRIEVE_WINDOW_CONTENT", - Permission::AndroidPermissionSendRespondViaMessage => "android.permission.SEND_RESPOND_VIA_MESSAGE", + Permission::AndroidPermissionRetrieveWindowContent => { + "android.permission.RETRIEVE_WINDOW_CONTENT" + } + Permission::AndroidPermissionSendRespondViaMessage => { + "android.permission.SEND_RESPOND_VIA_MESSAGE" + } Permission::AndroidPermissionSendSms => "android.permission.SEND_SMS", Permission::AndroidPermissionSetAlwaysFinish => "android.permission.SET_ALWAYS_FINISH", - Permission::AndroidPermissionSetAnimationScale => "android.permission.SET_ANIMATION_SCALE", + Permission::AndroidPermissionSetAnimationScale => { + "android.permission.SET_ANIMATION_SCALE" + } Permission::AndroidPermissionSetDebugApp => "android.permission.SET_DEBUG_APP", - Permission::AndroidPermissionSetPreferredApplications => "android.permission.SET_PREFERRED_APPLICATIONS", + Permission::AndroidPermissionSetPreferredApplications => { + "android.permission.SET_PREFERRED_APPLICATIONS" + } Permission::AndroidPermissionSetProcessLimit => "android.permission.SET_PROCESS_LIMIT", - Permission::AndroidPermissionSetScreenCompatibility => "android.permission.SET_SCREEN_COMPATIBILITY", + Permission::AndroidPermissionSetScreenCompatibility => { + "android.permission.SET_SCREEN_COMPATIBILITY" + } Permission::AndroidPermissionSetTime => "android.permission.SET_TIME", Permission::AndroidPermissionSetTimeZone => "android.permission.SET_TIME_ZONE", Permission::AndroidPermissionSetWallpaper => "android.permission.SET_WALLPAPER", - Permission::AndroidPermissionSetWallpaperComponent => "android.permission.SET_WALLPAPER_COMPONENT", - Permission::AndroidPermissionSetWallpaperHints => "android.permission.SET_WALLPAPER_HINTS", - Permission::AndroidPermissionSignalPersistentProcesses => "android.permission.SIGNAL_PERSISTENT_PROCESSES", - Permission::AndroidPermissionStartAnyActivity => "android.permission.START_ANY_ACTIVITY", + Permission::AndroidPermissionSetWallpaperComponent => { + "android.permission.SET_WALLPAPER_COMPONENT" + } + Permission::AndroidPermissionSetWallpaperHints => { + "android.permission.SET_WALLPAPER_HINTS" + } + Permission::AndroidPermissionSignalPersistentProcesses => { + "android.permission.SIGNAL_PERSISTENT_PROCESSES" + } + Permission::AndroidPermissionStartAnyActivity => { + "android.permission.START_ANY_ACTIVITY" + } Permission::AndroidPermissionStatusBar => "android.permission.STATUS_BAR", - Permission::AndroidPermissionSubscribedFeedsRead => "android.permission.SUBSCRIBED_FEEDS_READ", - Permission::AndroidPermissionSystemAlertWindow => "android.permission.SYSTEM_ALERT_WINDOW", - Permission::AndroidPermissionSubscribedFeedsWrite => "android.permission.SUBSCRIBED_FEEDS_WRITE", + Permission::AndroidPermissionSubscribedFeedsRead => { + "android.permission.SUBSCRIBED_FEEDS_READ" + } + Permission::AndroidPermissionSystemAlertWindow => { + "android.permission.SYSTEM_ALERT_WINDOW" + } + Permission::AndroidPermissionSubscribedFeedsWrite => { + "android.permission.SUBSCRIBED_FEEDS_WRITE" + } Permission::AndroidPermissionTransmitIr => "android.permission.TRANSMIT_IR", - Permission::AndroidPermissionUpdateDeviceStats => "android.permission.UPDATE_DEVICE_STATS", + Permission::AndroidPermissionUpdateDeviceStats => { + "android.permission.UPDATE_DEVICE_STATS" + } Permission::AndroidPermissionUseCredentials => "android.permission.USE_CREDENTIALS", Permission::AndroidPermissionUseFingerprint => "android.permission.USE_FINGERPRINT", Permission::AndroidPermissionUseSip => "android.permission.USE_SIP", Permission::AndroidPermissionVibrate => "android.permission.VIBRATE", Permission::AndroidPermissionWakeLock => "android.permission.WAKE_LOCK", - Permission::AndroidPermissionWriteApnSettings => "android.permission.WRITE_APN_SETTINGS", + Permission::AndroidPermissionWriteApnSettings => { + "android.permission.WRITE_APN_SETTINGS" + } Permission::AndroidPermissionWriteCalendar => "android.permission.WRITE_CALENDAR", Permission::AndroidPermissionWriteCallLog => "android.permission.WRITE_CALL_LOG", Permission::AndroidPermissionWriteContacts => "android.permission.WRITE_CONTACTS", Permission::AndroidPermissionWriteDreamState => "android.permission.WRITE_DREAM_STATE", - Permission::AndroidPermissionWriteExternalStorage => "android.permission.WRITE_EXTERNAL_STORAGE", + Permission::AndroidPermissionWriteExternalStorage => { + "android.permission.WRITE_EXTERNAL_STORAGE" + } Permission::AndroidPermissionWriteGservices => "android.permission.WRITE_GSERVICES", - Permission::AndroidPermissionWriteMediaStorage => "android.permission.WRITE_MEDIA_STORAGE", + Permission::AndroidPermissionWriteMediaStorage => { + "android.permission.WRITE_MEDIA_STORAGE" + } Permission::AndroidPermissionWriteProfile => "android.permission.WRITE_PROFILE", - Permission::AndroidPermissionWriteSecureSettings => "android.permission.WRITE_SECURE_SETTINGS", + Permission::AndroidPermissionWriteSecureSettings => { + "android.permission.WRITE_SECURE_SETTINGS" + } Permission::AndroidPermissionWriteSettings => "android.permission.WRITE_SETTINGS", Permission::AndroidPermissionWriteSms => "android.permission.WRITE_SMS", - Permission::AndroidPermissionWriteSocialStream => "android.permission.WRITE_SOCIAL_STREAM", - Permission::AndroidPermissionWriteSyncSettings => "android.permission.WRITE_SYNC_SETTINGS", - Permission::AndroidPermissionWriteUserDictionary => "android.permission.WRITE_USER_DICTIONARY", - Permission::ComAndroidAlarmPermissionSetAlarm => "com.android.alarm.permission.SET_ALARM", - Permission::ComAndroidBrowserPermissionReadHistoryBookmarks => "com.android.browser.permission.READ_HISTORY_BOOKMARKS", - Permission::ComAndroidBrowserPermissionWriteHistoryBookmarks => "com.android.browser.permission.WRITE_HISTORY_BOOKMARKS", - Permission::ComAndroidEmailPermissionReadAttachment => "com.android.email.permission.READ_ATTACHMENT", - Permission::ComAndroidLauncherPermissionInstallShortcut => "com.android.launcher.permission.INSTALL_SHORTCUT", - Permission::ComAndroidLauncherPermissionPreloadWorkspace => "com.android.launcher.permission.PRELOAD_WORKSPACE", - Permission::ComAndroidLauncherPermissionReadSettings => "com.android.launcher.permission.READ_SETTINGS", - Permission::ComAndroidLauncherPermissionUninstallShortcut => "com.android.launcher.permission.UNINSTALL_SHORTCUT", - Permission::ComAndroidLauncherPermissionWriteSettings => "com.android.launcher.permission.WRITE_SETTINGS", + Permission::AndroidPermissionWriteSocialStream => { + "android.permission.WRITE_SOCIAL_STREAM" + } + Permission::AndroidPermissionWriteSyncSettings => { + "android.permission.WRITE_SYNC_SETTINGS" + } + Permission::AndroidPermissionWriteUserDictionary => { + "android.permission.WRITE_USER_DICTIONARY" + } + Permission::ComAndroidAlarmPermissionSetAlarm => { + "com.android.alarm.permission.SET_ALARM" + } + Permission::ComAndroidBrowserPermissionReadHistoryBookmarks => { + "com.android.browser.permission.READ_HISTORY_BOOKMARKS" + } + Permission::ComAndroidBrowserPermissionWriteHistoryBookmarks => { + "com.android.browser.permission.WRITE_HISTORY_BOOKMARKS" + } + Permission::ComAndroidEmailPermissionReadAttachment => { + "com.android.email.permission.READ_ATTACHMENT" + } + Permission::ComAndroidLauncherPermissionInstallShortcut => { + "com.android.launcher.permission.INSTALL_SHORTCUT" + } + Permission::ComAndroidLauncherPermissionPreloadWorkspace => { + "com.android.launcher.permission.PRELOAD_WORKSPACE" + } + Permission::ComAndroidLauncherPermissionReadSettings => { + "com.android.launcher.permission.READ_SETTINGS" + } + Permission::ComAndroidLauncherPermissionUninstallShortcut => { + "com.android.launcher.permission.UNINSTALL_SHORTCUT" + } + Permission::ComAndroidLauncherPermissionWriteSettings => { + "com.android.launcher.permission.WRITE_SETTINGS" + } Permission::ComAndroidVendingCheckLicense => "com.android.vending.CHECK_LICENSE", - Permission::ComAndroidVoicemailPermissionAddVoicemail => "com.android.voicemail.permission.ADD_VOICEMAIL", - Permission::ComAndroidVoicemailPermissionReadVoicemail => "com.android.voicemail.permission.READ_VOICEMAIL", - Permission::ComAndroidVoicemailPermissionReadWriteAllVoicemail => "com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL", - Permission::ComAndroidVoicemailPermissionWriteVoicemail => "com.android.voicemail.permission.WRITE_VOICEMAIL", - Permission::ComGoogleAndroidC2dmPermissionReceive => "com.google.android.c2dm.permission.RECEIVE", - Permission::ComGoogleAndroidC2dmPermissionSend => "com.google.android.c2dm.permission.SEND", - Permission::ComGoogleAndroidGmsPermissionActivityRecognition => "com.google.android.gms.permission.ACTIVITY_RECOGNITION", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuth => "com.google.android.googleapps.permission.GOOGLE_AUTH", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAllServices => "com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthOtherServices => "com.google.android.googleapps.permission.GOOGLE_AUTH.OTHER_SERVICES", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthYoutubeuser => "com.google.android.googleapps.permission.GOOGLE_AUTH.YouTubeUser", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAdsense => "com.google.android.googleapps.permission.GOOGLE_AUTH.adsense", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAdwords => "com.google.android.googleapps.permission.GOOGLE_AUTH.adwords", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAh => "com.google.android.googleapps.permission.GOOGLE_AUTH.ah", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAndroid => "com.google.android.googleapps.permission.GOOGLE_AUTH.android", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAndroidsecure => "com.google.android.googleapps.permission.GOOGLE_AUTH.androidsecure", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthBlogger => "com.google.android.googleapps.permission.GOOGLE_AUTH.blogger", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthCl => "com.google.android.googleapps.permission.GOOGLE_AUTH.cl", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthCp => "com.google.android.googleapps.permission.GOOGLE_AUTH.cp", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthDodgeball => "com.google.android.googleapps.permission.GOOGLE_AUTH.dodgeball", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthDoraemon => "com.google.android.googleapps.permission.GOOGLE_AUTH.doraemon", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthFinance => "com.google.android.googleapps.permission.GOOGLE_AUTH.finance", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGbase => "com.google.android.googleapps.permission.GOOGLE_AUTH.gbase", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGeowiki => "com.google.android.googleapps.permission.GOOGLE_AUTH.geowiki", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGoannaMobile => "com.google.android.googleapps.permission.GOOGLE_AUTH.goanna_mobile", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGrandcentral => "com.google.android.googleapps.permission.GOOGLE_AUTH.grandcentral", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGroups2 => "com.google.android.googleapps.permission.GOOGLE_AUTH.groups2", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthHealth => "com.google.android.googleapps.permission.GOOGLE_AUTH.health", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthIg => "com.google.android.googleapps.permission.GOOGLE_AUTH.ig", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthJotspot => "com.google.android.googleapps.permission.GOOGLE_AUTH.jotspot", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthKnol => "com.google.android.googleapps.permission.GOOGLE_AUTH.knol", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthLh2 => "com.google.android.googleapps.permission.GOOGLE_AUTH.lh2", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthLocal => "com.google.android.googleapps.permission.GOOGLE_AUTH.local", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthMail => "com.google.android.googleapps.permission.GOOGLE_AUTH.mail", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthMobile => "com.google.android.googleapps.permission.GOOGLE_AUTH.mobile", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthNews => "com.google.android.googleapps.permission.GOOGLE_AUTH.news", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthNotebook => "com.google.android.googleapps.permission.GOOGLE_AUTH.notebook", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthOrkut => "com.google.android.googleapps.permission.GOOGLE_AUTH.orkut", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthPanoramio => "com.google.android.googleapps.permission.GOOGLE_AUTH.panoramio", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthPrint => "com.google.android.googleapps.permission.GOOGLE_AUTH.print", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthReader => "com.google.android.googleapps.permission.GOOGLE_AUTH.reader", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierra => "com.google.android.googleapps.permission.GOOGLE_AUTH.sierra", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierraqa => "com.google.android.googleapps.permission.GOOGLE_AUTH.sierraqa", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierrasandbox => "com.google.android.googleapps.permission.GOOGLE_AUTH.sierrasandbox", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSitemaps => "com.google.android.googleapps.permission.GOOGLE_AUTH.sitemaps", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSpeech => "com.google.android.googleapps.permission.GOOGLE_AUTH.speech", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSpeechpersonalization => "com.google.android.googleapps.permission.GOOGLE_AUTH.speechpersonalization", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthTalk => "com.google.android.googleapps.permission.GOOGLE_AUTH.talk", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWifi => "com.google.android.googleapps.permission.GOOGLE_AUTH.wifi", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWise => "com.google.android.googleapps.permission.GOOGLE_AUTH.wise", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWritely => "com.google.android.googleapps.permission.GOOGLE_AUTH.writely", - Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthYoutube => "com.google.android.googleapps.permission.GOOGLE_AUTH.youtube", - Permission::ComGoogleAndroidGtalkservicePermissionGtalkService => "com.google.android.gtalkservice.permission.GTALK_SERVICE", - Permission::ComGoogleAndroidGtalkservicePermissionSendHeartbeat => "com.google.android.gtalkservice.permission.SEND_HEARTBEAT", - Permission::ComGoogleAndroidPermissionBroadcastDataMessage => "com.google.android.permission.BROADCAST_DATA_MESSAGE", - Permission::ComGoogleAndroidProvidersGsfPermissionReadGservices => "com.google.android.providers.gsf.permission.READ_GSERVICES", - Permission::ComGoogleAndroidProvidersTalkPermissionReadOnly => "com.google.android.providers.talk.permission.READ_ONLY", - Permission::ComGoogleAndroidProvidersTalkPermissionWriteOnly => "com.google.android.providers.talk.permission.WRITE_ONLY", - Permission::ComGoogleAndroidXmppPermissionBroadcast => "com.google.android.xmpp.permission.BROADCAST", - Permission::ComGoogleAndroidXmppPermissionSendReceive => "com.google.android.xmpp.permission.SEND_RECEIVE", - Permission::ComGoogleAndroidXmppPermissionUseXmppEndpoint => "com.google.android.xmpp.permission.USE_XMPP_ENDPOINT", - Permission::ComGoogleAndroidXmppPermissionXmppEndpointBroadcast => "com.google.android.xmpp.permission.XMPP_ENDPOINT_BROADCAST", + Permission::ComAndroidVoicemailPermissionAddVoicemail => { + "com.android.voicemail.permission.ADD_VOICEMAIL" + } + Permission::ComAndroidVoicemailPermissionReadVoicemail => { + "com.android.voicemail.permission.READ_VOICEMAIL" + } + Permission::ComAndroidVoicemailPermissionReadWriteAllVoicemail => { + "com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL" + } + Permission::ComAndroidVoicemailPermissionWriteVoicemail => { + "com.android.voicemail.permission.WRITE_VOICEMAIL" + } + Permission::ComGoogleAndroidC2dmPermissionReceive => { + "com.google.android.c2dm.permission.RECEIVE" + } + Permission::ComGoogleAndroidC2dmPermissionSend => { + "com.google.android.c2dm.permission.SEND" + } + Permission::ComGoogleAndroidGmsPermissionActivityRecognition => { + "com.google.android.gms.permission.ACTIVITY_RECOGNITION" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuth => { + "com.google.android.googleapps.permission.GOOGLE_AUTH" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAllServices => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthOtherServices => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.OTHER_SERVICES" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthYoutubeuser => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.YouTubeUser" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAdsense => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.adsense" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAdwords => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.adwords" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAh => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.ah" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAndroid => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.android" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAndroidsecure => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.androidsecure" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthBlogger => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.blogger" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthCl => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.cl" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthCp => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.cp" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthDodgeball => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.dodgeball" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthDoraemon => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.doraemon" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthFinance => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.finance" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGbase => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.gbase" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGeowiki => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.geowiki" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGoannaMobile => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.goanna_mobile" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGrandcentral => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.grandcentral" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGroups2 => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.groups2" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthHealth => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.health" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthIg => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.ig" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthJotspot => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.jotspot" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthKnol => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.knol" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthLh2 => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.lh2" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthLocal => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.local" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthMail => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.mail" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthMobile => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.mobile" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthNews => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.news" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthNotebook => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.notebook" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthOrkut => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.orkut" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthPanoramio => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.panoramio" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthPrint => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.print" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthReader => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.reader" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierra => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.sierra" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierraqa => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.sierraqa" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierrasandbox => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.sierrasandbox" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSitemaps => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.sitemaps" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSpeech => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.speech" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSpeechpersonalization => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.speechpersonalization" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthTalk => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.talk" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWifi => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.wifi" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWise => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.wise" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWritely => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.writely" + } + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthYoutube => { + "com.google.android.googleapps.permission.GOOGLE_AUTH.youtube" + } + Permission::ComGoogleAndroidGtalkservicePermissionGtalkService => { + "com.google.android.gtalkservice.permission.GTALK_SERVICE" + } + Permission::ComGoogleAndroidGtalkservicePermissionSendHeartbeat => { + "com.google.android.gtalkservice.permission.SEND_HEARTBEAT" + } + Permission::ComGoogleAndroidPermissionBroadcastDataMessage => { + "com.google.android.permission.BROADCAST_DATA_MESSAGE" + } + Permission::ComGoogleAndroidProvidersGsfPermissionReadGservices => { + "com.google.android.providers.gsf.permission.READ_GSERVICES" + } + Permission::ComGoogleAndroidProvidersTalkPermissionReadOnly => { + "com.google.android.providers.talk.permission.READ_ONLY" + } + Permission::ComGoogleAndroidProvidersTalkPermissionWriteOnly => { + "com.google.android.providers.talk.permission.WRITE_ONLY" + } + Permission::ComGoogleAndroidXmppPermissionBroadcast => { + "com.google.android.xmpp.permission.BROADCAST" + } + Permission::ComGoogleAndroidXmppPermissionSendReceive => { + "com.google.android.xmpp.permission.SEND_RECEIVE" + } + Permission::ComGoogleAndroidXmppPermissionUseXmppEndpoint => { + "com.google.android.xmpp.permission.USE_XMPP_ENDPOINT" + } + Permission::ComGoogleAndroidXmppPermissionXmppEndpointBroadcast => { + "com.google.android.xmpp.permission.XMPP_ENDPOINT_BROADCAST" + } } } } @@ -3455,7 +3839,9 @@ impl FromStr for Permission { "android.permission.REMOVE_TASKS" => Ok(Permission::AndroidPermissionRemoveTasks), "android.permission.REORDER_TASKS" => Ok(Permission::AndroidPermissionReorderTasks), "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" => { - Ok(Permission::AndroidPermissionRequestIgnoreBatteryOptimizations) + Ok( + Permission::AndroidPermissionRequestIgnoreBatteryOptimizations, + ) } "android.permission.REQUEST_INSTALL_PACKAGES" => { Ok(Permission::AndroidPermissionRequestInstallPackages) @@ -3586,7 +3972,9 @@ impl FromStr for Permission { Ok(Permission::ComAndroidVoicemailPermissionReadVoicemail) } "com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL" => { - Ok(Permission::ComAndroidVoicemailPermissionReadWriteAllVoicemail) + Ok( + Permission::ComAndroidVoicemailPermissionReadWriteAllVoicemail, + ) } "com.android.voicemail.permission.WRITE_VOICEMAIL" => { Ok(Permission::ComAndroidVoicemailPermissionWriteVoicemail) @@ -3604,31 +3992,47 @@ impl FromStr for Permission { Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuth) } "com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAllServices) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAllServices, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.OTHER_SERVICES" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthOtherServices) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthOtherServices, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.YouTubeUser" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthYoutubeuser) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthYoutubeuser, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.adsense" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAdsense) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAdsense, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.adwords" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAdwords) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAdwords, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.ah" => { Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAh) } "com.google.android.googleapps.permission.GOOGLE_AUTH.android" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAndroid) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAndroid, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.androidsecure" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAndroidsecure) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthAndroidsecure, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.blogger" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthBlogger) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthBlogger, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.cl" => { Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthCl) @@ -3637,115 +4041,185 @@ impl FromStr for Permission { Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthCp) } "com.google.android.googleapps.permission.GOOGLE_AUTH.dodgeball" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthDodgeball) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthDodgeball, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.doraemon" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthDoraemon) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthDoraemon, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.finance" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthFinance) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthFinance, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.gbase" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGbase) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGbase, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.geowiki" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGeowiki) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGeowiki, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.goanna_mobile" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGoannaMobile) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGoannaMobile, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.grandcentral" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGrandcentral) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGrandcentral, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.groups2" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGroups2) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthGroups2, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.health" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthHealth) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthHealth, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.ig" => { Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthIg) } "com.google.android.googleapps.permission.GOOGLE_AUTH.jotspot" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthJotspot) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthJotspot, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.knol" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthKnol) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthKnol, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.lh2" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthLh2) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthLh2, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.local" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthLocal) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthLocal, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.mail" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthMail) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthMail, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.mobile" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthMobile) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthMobile, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.news" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthNews) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthNews, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.notebook" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthNotebook) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthNotebook, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.orkut" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthOrkut) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthOrkut, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.panoramio" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthPanoramio) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthPanoramio, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.print" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthPrint) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthPrint, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.reader" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthReader) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthReader, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.sierra" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierra) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierra, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.sierraqa" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierraqa) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierraqa, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.sierrasandbox" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierrasandbox) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSierrasandbox, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.sitemaps" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSitemaps) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSitemaps, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.speech" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSpeech) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSpeech, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.speechpersonalization" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSpeechpersonalization) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthSpeechpersonalization, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.talk" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthTalk) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthTalk, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.wifi" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWifi) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWifi, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.wise" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWise) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWise, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.writely" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWritely) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthWritely, + ) } "com.google.android.googleapps.permission.GOOGLE_AUTH.youtube" => { - Ok(Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthYoutube) + Ok( + Permission::ComGoogleAndroidGoogleappsPermissionGoogleAuthYoutube, + ) } "com.google.android.gtalkservice.permission.GTALK_SERVICE" => { - Ok(Permission::ComGoogleAndroidGtalkservicePermissionGtalkService) + Ok( + Permission::ComGoogleAndroidGtalkservicePermissionGtalkService, + ) } "com.google.android.gtalkservice.permission.SEND_HEARTBEAT" => { - Ok(Permission::ComGoogleAndroidGtalkservicePermissionSendHeartbeat) + Ok( + Permission::ComGoogleAndroidGtalkservicePermissionSendHeartbeat, + ) } "com.google.android.permission.BROADCAST_DATA_MESSAGE" => { Ok(Permission::ComGoogleAndroidPermissionBroadcastDataMessage) } "com.google.android.providers.gsf.permission.READ_GSERVICES" => { - Ok(Permission::ComGoogleAndroidProvidersGsfPermissionReadGservices) + Ok( + Permission::ComGoogleAndroidProvidersGsfPermissionReadGservices, + ) } "com.google.android.providers.talk.permission.READ_ONLY" => { Ok(Permission::ComGoogleAndroidProvidersTalkPermissionReadOnly) @@ -3763,7 +4237,9 @@ impl FromStr for Permission { Ok(Permission::ComGoogleAndroidXmppPermissionUseXmppEndpoint) } "com.google.android.xmpp.permission.XMPP_ENDPOINT_BROADCAST" => { - Ok(Permission::ComGoogleAndroidXmppPermissionXmppEndpointBroadcast) + Ok( + Permission::ComGoogleAndroidXmppPermissionXmppEndpointBroadcast, + ) } _ => Err(ErrorKind::Parse.into()), } diff --git a/src/static_analysis/mod.rs b/src/static_analysis/mod.rs index 71dd68910..5549bb4e1 100644 --- a/src/static_analysis/mod.rs +++ b/src/static_analysis/mod.rs @@ -23,9 +23,11 @@ use error::*; /// * Benchmarking support. pub fn static_analysis>(config: &Config, package: S, results: &mut Results) { if config.is_verbose() { - println!("It's time to analyze the application. First, a static analysis will be \ + println!( + "It's time to analyze the application. First, a static analysis will be \ performed, starting with the AndroidManifest.xml file and then going through \ - the actual code. Let's start!"); + the actual code. Let's start!" + ); } // Run analysis for manifest file. @@ -34,8 +36,10 @@ pub fn static_analysis>(config: &Config, package: S, results: &mut if cfg!(feature = "certificate") { // Run analysis for cerificate file. if let Err(e) = certificate_analysis(config, package.as_ref(), results) { - print_warning(format!("There was an error analysing the certificate: {}", - e.description())) + print_warning(format!( + "There was an error analysing the certificate: {}", + e.description() + )) } } diff --git a/src/utils.rs b/src/utils.rs index 026fc3947..420585b56 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -33,8 +33,10 @@ pub fn print_warning>(warning: S) { if log_enabled!(Debug) { sleep(Duration::from_millis(200)); } else { - println!("If you need more information, try to run the program again with the {} flag.", - "-v".bold()) + println!( + "If you need more information, try to run the program again with the {} flag.", + "-v".bold() + ) } } } @@ -43,9 +45,11 @@ pub fn print_warning>(warning: S) { #[allow(print_stdout)] pub fn print_vulnerability>(text: S, criticality: Criticality) { if cfg!(not(test)) && log_enabled!(Debug) { - let message = format!("Possible {} criticality vulnerability found!: {}", - criticality, - text.as_ref()); + let message = format!( + "Possible {} criticality vulnerability found!: {}", + criticality, + text.as_ref() + ); let formatted_message = match criticality { Criticality::Low => message.cyan(), @@ -87,29 +91,30 @@ pub fn get_code>(code: S, s_line: usize, e_line: usize) -> String } /// Gets a string from the strings XML file. -pub fn get_string, P: AsRef>(label: L, - config: &Config, - package: P) - -> Result { +pub fn get_string, P: AsRef>( + label: L, + config: &Config, + package: P, +) -> Result { let mut file = fs::File::open({ - let path = config - .get_dist_folder() - .join(package.as_ref()) - .join("res") - .join("values-en") - .join("strings.xml"); - - if path.exists() { - path - } else { - config - .get_dist_folder() - .join(package.as_ref()) - .join("res") - .join("values") - .join("strings.xml") - } - })?; + let path = config + .get_dist_folder() + .join(package.as_ref()) + .join("res") + .join("values-en") + .join("strings.xml"); + + if path.exists() { + path + } else { + config + .get_dist_folder() + .join(package.as_ref()) + .join("res") + .join("values") + .join("strings.xml") + } + })?; let mut code = String::new(); let _ = file.read_to_string(&mut code)?; @@ -158,11 +163,13 @@ impl Benchmark { impl fmt::Display for Benchmark { fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> { - write!(f, - "{}: {}.{}s", - self.label, - self.duration.as_secs(), - self.duration.subsec_nanos()) + write!( + f, + "{}: {}.{}s", + self.label, + self.duration.as_secs(), + self.duration.subsec_nanos() + ) } } @@ -186,24 +193,29 @@ mod test { mattis, tortor neque adipiscing\nVestibulum ante ipsum primis in faucibus \ orci luctus et ultrices"; - assert_eq!(get_code(code, 1, 1), - "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\ + assert_eq!( + get_code(code, 1, 1), + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\ Curabitur tortor. Pellentesque nibh. Aenean quam.\n\ Sed lacinia, urna non tincidunt mattis, tortor neque\n\ Praesent blandit dolor. Sed non quam. In vel mi\n\ Sed aliquet risus a tortor. Integer id quam. Morbi mi.\n\ - Nullam mauris orci, aliquet et, iaculis et, viverra vitae, ligula.\n"); + Nullam mauris orci, aliquet et, iaculis et, viverra vitae, ligula.\n" + ); - assert_eq!(get_code(code, 13, 13), - "Vestibulum tincidunt malesuada tellus. Ut ultrices ultrices enim.\n\ + assert_eq!( + get_code(code, 13, 13), + "Vestibulum tincidunt malesuada tellus. Ut ultrices ultrices enim.\n\ Aenean laoreet. Vestibulum nisi lectus, commodo ac, facilisis\n\ Integer nec odio. Praesent libero. Sed cursus ante dapibus diam.\n\ Pellentesque nibh. Aenean quam. In scelerisque sem at dolor.\n\ Sed lacinia, urna non tincidunt mattis, tortor neque adipiscing\n\ - Vestibulum ante ipsum primis in faucibus orci luctus et ultrices\n"); + Vestibulum ante ipsum primis in faucibus orci luctus et ultrices\n" + ); - assert_eq!(get_code(code, 7, 7), - "Praesent blandit dolor. Sed non quam. In vel mi\n\ + assert_eq!( + get_code(code, 7, 7), + "Praesent blandit dolor. Sed non quam. In vel mi\n\ Sed aliquet risus a tortor. Integer id quam. Morbi mi.\n\ Nullam mauris orci, aliquet et, iaculis et, viverra vitae, ligula.\n\ Praesent mauris. Fusce nec tellus sed ugue semper porta. Mauris massa.\n\ @@ -211,10 +223,12 @@ mod test { Vestibulum sapien. Proin quam. Etiam ultrices. Suspendisse in\n\ Vestibulum tincidunt malesuada tellus. Ut ultrices ultrices enim.\n\ Aenean laoreet. Vestibulum nisi lectus, commodo ac, facilisis\n\ - Integer nec odio. Praesent libero. Sed cursus ante dapibus diam.\n"); + Integer nec odio. Praesent libero. Sed cursus ante dapibus diam.\n" + ); - assert_eq!(get_code(code, 7, 9), - "Praesent blandit dolor. Sed non quam. In vel mi\n\ + assert_eq!( + get_code(code, 7, 9), + "Praesent blandit dolor. Sed non quam. In vel mi\n\ Sed aliquet risus a tortor. Integer id quam. Morbi mi.\n\ Nullam mauris orci, aliquet et, iaculis et, viverra vitae, ligula.\n\ Praesent mauris. Fusce nec tellus sed ugue semper porta. Mauris massa.\n\ @@ -224,6 +238,7 @@ mod test { Aenean laoreet. Vestibulum nisi lectus, commodo ac, facilisis\n\ Integer nec odio. Praesent libero. Sed cursus ante dapibus diam.\n\ Pellentesque nibh. Aenean quam. In scelerisque sem at dolor.\n\ - Sed lacinia, urna non tincidunt mattis, tortor neque adipiscing\n"); + Sed lacinia, urna non tincidunt mattis, tortor neque adipiscing\n" + ); } }