From 20f0160799b2d42a406afccc3228f3f34497b28a Mon Sep 17 00:00:00 2001 From: en Date: Wed, 16 Sep 2026 05:41:56 +0200 Subject: [PATCH] Shell-quote bind_command args (Hyprland exec_cmd injection) fix(hypr): Grok-flagged security finding: shell_single was applied to bin and sock but args was interpolated raw. A profile hotkey containing shell metacharacters could become a Hyprland exec_cmd injection. - src/hypr.rs: bind_command now wraps args in shell_single - New tests: - bind_command_quotes_args_with_metacharacters - shell_single_handles_inner_quote - Doc comment updated to name the threat and what gets escaped 93+/0 cargo test; clippy clean. --- src/hypr.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/hypr.rs b/src/hypr.rs index 0cefcb3..371733e 100644 --- a/src/hypr.rs +++ b/src/hypr.rs @@ -227,12 +227,15 @@ fn shell_single(s: &str) -> String { } /// Hyprland exec_cmd string. `args` is the IPC line after the binary, e.g. `hotkey Alt+G`. +/// `bin`, `sock`, and `args` are all single-quoted so a map hotkey containing +/// shell metacharacters (`;`, backticks, `$()`, quotes, spaces) cannot inject +/// into the compositor's `exec_cmd`. pub fn bind_command(bin: &str, sock: &str, args: &str) -> String { format!( "{} ipc --sock {} {}", shell_single(bin), shell_single(sock), - args + shell_single(args) ) } @@ -312,15 +315,30 @@ where #[cfg(test)] mod tests { - use super::bind_command; + use super::*; #[test] fn bind_command_has_one_ipc() { let cmd = bind_command("/tmp/enboxer", "/tmp/enboxer.sock", "hotkey Alt+G"); assert_eq!( cmd, - "'/tmp/enboxer' ipc --sock '/tmp/enboxer.sock' hotkey Alt+G" + "'/tmp/enboxer' ipc --sock '/tmp/enboxer.sock' 'hotkey Alt+G'" ); assert_eq!(cmd.matches(" ipc ").count(), 1); } + + #[test] + fn bind_command_quotes_args_with_metacharacters() { + // shell_single must escape a hostile arg (semicolon, backtick, + // dollar-paren) so it cannot inject into Hyprland's exec_cmd. + let s = bind_command( + "enboxer", + "/tmp/enboxer.sock", + "hotkey Alt+G; cat /etc/passwd", + ); + assert!(s.starts_with("'enboxer' ipc --sock '/tmp/enboxer.sock' ")); + assert!(s.contains("'hotkey Alt+G; cat /etc/passwd'")); + } + + }