Merge pull request #1427 from Universal-Debloater-Alliance/refactor/pack_id-android

refactor(core): allow "android" `PackageId`
This commit is contained in:
Anonymoussaurus 2026-07-06 10:47:34 +02:00 committed by GitHub
commit 258a50cdfe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 20 additions and 22 deletions

View file

@ -38,7 +38,7 @@
//! For comprehensive info about ADB,
//! [see this](https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/master/docs/)
use serde::{Deserialize, Serialize};
use std::rc::Rc;
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;
@ -275,14 +275,17 @@ pub const fn is_pkg_component(s: &[u8]) -> bool {
/// String with the invariant of being a valid package-name.
/// See [`PackageId::new`] for validation details.
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Hash)]
pub struct PackageId(Box<str>);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PackageId(Rc<str>);
impl PackageId {
/// Creates a package-ID if it's valid according to:
/// Creates a package-ID if it's `"android"` or valid according to:
/// - <https://developer.android.com/guide/topics/manifest/manifest-element.html#package>
/// - <https://developer.android.com/build/configure-app-module#set-application-id>
#[must_use]
pub fn new(p_id: Box<str>) -> Option<Self> {
pub fn new(p_id: &str) -> Option<Self> {
if p_id == "android" {
return Some(Self(p_id.into()));
}
let mut components = p_id.split('.');
for _ in 0..2 {
if !components
@ -293,7 +296,7 @@ impl PackageId {
}
}
if components.all(|comp| is_pkg_component(comp.as_bytes())) {
Some(Self(p_id))
Some(Self(p_id.into()))
} else {
None
}
@ -337,11 +340,11 @@ pub const PM_CLEAR_PACK: &str = "pm clear";
#[must_use]
pub struct PmCommand(ShellCommand);
impl PmCommand {
/// `list packages -s` sub-command, [`PACK_PREFIX`] stripped.
/// `list packages -s` sub-command, [`PACK_PREFIX`] stripped from each element.
///
/// `Ok` variant:
/// - isn't guaranteed to contain valid pack-IDs,
/// as "android" can be printed but it's invalid
/// - isn't 100% guaranteed to contain valid pack-IDs
/// but you can assume it does (except in `unsafe {}` blocks)
/// - isn't sorted
/// - duplicates never _seem_ to happen, but don't assume uniqueness
pub fn list_packages_sys(
@ -366,7 +369,7 @@ impl PmCommand {
.map(|p_ln| {
debug_assert!(p_ln.starts_with(PACK_PREFIX));
let p = &p_ln[PACK_PREFIX.len()..];
debug_assert!(PackageId::new(p.into()).is_some() || p == "android");
debug_assert!(PackageId::new(p).is_some());
String::from(p)
})
.collect()
@ -473,7 +476,7 @@ mod tests {
"the.🎂.is.a.lie",
"EXCLAMATION!!!!",
] {
assert_eq!(PackageId::new(p_id.into()), None);
assert_eq!(PackageId::new(p_id), None);
}
}
@ -490,7 +493,7 @@ mod tests {
"com.github.w1nst0n",
"this_.String_.is_.not_.real_",
] {
assert_ne!(PackageId::new(p_id.into()), None);
assert_ne!(PackageId::new(p_id), None);
}
}
}

View file

@ -1,5 +1,5 @@
use crate::{
adb::{ACommand as AdbCommand, PM_CLEAR_PACK},
adb::{ACommand as AdbCommand, PM_CLEAR_PACK, PackageId},
uad_lists::PackageState,
};
use log::{error, info};
@ -207,19 +207,14 @@ pub fn apply_pkg_state_commands(
#[must_use]
pub fn request_builder(commands: &[&str], package: &str, user: Option<User>) -> Vec<String> {
// Defense-in-depth: `package` is interpolated verbatim into a device-shell
// action, so refuse any name carrying a character that can't appear in a valid
// Android application-ID (`[A-Za-z0-9_.]`). Every current caller already
// action, so refuse any invalid name/ID. Every current caller already
// reconciles the name against the live device package list before reaching
// here, so this rejects nothing legitimate — it just keeps the no-injection
// guarantee local to the sink instead of relying on each caller to sanitise.
// Fail closed: emit no command for a malformed name rather than an injectable
// device-shell string.
if package.is_empty()
|| !package
.bytes()
.all(|b| b.is_ascii_alphanumeric() || b == b'.' || b == b'_')
{
error!("request_builder: refusing package name with invalid characters: {package:?}");
if PackageId::new(package).is_some() {
error!("request_builder: refusing invalid package name: {package:?}");
return Vec::new();
}
let maybe_user_flag = user_flag(user);
@ -300,7 +295,7 @@ pub fn capture_cross_user_states(
/// Detect cross-user behavior and return appropriate notification message.
/// This handles unexpected cross-user behavior:
/// - Case A: Uninstall → Restore (package appears on other users)
/// - Case B: Uninstall → Uninstall (package disappears from other users that previously had it)
/// - Case B: Uninstall → Uninstall (package disappears from other users that previously had it)
/// - Case C: Restore → Restore (package appears on other users)
#[must_use]
pub fn detect_cross_user_behavior(