From f2c39254315104cec364e3335bd371f9a7c78f5a Mon Sep 17 00:00:00 2001 From: Afonso Oliveira Date: Sun, 6 Sep 2026 22:49:43 +0100 Subject: [PATCH] Revoke before session cleanup and protect standalone inhibition --- bin/omarchy-update | 5 ++++ bin/omarchy-update-stay-awake | 15 ++++++++++-- test/shell.d/update-hook-security-test.sh | 6 +++++ test/shell.d/update-lock-test.sh | 29 ++++++++++++++++------- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/bin/omarchy-update b/bin/omarchy-update index a3cc94a9..b6e5c7bd 100755 --- a/bin/omarchy-update +++ b/bin/omarchy-update @@ -23,6 +23,10 @@ update_stay_awake_stopped=0 cleanup_update() { local status=$? trap - EXIT HUP INT TERM + if ! omarchy_security_revoke_sudo_timestamp; then + echo "Could not invalidate sudo before update cleanup." >&2 + omarchy_security_exit_with_revoked_sudo 1 + fi if (( update_stay_awake_stopped == 0 )); then omarchy-update-stay-awake stop || status=1 fi @@ -88,6 +92,7 @@ if [[ ${1:-} == "-y" ]] || omarchy-update-confirm; then # Release update-owned inhibitors before offering a reboot. A confirmed # reboot can terminate this process before its EXIT trap gets a chance to # remove the persistent Stay Awake marker. + omarchy_security_revoke_sudo_timestamp omarchy-update-stay-awake stop update_stay_awake_stopped=1 diff --git a/bin/omarchy-update-stay-awake b/bin/omarchy-update-stay-awake index 826fee5e..4b214ca0 100755 --- a/bin/omarchy-update-stay-awake +++ b/bin/omarchy-update-stay-awake @@ -1,10 +1,21 @@ -#!/bin/bash +#!/bin/bash -p # omarchy:summary=Manage sleep and idle inhibition during an update # omarchy:args= # omarchy:hidden=true +if [[ $- != *p* ]]; then + echo "Refusing an unsafe Bash startup." >&2 + exit 126 +fi + +source "${BASH_SOURCE[0]%/*}/omarchy-security-functions" || exit 126 +omarchy_security_require_privileged_bash_startup || exit 126 set -e +omarchy_security_sanitize_bash_environment "$0" "$@" +omarchy_security_revoke_sudo_timestamp || exit 1 +omarchy_security_install_sudo_cleanup_traps +omarchy_security_enable_no_update_sudo state_dir="${XDG_RUNTIME_DIR:-/tmp/omarchy-$UID}/omarchy-update-stay-awake" idle_owner_file="$state_dir/idle-owner" @@ -106,7 +117,7 @@ start() { ( [[ -z ${OMARCHY_UPDATE_LOCK_FD:-} ]] || exec {OMARCHY_UPDATE_LOCK_FD}>&- exec "${hold_command[@]}" ) & elif [[ -t 0 ]]; then - sudo -N -b -- "${hold_command[@]}" + /usr/bin/sudo -N -b -- "${hold_command[@]}" else ( [[ -z ${OMARCHY_UPDATE_LOCK_FD:-} ]] || exec {OMARCHY_UPDATE_LOCK_FD}>&- exec pkexec "${hold_command[@]}" ) & diff --git a/test/shell.d/update-hook-security-test.sh b/test/shell.d/update-hook-security-test.sh index 4e0922c3..0181b800 100755 --- a/test/shell.d/update-hook-security-test.sh +++ b/test/shell.d/update-hook-security-test.sh @@ -36,6 +36,12 @@ for step in omarchy-update-system-pkgs yay omarchy-hook omarchy-update-mise; do export SUDO_TEST_FAIL_STEP=$step if run_update -y; then fail "$step failure must fail the update"; fi assert_boundary_cold "failed $step" + python3 - "$SUDO_TEST_LOG" <<'PY' +import sys +s=open(sys.argv[1]).read().splitlines() +for i,line in enumerate(s): + if line=='step:omarchy-update-stay-awake stop': assert i>0 and s[i-1]=='sudo -k',s +PY pass "update revokes credentials after $step fails" done diff --git a/test/shell.d/update-lock-test.sh b/test/shell.d/update-lock-test.sh index 7348848c..5b0e3b54 100644 --- a/test/shell.d/update-lock-test.sh +++ b/test/shell.d/update-lock-test.sh @@ -136,14 +136,10 @@ kill -0 "$inhibitor_pid" 2>/dev/null && pass "omarchy-update waits for its sleep inhibitor to stop" if (( EUID != 0 )); then - sudo_log="$test_tmp/sudo.log" + sudo_log="$SUDO_TEST_LOG" + : >"$sudo_log" pkexec_marker="$test_tmp/pkexec-used" terminal_inhibit_pid_file="$test_tmp/terminal-inhibit-pid" - write_stub sudo ' -printf "%s\n" "$*" >>"$SUDO_LOG" -[[ $1 == "-N" && $2 == "-b" && $3 == "--" ]] || exit 90 -shift 3 -"$@" &' write_stub pkexec '[[ -z ${PKEXEC_MARKER:-} ]] || touch "$PKEXEC_MARKER"; exec "$@"' # start leaves the inhibitor running on purpose, but script tears the pty down @@ -154,7 +150,7 @@ shift 3 #!/bin/bash omarchy-update-stay-awake start for _ in {1..200}; do - grep -q -- '^-N -b -- ' "$SUDO_LOG" && break + grep -q -- '^sudo -N -b -- ' "$SUDO_LOG" && break sleep 0.05 done SH @@ -163,8 +159,7 @@ SH SUDO_LOG="$sudo_log" PKEXEC_MARKER="$pkexec_marker" INHIBIT_PID_FILE="$terminal_inhibit_pid_file" \ run_with_lock_env script -qefc "$terminal_driver" /dev/null >/dev/null - grep -q -- '^-N -b -- ' "$sudo_log" || fail "terminal inhibition authenticates its background command without a reusable timestamp" - grep -q -- '^-N -b -- ' "$sudo_log" || fail "terminal sleep inhibition runs through sudo" + grep -q -- '^sudo -N -b -- ' "$sudo_log" || fail "terminal inhibition authenticates its background command without a reusable timestamp" [[ ! -e $pkexec_marker ]] || fail "terminal sleep inhibition does not use pkexec" run_with_lock_env "$SUDO_TEST_ROOT/bin/omarchy-update-stay-awake" stop pass "terminal updates use sudo instead of Polkit for sleep inhibition" @@ -229,3 +224,19 @@ kill -0 "$unrelated_pid" 2>/dev/null || kill "$unrelated_pid" wait "$unrelated_pid" 2>/dev/null || true pass "stale inhibitor state does not terminate a reused PID" + +# The hidden helper also establishes its own boundary when invoked directly. +reset_boundary +touch "$SUDO_TEST_CACHE" +run_with_lock_env "$SUDO_TEST_ROOT/bin/omarchy-update-stay-awake" stop +[[ $(head -1 "$SUDO_TEST_LOG") == "sudo -k" ]] || fail "standalone inhibitor cleanup did not start cold" +assert_boundary_cold "standalone inhibitor cleanup" +pass "standalone inhibitor cleanup revokes before and after session work" + +reset_boundary +export SUDO_TEST_REVOKE_FAIL=1 +if run_with_lock_env "$SUDO_TEST_ROOT/bin/omarchy-update-stay-awake" start; then + fail "inhibitor started after failed initial revocation" +fi +[[ ! -e $stay_awake_helper_state/inhibit-pid ]] || fail "failed revocation started an inhibitor" +pass "failed initial revocation prevents standalone inhibition"