From bd1ed7ea766b0a9def0122994897461fde55359f Mon Sep 17 00:00:00 2001 From: aeonframework Date: Mon, 22 Jun 2026 10:54:06 -0400 Subject: [PATCH] harden: validate package-name charset in request_builder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `request_builder` interpolates `package` verbatim into a device-shell action string. Every current caller reconciles the name against the live device package list first, so this is safe today — but the safety lives in the callers, not the sink. Add a local charset guard (Android app-ID set `[A-Za-z0-9_.]`) so a future caller passing an unreconciled or file-sourced name can't produce an injectable device-shell command. Rejects nothing legitimate; fails closed (emits no command) on a malformed name. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/uad-core/src/sync.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crates/uad-core/src/sync.rs b/crates/uad-core/src/sync.rs index fe83788..eb3f7e5 100644 --- a/crates/uad-core/src/sync.rs +++ b/crates/uad-core/src/sync.rs @@ -206,6 +206,22 @@ pub fn apply_pkg_state_commands( /// which act on a common `package` and `user`. #[must_use] pub fn request_builder(commands: &[&str], package: &str, user: Option) -> Vec { + // 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 + // 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:?}"); + return Vec::new(); + } let maybe_user_flag = user_flag(user); commands .iter()