Run notification click actions as argv, not shell strings

The click action of a notification was a free-form shell string run through
`bash -lc`, safe only when every sender shell-quoted every interpolated value
perfectly. One slip is RCE: a hostile yt-dlp video title forged an output
record and injected an mpv option into the click command (mehmetince.net RCE,
partially addressed by #7847).

Add a parameterized transport: omarchy-notification-send gains --exec-arg
(repeatable), encoding a JSON argv into the omarchy-exec-argv hint. The shell
runs it with Quickshell.execDetached(argv) and no shell, so data an attacker
controls is only ever one argument and can never be reparsed as a command. The
shell fails closed on a malformed argv hint.

The legacy free-form --exec string is retained but honored only from Omarchy's
own omarchy-action toasts, and deprecated. Migrate all in-repo callers
(screenshot, screen recording, taildrop receive, migrate-notify, crash-watch,
yt-dlp host) to --exec-arg. Update docs and tests.
This commit is contained in:
Ryan Hughes
2026-08-23 12:00:03 -04:00
parent ef6d9e6605
commit 07443f3970
16 changed files with 219 additions and 45 deletions
+5 -4
View File
@@ -62,11 +62,12 @@ pass "taildrop receive announces other files with a glyph"
# The shell keeps the click command with the toast, so receiving does not have
# to sit blocked on an answer -- and the toast still opens the file after a shell
# restart. Names with spaces have to arrive quoted for the shell to run them.
grep -qF -- "--exec xdg-open $downloads/photo.png" <<<"$notifications" ||
# restart. The path rides as its own --exec-arg, so the shell runs it as literal
# data with no quoting for a name with spaces to get wrong.
grep -qF -- "--exec-arg xdg-open --exec-arg $downloads/photo.png" <<<"$notifications" ||
fail "taildrop receive attaches the open command to the notification" "$notifications"
grep -qF -- "--exec xdg-open $(printf %q "$downloads/notes with space.pdf")" <<<"$notifications" ||
fail "taildrop receive quotes spaced names in the open command" "$notifications"
grep -qF -- "--exec-arg xdg-open --exec-arg $downloads/notes with space.pdf" <<<"$notifications" ||
fail "taildrop receive carries spaced names as a literal open argument" "$notifications"
pass "taildrop receive lets a click open the received file"
grep -q "unrelated.txt" <<<"$notifications" &&