mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(security): stop classifying read-only pacman -S queries as Install (#2667)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
4e7c6e1f53
commit
ef7a94dc6f
@@ -814,6 +814,23 @@ const NODE_PKG_READ_VERBS: &[&str] = &[
|
||||
/// run build scripts, so they are fail-closed to `Write`.
|
||||
const CARGO_READ_VERBS: &[&str] = &["tree", "metadata", "search", "info", "version", "help"];
|
||||
|
||||
/// Detect a pacman *install/upgrade* from its bundled operation flag.
|
||||
///
|
||||
/// pacman packs its operation and modifiers into a single flag (`-Syu`, `-Ss`),
|
||||
/// and `args` reach us already lowercased — so the `-S` (sync) operation is
|
||||
/// indistinguishable from a literal `-s` by case alone. We therefore key off
|
||||
/// the *modifier* letters instead of a blanket `starts_with("-s")`, which would
|
||||
/// over-match every read-only `-S` query: a `-S`-family flag mutates the host
|
||||
/// only when it carries none of pacman's read-only query modifiers — search
|
||||
/// (`s`), info (`i`), list (`l`), groups (`g`) or print (`p`). So `-S pkg`,
|
||||
/// `-Sy`, `-Syu` are installs while `-Ss`/`-Si`/`-Sl`/`-Sg`/`-Sp` are reads.
|
||||
fn is_pacman_install(args: &[String]) -> bool {
|
||||
args.iter().any(|a| {
|
||||
a.strip_prefix("-s")
|
||||
.is_some_and(|modifiers| !modifiers.contains(['s', 'i', 'l', 'g', 'p']))
|
||||
})
|
||||
}
|
||||
|
||||
/// Detect a package-manager *install* invocation. These mutate the host /
|
||||
/// global environment, so they are the always-ask `Install` bucket (even in
|
||||
/// Full) — the same gate the dedicated `install_tool` enforces, applied to the
|
||||
@@ -826,7 +843,7 @@ fn is_install_command(base: &str, args: &[String]) -> bool {
|
||||
match base {
|
||||
// System package managers.
|
||||
"apt" | "apt-get" | "dnf" | "yum" | "zypper" => has("install"),
|
||||
"pacman" => args.iter().any(|a| a.starts_with("-s")), // -S / -Sy / -Syu (lowercased)
|
||||
"pacman" => is_pacman_install(args),
|
||||
"apk" => has("add"),
|
||||
"brew" | "snap" | "flatpak" | "winget" | "choco" | "scoop" => has("install"),
|
||||
// Language package managers — host/global-modifying installs only.
|
||||
|
||||
@@ -427,6 +427,8 @@ fn classify_installs_are_install_bucket() {
|
||||
"apt-get install -y curl",
|
||||
"brew install ripgrep",
|
||||
"pacman -S vim",
|
||||
"pacman -Sy",
|
||||
"pacman -Syu",
|
||||
"apk add bash",
|
||||
"dnf install nginx",
|
||||
"pip install requests",
|
||||
@@ -456,6 +458,23 @@ fn classify_local_installs_are_write_not_install() {
|
||||
assert_eq!(p.classify_command("cargo add serde"), CommandClass::Write);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classify_pacman_readonly_queries_are_not_install() {
|
||||
let p = default_policy();
|
||||
// pacman's `-S` family includes read-only queries (search/info/list/groups/
|
||||
// print). A blanket `starts_with("-s")` mis-bucketed these as always-ask
|
||||
// Install; they must fall through to the fail-closed Write default instead.
|
||||
for c in [
|
||||
"pacman -Ss firefox", // search
|
||||
"pacman -Si vim", // info
|
||||
"pacman -Sl core", // list a repo
|
||||
"pacman -Sg", // list groups
|
||||
"pacman -Sp vim", // print download URLs
|
||||
] {
|
||||
assert_eq!(p.classify_command(c), CommandClass::Write, "{c}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gate_decision_install_always_asks_even_in_full() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user