Implement Android Credential Manager and Autofill providers (#109)
This commit is contained in:
1 parent
316a875d63
commit
0c8366d34f
48 files changed
+4138
-78
No files matched your search
@@ -61,8 +61,20 @@ fn manifest_text(source: &str) -> Result<(), Box<dyn Error>> {
|
||||
"de.rfc1437.ironstorage.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
|
||||
"android.permission.INTERNET",
|
||||
"android.permission.CAMERA",
|
||||
"android.permission.QUERY_ALL_PACKAGES",
|
||||
];
|
||||
for node in root.children().filter(|node| node.is_element()) {
|
||||
if node.attribute((ANDROID, "name")) == Some("android.permission.QUERY_ALL_PACKAGES")
|
||||
&& !application.children().any(|service| {
|
||||
service.has_tag_name("service")
|
||||
&& service.attribute((ANDROID, "name"))
|
||||
== Some("de.rfc1437.ironstorage.providers.IronAutofillService")
|
||||
&& service.attribute((ANDROID, "permission"))
|
||||
== Some("android.permission.BIND_AUTOFILL_SERVICE")
|
||||
})
|
||||
{
|
||||
return Err("package visibility requires the reviewed autofill provider".into());
|
||||
}
|
||||
if node.tag_name().name().starts_with("uses-permission")
|
||||
&& !allowed.contains(&node.attribute((ANDROID, "name")).unwrap_or(""))
|
||||
{
|
||||
@@ -109,6 +121,46 @@ fn manifest_text(source: &str) -> Result<(), Box<dyn Error>> {
|
||||
{
|
||||
return Err("unreviewed launcher intent".into());
|
||||
}
|
||||
} else if node.has_tag_name("service")
|
||||
&& matches!(
|
||||
node.attribute((ANDROID, "name")),
|
||||
Some(
|
||||
"de.rfc1437.ironstorage.providers.IronAutofillService"
|
||||
| "de.rfc1437.ironstorage.providers.IronCredentialProviderService"
|
||||
)
|
||||
)
|
||||
{
|
||||
let autofill = node.attribute((ANDROID, "name"))
|
||||
== Some("de.rfc1437.ironstorage.providers.IronAutofillService");
|
||||
let (permission, action, metadata) = if autofill {
|
||||
(
|
||||
"android.permission.BIND_AUTOFILL_SERVICE",
|
||||
"android.service.autofill.AutofillService",
|
||||
"android.autofill",
|
||||
)
|
||||
} else {
|
||||
(
|
||||
"android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE",
|
||||
"android.service.credentials.CredentialProviderService",
|
||||
"android.credentials.provider",
|
||||
)
|
||||
};
|
||||
let actions: Vec<_> = node
|
||||
.descendants()
|
||||
.filter(|node| node.has_tag_name("action") || node.has_tag_name("category"))
|
||||
.filter_map(|node| node.attribute((ANDROID, "name")))
|
||||
.collect();
|
||||
if node.attribute((ANDROID, "exported")) != Some("true")
|
||||
|| node.attribute((ANDROID, "permission")) != Some(permission)
|
||||
|| actions != [action]
|
||||
|| !node.children().any(|node| {
|
||||
node.has_tag_name("meta-data")
|
||||
&& node.attribute((ANDROID, "name")) == Some(metadata)
|
||||
&& node.attribute((ANDROID, "resource")).is_some()
|
||||
})
|
||||
{
|
||||
return Err("credential services must retain their system-only binding permissions and reviewed actions".into());
|
||||
}
|
||||
} else if node.has_tag_name("receiver")
|
||||
&& node.attribute((ANDROID, "name"))
|
||||
== Some("androidx.profileinstaller.ProfileInstallReceiver")
|
||||
@@ -442,6 +494,10 @@ mod tests {
|
||||
r#"<manifest xmlns:android="{ANDROID}" package="de.rfc1437.ironstorage"><application android:allowBackup="false" android:usesCleartextTraffic="false" android:fullBackupContent="@xml/backup_rules" android:dataExtractionRules="@xml/data_extraction_rules"><activity android:name="de.rfc1437.ironstorage.MainActivity" android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity><provider android:name="fixture" android:exported="false"/></application></manifest>"#
|
||||
);
|
||||
assert!(manifest_text(&source).is_ok());
|
||||
assert!(manifest_text(&source.replace(
|
||||
"<application",
|
||||
"<uses-permission android:name=\"android.permission.QUERY_ALL_PACKAGES\"/><application",
|
||||
)).is_err());
|
||||
assert!(
|
||||
manifest_text(&source.replace(
|
||||
"<application",
|
||||
@@ -486,6 +542,45 @@ mod tests {
|
||||
assert!(manifest_text(&launcher.replace("INSTALL_PROFILE", "UNREVIEWED")).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_services_require_exact_system_binding_permissions() {
|
||||
let source = format!(
|
||||
r#"<manifest xmlns:android="{ANDROID}" package="de.rfc1437.ironstorage"><application android:allowBackup="false" android:usesCleartextTraffic="false" android:fullBackupContent="@xml/backup_rules" android:dataExtractionRules="@xml/data_extraction_rules"><activity android:name="de.rfc1437.ironstorage.MainActivity" android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity><service android:name="de.rfc1437.ironstorage.providers.IronAutofillService" android:exported="true" android:permission="android.permission.BIND_AUTOFILL_SERVICE"><intent-filter><action android:name="android.service.autofill.AutofillService"/></intent-filter><meta-data android:name="android.autofill" android:resource="@xml/autofill_service"/></service></application></manifest>"#
|
||||
);
|
||||
assert!(manifest_text(&source).is_ok());
|
||||
let visible = source.replace(
|
||||
"<application",
|
||||
"<uses-permission android:name=\"android.permission.QUERY_ALL_PACKAGES\"/><application",
|
||||
);
|
||||
assert!(manifest_text(&visible).is_ok());
|
||||
assert!(
|
||||
manifest_text(&visible.replace("IronAutofillService", "UnreviewedService")).is_err()
|
||||
);
|
||||
assert!(
|
||||
manifest_text(&source.replace(
|
||||
"android.permission.BIND_AUTOFILL_SERVICE",
|
||||
"android.permission.INTERNET"
|
||||
))
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
manifest_text(&source.replace(
|
||||
"android.service.autofill.AutofillService",
|
||||
"unreviewed.action"
|
||||
))
|
||||
.is_err()
|
||||
);
|
||||
let credentials = source
|
||||
.replace("IronAutofillService", "IronCredentialProviderService")
|
||||
.replace("BIND_AUTOFILL_SERVICE", "BIND_CREDENTIAL_PROVIDER_SERVICE")
|
||||
.replace(
|
||||
"android.service.autofill.AutofillService",
|
||||
"android.service.credentials.CredentialProviderService",
|
||||
)
|
||||
.replace("android.autofill", "android.credentials.provider");
|
||||
assert!(manifest_text(&credentials).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backup_requires_both_sections_all_domains_and_no_include() {
|
||||
let exclusions = DOMAINS
|
||||
|
||||
@@ -143,6 +143,7 @@ fn check(root: &Path) -> Result<(), Box<dyn Error>> {
|
||||
for (module, allowed) in [
|
||||
("app", &["features", "core", "platform"][..]),
|
||||
("performance", &["app"][..]),
|
||||
("provider-client", &[][..]),
|
||||
("features", &["core"][..]),
|
||||
("core", &["rust-bridge"][..]),
|
||||
("platform", &["core"][..]),
|
||||
|
||||
Reference in new issue
Block a user