#[cfg(feature = "apk")]
use std::str::from_utf8;
#[cfg(feature = "crx")]
use crate::types::ExtensionManifestSummary;
use crate::types::PackageJsonSummary;
#[cfg(feature = "crx")]
pub(crate) fn summarize_extension_manifest(value: serde_json::Value) -> ExtensionManifestSummary {
let permissions = value
.get("permissions")
.and_then(|value| value.as_array())
.into_iter()
.flatten()
.filter_map(|value| value.as_str().map(ToOwned::to_owned))
.collect::<Vec<_>>();
let host_permissions = value
.get("host_permissions")
.and_then(|value| value.as_array())
.into_iter()
.flatten()
.filter_map(|value| value.as_str().map(ToOwned::to_owned))
.collect::<Vec<_>>();
let background_scripts = value
.get("background")
.map(background_scripts_from_manifest)
.unwrap_or_default();
let content_scripts = value
.get("content_scripts")
.and_then(|value| value.as_array())
.into_iter()
.flatten()
.flat_map(|group| {
group
.get("js")
.and_then(|value| value.as_array())
.into_iter()
.flatten()
.filter_map(|value| value.as_str().map(ToOwned::to_owned))
})
.collect::<Vec<_>>();
ExtensionManifestSummary {
name: value
.get("name")
.and_then(|value| value.as_str())
.map(ToOwned::to_owned),
version: value
.get("version")
.and_then(|value| value.as_str())
.map(ToOwned::to_owned),
manifest_version: value
.get("manifest_version")
.and_then(|value| value.as_u64()),
permissions,
host_permissions,
background_scripts,
content_scripts,
}
}
#[cfg(feature = "crx")]
fn background_scripts_from_manifest(value: &serde_json::Value) -> Vec<String> {
let mut scripts = value
.get("scripts")
.and_then(|value| value.as_array())
.into_iter()
.flatten()
.filter_map(|value| value.as_str().map(ToOwned::to_owned))
.collect::<Vec<_>>();
if let Some(worker) = value
.get("service_worker")
.and_then(|value| value.as_str())
.map(ToOwned::to_owned)
{
scripts.push(worker);
}
scripts
}
pub(crate) fn summarize_package_json(value: serde_json::Value) -> PackageJsonSummary {
let dependencies = value
.get("dependencies")
.and_then(|value| value.as_object())
.map(|deps| deps.keys().cloned().collect::<Vec<_>>())
.unwrap_or_default();
PackageJsonSummary {
name: value
.get("name")
.and_then(|value| value.as_str())
.map(ToOwned::to_owned),
version: value
.get("version")
.and_then(|value| value.as_str())
.map(ToOwned::to_owned),
description: value
.get("description")
.and_then(|value| value.as_str())
.map(ToOwned::to_owned),
main: value
.get("main")
.and_then(|value| value.as_str())
.map(ToOwned::to_owned),
module: value
.get("module")
.and_then(|value| value.as_str())
.map(ToOwned::to_owned),
browser: value
.get("browser")
.and_then(|value| value.as_str())
.map(ToOwned::to_owned),
dependencies,
}
}
#[cfg(feature = "apk")]
pub(crate) fn parse_android_manifest(bytes: &[u8]) -> Option<crate::AndroidManifest> {
let xml = from_utf8(bytes).ok()?;
let package = extract_xml_attr(xml, "package")?;
Some(crate::AndroidManifest {
package,
version_name: extract_xml_attr(xml, "versionName"),
version_code: extract_xml_attr(xml, "versionCode"),
min_sdk: extract_block_attr(xml, "uses-sdk", "android:minSdkVersion")
.or_else(|| extract_block_attr(xml, "uses-sdk", "android:targetSdkVersion")),
})
}
#[cfg(feature = "apk")]
fn extract_xml_attr(xml: &str, attr: &str) -> Option<String> {
let token = format!(" {}=\"", attr);
let start = xml.find(&token)? + token.len();
let rest = &xml[start..];
let end = rest.find('"')?;
Some(rest[..end].to_string())
}
#[cfg(feature = "apk")]
fn extract_block_attr(xml: &str, block: &str, attr: &str) -> Option<String> {
let block_start = xml.find(&format!("<{}", block))?;
let after_block = &xml[block_start..];
let token = format!(" {}=\"", attr);
let start = after_block.find(&token)? + block_start + token.len();
let value_tail = &xml[start..];
let end = value_tail.find('"')?;
Some(value_tail[..end].to_string())
}
#[cfg(feature = "ipa")]
pub(crate) fn parse_info_plist(xml: &str) -> Option<crate::IpaInfoPlist> {
Some(crate::IpaInfoPlist {
bundle_identifier: parse_plist_key(xml, "CFBundleIdentifier"),
bundle_version: parse_plist_key(xml, "CFBundleShortVersionString"),
executable: parse_plist_key(xml, "CFBundleExecutable"),
})
}
#[cfg(feature = "ipa")]
fn parse_plist_key(xml: &str, key: &str) -> Option<String> {
let marker = format!("<key>{}</key>", key);
let key_pos = xml.find(&marker)?;
let start =
xml[key_pos + marker.len()..].find("<string>")? + key_pos + marker.len() + "<string>".len();
let value_tail = &xml[start..];
let end = value_tail.find("</string>")?;
Some(value_tail[..end].trim().to_string())
}