Refresh the passwordless sudo indicator on change instead of polling (#15026)

* Refresh the passwordless sudo indicator on change instead of polling

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Refresh the sudo indicator after failed changes and on a wall-clock deadline

Addresses Codex review: a rolled-back renewal skipped the refresh, the monotonic user timer paused through suspend, and default timer accuracy allowed a minute's delay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test the sudo indicator refreshes by running the grant flows

Replace source-string checks with runs of timed, renewed, failed, permanent and disable flows against the fixture, asserting the recorded omarchy-shell refresh and wall-clock systemd-run --user timer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier HanssonandClaude Opus 5.5 authored and GitHub committed 2026-10-11 23:01:45 +09:00
1 parent 8bd1b7c045
commit 3c1f7b780f
5 files changed
+96 -14

No files matched your search

+30 -3
View File
@@ -462,7 +462,34 @@ root_dispatch() {
esac
}
# The bar indicator only probes on demand, so every grant change pushes a
# refresh. Root's expiry timer can't reach the user's shell, so a user timer
# refreshes once the deadline has passed. Both are best effort: a stale red
# indicator still revokes cleanly when clicked.
refresh_indicator() {
/usr/bin/omarchy-shell -q omarchy.indicators refresh >/dev/null 2>&1 || true
}
# A failed enable can still change the grant (a renewal that rolls back removes
# the previous policy), so refresh whatever the privileged action returned.
change_grant() {
local status=0
/usr/bin/sudo "$@" || status=$?
refresh_indicator
return "$status"
}
# A wall-clock deadline, like the sudoers rule and root's timer, so expiry
# during suspend still refreshes on resume.
schedule_expiry_refresh() {
[[ $minutes == "permanent" ]] && return 0
/usr/bin/systemd-run --user --quiet --collect --on-calendar="@$((EPOCHSECONDS + 10#$minutes * 60 + 2))" \
--timer-property=AccuracySec=1s \
-- /usr/bin/omarchy-shell -q omarchy.indicators refresh >/dev/null 2>&1 || true
}
show_enabled() {
schedule_expiry_refresh
if [[ $minutes == "permanent" ]]; then
echo "Passwordless sudo has been ENABLED permanently. Run this command again to disable it."
else
@@ -542,12 +569,12 @@ else
fi
fi
if (( status == 0 )) && { (($# == 0)) || [[ $minutes == "--disable" ]]; }; then
/usr/bin/sudo -n -N -- "$INSTALLED_SELF" __disable "$uid"
change_grant -n -N -- "$INSTALLED_SELF" __disable "$uid"
echo "Passwordless sudo has been DISABLED. Sudo will require a password again."
elif (( status == STATUS_INACTIVE )) && [[ $minutes == "--disable" ]]; then
echo "Passwordless sudo is already DISABLED."
elif (( status == 0 )) && [[ $minutes != "permanent" ]]; then
/usr/bin/sudo -N -- "$INSTALLED_SELF" __enable "$uid" "$minutes"
change_grant -N -- "$INSTALLED_SELF" __enable "$uid" "$minutes"
show_enabled
else
if (( status != 0 && status != STATUS_INACTIVE )); then
@@ -587,7 +614,7 @@ else
echo ""
if /usr/bin/gum confirm "Enable passwordless sudo $duration? This is a significant security risk!"; then
/usr/bin/sudo -N -- "$INSTALLED_SELF" __enable "$uid" "$minutes"
change_grant -N -- "$INSTALLED_SELF" __enable "$uid" "$minutes"
echo ""
show_enabled
else
+1 -1
View File
@@ -2,7 +2,7 @@
`omarchy-sudo-passwordless` publishes a timed or permanent grant for the numeric UID authenticated by sudo. Its user interface inspects sudo policy without prompting and authenticates only the enable action when access is off, without a reusable sudo timestamp; fixed installed internal actions run as root and serialize on `/run/lock/omarchy-sudo-passwordless.lock`.
The menu bar's `PasswordlessSudo` indicator polls `--active` every five seconds and shows the passwordless sudo icon in red while access is active. The probe normally reads sudo's noninteractive long policy listing for the installed `__status` action and checks its `!authenticate` tag. If listing requires authentication, it falls back to a noninteractive `__status` call that ignores cached credentials. Neither path prompts or updates the credential cache. Interactive setup likewise falls back to noninteractive status and, when authentication is unavailable, leaves validation to the single confirmed enable action. Clicking the active indicator runs `--disable` noninteractively without opening a terminal, so a grant expiring between display and click cannot start the enable flow.
The menu bar's `PasswordlessSudo` indicator runs `--active` when the shell starts and whenever `omarchy.indicators` is refreshed, and shows the passwordless sudo icon in red while access is active. The probe normally reads sudo's noninteractive long policy listing for the installed `__status` action and checks its `!authenticate` tag. If listing requires authentication, it falls back to a noninteractive `__status` call that ignores cached credentials. Neither path prompts or updates the credential cache. Interactive setup likewise falls back to noninteractive status and, when authentication is unavailable, leaves validation to the single confirmed enable action. Clicking the active indicator runs `--disable` noninteractively without opening a terminal, so a grant expiring between display and click cannot start the enable flow. The indicator never polls: every enable or disable attempt refreshes it directly, failed ones included since a rolled-back renewal can remove the previous grant, and enabling a timed grant also starts a transient user timer on the same wall-clock deadline that refreshes it two seconds later, so expiry during suspend shows on resume. The root expiry timer cannot reach the user's shell. If that refresh is missed, the indicator stays red past expiry, and clicking it reports access is already disabled and clears it.
## Grant lifecycle
@@ -31,19 +31,15 @@ BarIndicator {
onBarChanged: refresh()
Component.onCompleted: refresh()
// No polling: each probe is a hardened script plus sudo calls that can log.
// omarchy-sudo-passwordless refreshes omarchy.indicators on enable, disable
// and expiry instead.
Connections {
target: root.indicatorHost
ignoreUnknownSignals: true
function onRefreshRequested() { root.refresh() }
}
Timer {
interval: 5000
repeat: true
running: !!root.bar
onTriggered: root.refresh()
}
Process {
id: statusProc
command: ["omarchy-sudo-passwordless", "--active"]
@@ -58,6 +58,8 @@ case "$name" in
[[ ${TEST_POST_PUBLISH_FAIL:-0} != 1 ]] || : >"$TEST_GRANT_ROOT/run/omarchy-sudo-passwordless-package-removing"
;;
systemd-run)
# The indicator's user refresh timer is not the root expiry timer.
[[ ${1:-} != --user ]] || exit 0
[[ ${TEST_TIMER_FAIL:-0} != 1 ]] || exit 1
if [[ ${TEST_CANCEL_ENABLE:-0} == 1 ]]; then kill -TERM "$PPID"; fi
;;
@@ -67,6 +69,7 @@ case "$name" in
date)
if [[ ${TEST_EXPIRED:-0} == 1 && $* == '-u +%Y%m%d%H%M%SZ' ]]; then echo 99991231235959Z; else /usr/bin/date "$@"; fi
;;
omarchy-shell) ;;
getent) printf '%s:x:1000:1000:Test:/nonexistent:/bin/bash\n' "${TEST_ACCOUNT:-audituser}" ;;
sudo)
if [[ ${1:-} == -h ]]; then echo 'usage: sudo [-N] command'; exit 0; fi
@@ -86,6 +89,7 @@ case "$name" in
TEST_EUID=0 /usr/bin/bash -p "$@"
else
[[ ${2:-} != __status ]] || exit "${TEST_STATUS:-3}"
[[ ${2:-} != __enable || ${TEST_ENABLE_FAIL:-0} != 1 ]] || exit 1
fi
;;
gum)
@@ -100,7 +104,7 @@ case "$name" in
esac
STUB
chmod +x "$test_tmp/bin/mock"
for name in stat chown install rm mv systemd-run systemctl date getent sudo gum; do
for name in stat chown install rm mv systemd-run systemctl date getent sudo gum omarchy-shell; do
ln -s mock "$test_tmp/bin/$name"
done
@@ -114,7 +118,7 @@ for name in ('omarchy-sudo-passwordless', 'omarchy-security-functions'):
target = str(temp/'hooks') if path == '/usr/share/libalpm/hooks' else str(temp) + path
text = text.replace(path, target)
text = text.replace('((EUID == 0))', '((${TEST_EUID:-1} == 0))')
for command in ('stat', 'chown', 'install', 'rm', 'mv', 'systemd-run', 'systemctl', 'date', 'getent', 'sudo', 'gum'):
for command in ('stat', 'chown', 'install', 'rm', 'mv', 'systemd-run', 'systemctl', 'date', 'getent', 'sudo', 'gum', 'omarchy-shell'):
text = text.replace('/usr/bin/' + command, str(temp/'bin'/command))
(temp/name).write_text(text)
(temp/name).chmod(0o755)
@@ -17,7 +17,8 @@ assert(!widget.includes('sudoHorizontal') && !widget.includes('sudoVertical'), '
assert(!indicator.includes('visible:'), 'sudo uses the shared indicator visibility and hover behavior')
assert(indicator.includes('command: ["omarchy-sudo-passwordless", "--active"]'), 'indicator uses the noninteractive grant probe')
assert(indicator.includes('root.granted = exitCode === 0 && exitStatus === 0'), 'failed or interrupted probes do not claim an active grant')
assert(indicator.includes('interval: 5000') && indicator.includes('onTriggered: root.refresh()'), 'indicator refreshes after activation, revocation, and expiry')
assert(!indicator.includes('Timer'), 'indicator does not poll the sudo probe')
assert(indicator.includes('function onRefreshRequested() { root.refresh() }'), 'indicator probes when the indicator host asks for a refresh')
assert(indicator.includes('command: ["omarchy-sudo-passwordless", "--disable"]'), 'active indicator revokes access through a background process')
assert(indicator.includes('if (root.granted) disableProc.running = true'), 'active click bypasses the terminal launcher')
assert(indicator.includes('root.indicatorHost.refresh()'), 'disabling access refreshes all indicator instances immediately')
@@ -34,3 +35,57 @@ revoke.running = false
press(button, revoke)
assertDeepEqual(launched, ['omarchy-launch-floating-terminal-with-presentation omarchy-sudo-passwordless'], 'inactive click still opens interactive setup')
JS
# Run the public grant flows against the fixture's stand-ins and check the
# refreshes they push, without touching the host shell or user timers.
source "$SHELL_TEST_DIR/fixtures/passwordless-sudo-test.sh"
refresh="omarchy-shell -q omarchy.indicators refresh"
run_grant_flow() {
: >"$test_tmp/commands"
env "$@" /usr/bin/bash -p "$test_tmp/omarchy-sudo-passwordless" "${grant_args[@]}" >"$test_tmp/public.log" 2>&1 || true
}
refresh_follows() {
grep -A1 -- "$1" "$test_tmp/commands" | grep -qxF -- "$refresh"
}
user_timers() {
grep -c '^systemd-run --user ' "$test_tmp/commands" || true
}
grant_args=(15)
before=$EPOCHSECONDS
run_grant_flow TEST_STATUS=3 TEST_CONFIRM_STATUS=0
after=$EPOCHSECONDS
refresh_follows ' __enable [0-9]* 15$' || fail "timed enable must refresh the indicator after the grant changes"
timer=$(grep '^systemd-run --user ' "$test_tmp/commands") || fail "timed enable must schedule an expiry refresh"
timer_pattern="^systemd-run --user --quiet --collect --on-calendar=@([0-9]+) --timer-property=AccuracySec=1s -- $test_tmp/bin/omarchy-shell -q omarchy.indicators refresh$"
[[ $timer =~ $timer_pattern ]] || fail "unexpected expiry refresh timer: $timer"
deadline=${BASH_REMATCH[1]}
(( deadline >= before + 15 * 60 + 2 && deadline <= after + 15 * 60 + 2 )) || fail "expiry refresh must fire just after the wall-clock deadline"
pass "timed enable refreshes now and on a wall-clock timer just after expiry"
run_grant_flow TEST_STATUS=0
refresh_follows ' __enable [0-9]* 15$' || fail "renewal must refresh the indicator"
(( $(user_timers) == 1 )) || fail "renewal must schedule an expiry refresh"
pass "renewing a timed grant refreshes and reschedules the expiry refresh"
run_grant_flow TEST_STATUS=0 TEST_ENABLE_FAIL=1
refresh_follows ' __enable [0-9]* 15$' || fail "failed renewal must still refresh the indicator"
(( $(user_timers) == 0 )) || fail "failed renewal must not schedule an expiry refresh"
pass "a failed grant change still refreshes the indicator"
grant_args=(permanent)
run_grant_flow TEST_STATUS=3 TEST_CONFIRM_STATUS=0
refresh_follows ' __enable [0-9]* permanent$' || fail "permanent enable must refresh the indicator"
(( $(user_timers) == 0 )) || fail "permanent enable must not schedule an expiry refresh"
pass "permanent enable refreshes without scheduling an expiry refresh"
assert_disable_refreshes() {
run_grant_flow TEST_STATUS=0
refresh_follows ' __disable [0-9]*$' || fail "disable ${grant_args[*]} must refresh the indicator"
(( $(user_timers) == 0 )) || fail "disable must not schedule an expiry refresh"
}
grant_args=()
assert_disable_refreshes
grant_args=(--disable)
assert_disable_refreshes
pass "disabling access refreshes the indicator"