Skip to content

Commit 1ab21bf

Browse files
authored
Add sudo password prompt alerts (#889)
1 parent 1dfc29d commit 1ab21bf

5 files changed

Lines changed: 180 additions & 5 deletions

File tree

Makefile

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ help:
113113
@printf ' %-18s %s\n' "PACKAGE_WITH_UPDATER=0" "Build packages without codex-update-manager or the updater service"
114114
@printf ' %-18s %s\n' "CODEX_CLI_BUNDLE_SOURCE=..." "Embed an installed Codex CLI package in a local AppImage"
115115
@printf ' %-18s %s\n' "MAX_BUILD_THREADS=8" "Set supported build jobs/compression threads (default: 0, tool/user defaults)"
116+
@printf ' %-18s %s\n' "CODEX_SUDO_ALERT=1" "Play a best-effort alert before an interactive sudo password prompt"
116117
@printf ' %-18s %s\n' "RPM_BINARY_PAYLOAD=..." "Advanced RPM payload flags override (default follows MAX_BUILD_THREADS)"
117118
@printf ' %-18s %s\n' "APPIMAGETOOL=..." "Override the appimagetool executable for make appimage"
118119
@printf ' %-18s %s\n' "DEB=/path/file.deb" "Override the .deb used by make install"
@@ -127,6 +128,7 @@ help:
127128
@printf ' %s\n' "make setup-native"
128129
@printf ' %s\n' "make bootstrap-native"
129130
@printf ' %s\n' "make install-native"
131+
@printf ' %s\n' "CODEX_SUDO_ALERT=1 make install-native"
130132
@printf ' %s\n' "PACKAGE_WITH_UPDATER=0 make update-native"
131133
@printf ' %s\n' "CODEX_CLI_BUNDLE_SOURCE=/path/to/node_modules/@openai/codex make appimage"
132134
@printf ' %s\n' "make inspect-upstream DMG=/tmp/Codex.dmg"
@@ -317,35 +319,35 @@ install:
317319
echo "[make] No pacman package found. Run 'make pacman' first." >&2; exit 1; \
318320
fi; \
319321
echo "[make] Installing $$pkg"; \
320-
sudo pacman -U --noconfirm "$$pkg"; \
322+
"$(CURDIR)/scripts/sudo-with-alert.sh" pacman -U --noconfirm "$$pkg"; \
321323
elif [ "$$format" = "rpm" ] && command -v dnf >/dev/null 2>&1; then \
322324
rpm="$${RPM:-$$(latest_matching_file "$(RPM_GLOB)")}"; \
323325
if [ -z "$$rpm" ]; then \
324326
echo "[make] No RPM package found. Run 'make rpm' first." >&2; exit 1; \
325327
fi; \
326328
echo "[make] Installing $$rpm"; \
327-
sudo dnf install -y "$$rpm"; \
329+
"$(CURDIR)/scripts/sudo-with-alert.sh" dnf install -y "$$rpm"; \
328330
elif [ "$$format" = "rpm" ] && command -v zypper >/dev/null 2>&1; then \
329331
rpm="$${RPM:-$$(latest_matching_file "$(RPM_GLOB)")}"; \
330332
if [ -z "$$rpm" ]; then \
331333
echo "[make] No RPM package found. Run 'make rpm' first." >&2; exit 1; \
332334
fi; \
333335
echo "[make] Installing $$rpm"; \
334-
sudo zypper --non-interactive --no-gpg-checks install -y "$$rpm"; \
336+
"$(CURDIR)/scripts/sudo-with-alert.sh" zypper --non-interactive --no-gpg-checks install -y "$$rpm"; \
335337
elif [ "$$format" = "rpm" ]; then \
336338
rpm="$${RPM:-$$(latest_matching_file "$(RPM_GLOB)")}"; \
337339
if [ -z "$$rpm" ]; then \
338340
echo "[make] No RPM package found. Run 'make rpm' first." >&2; exit 1; \
339341
fi; \
340342
echo "[make] Installing $$rpm"; \
341-
sudo rpm -Uvh "$$rpm"; \
343+
"$(CURDIR)/scripts/sudo-with-alert.sh" rpm -Uvh "$$rpm"; \
342344
elif [ "$$format" = "deb" ]; then \
343345
deb="$${DEB:-$$(latest_matching_file "$(DEB_GLOB)")}"; \
344346
if [ -z "$$deb" ]; then \
345347
echo "[make] No Debian package found. Run 'make deb' first." >&2; exit 1; \
346348
fi; \
347349
echo "[make] Installing $$deb"; \
348-
sudo dpkg -i "$$deb"; \
350+
"$(CURDIR)/scripts/sudo-with-alert.sh" dpkg -i "$$deb"; \
349351
else \
350352
echo "[make] No supported package manager found (dpkg, rpm, zypper, or pacman)." >&2; exit 1; \
351353
fi

docs/native-setup.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ If dependencies are already installed:
2424
make install-native
2525
```
2626

27+
To play a best-effort sound immediately before an interactive `sudo` password
28+
prompt, opt in with `CODEX_SUDO_ALERT=1`:
29+
30+
```bash
31+
CODEX_SUDO_ALERT=1 make install-native
32+
CODEX_SUDO_ALERT=1 make bootstrap-native
33+
CODEX_SUDO_ALERT=1 make update-native
34+
```
35+
36+
The alert is skipped while the existing `sudo` credential timestamp is valid.
37+
It tries the desktop sound system first and falls back to the terminal bell.
38+
Missing audio tools, sound files, or audio services never block installation.
39+
2740
## Guided Setup
2841

2942
```bash

scripts/install-deps.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
88
# shellcheck disable=SC1091
99
source "$SCRIPT_DIR/lib/linux-target-detect.sh"
1010

11+
sudo() {
12+
"$SCRIPT_DIR/sudo-with-alert.sh" "$@"
13+
}
14+
1115
RED='\033[0;31m'
1216
GREEN='\033[0;32m'
1317
YELLOW='\033[1;33m'

scripts/sudo-with-alert.sh

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
4+
play_sudo_alert_command() {
5+
if command -v timeout >/dev/null 2>&1; then
6+
timeout 1s "$@"
7+
else
8+
"$@"
9+
fi
10+
}
11+
12+
play_sudo_alert() {
13+
local sound_file
14+
15+
sound_file="/usr/share/sounds/freedesktop/stereo/dialog-warning.oga"
16+
if command -v pw-play >/dev/null 2>&1 \
17+
&& [ -r "$sound_file" ] \
18+
&& play_sudo_alert_command pw-play "$sound_file" >/dev/null 2>&1; then
19+
return 0
20+
fi
21+
22+
if command -v paplay >/dev/null 2>&1 \
23+
&& [ -r "$sound_file" ] \
24+
&& play_sudo_alert_command paplay "$sound_file" >/dev/null 2>&1; then
25+
return 0
26+
fi
27+
28+
if command -v canberra-gtk-play >/dev/null 2>&1 \
29+
&& play_sudo_alert_command canberra-gtk-play -i dialog-warning >/dev/null 2>&1; then
30+
return 0
31+
fi
32+
33+
for sound_file in \
34+
/usr/share/sounds/alsa/Front_Center.wav \
35+
/usr/share/sounds/sound-icons/prompt.wav; do
36+
if command -v aplay >/dev/null 2>&1 \
37+
&& [ -r "$sound_file" ] \
38+
&& play_sudo_alert_command aplay -q "$sound_file" >/dev/null 2>&1; then
39+
return 0
40+
fi
41+
done
42+
43+
printf '\a' >&2 || true
44+
}
45+
46+
if [ "${CODEX_SUDO_ALERT:-0}" != "1" ]; then
47+
exec sudo "$@"
48+
fi
49+
50+
if sudo -n -v 2>/dev/null; then
51+
exec sudo "$@"
52+
fi
53+
54+
play_sudo_alert || true
55+
sudo -v
56+
exec sudo "$@"

tests/scripts_smoke.sh

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2777,6 +2777,104 @@ test_native_shortcut_targets_compose_existing_flows() {
27772777
assert_contains "$setup_log" 'bash scripts/bootstrap-wizard.sh'
27782778
}
27792779

2780+
test_sudo_alert_wrapper() {
2781+
info "Checking opt-in sudo password alerts"
2782+
local workspace="$TMP_DIR/sudo-alert"
2783+
local bin_dir="$workspace/bin"
2784+
local log_file="$workspace/events.log"
2785+
local wrapper="$REPO_DIR/scripts/sudo-with-alert.sh"
2786+
mkdir -p "$bin_dir"
2787+
2788+
cat > "$bin_dir/sudo" <<'EOF'
2789+
#!/usr/bin/env bash
2790+
set -eu
2791+
printf 'sudo:%s\n' "$*" >> "$SUDO_ALERT_TEST_LOG"
2792+
if [ "${1-}" = "-n" ] && [ "${2-}" = "-v" ]; then
2793+
exit "${SUDO_ALERT_TEST_CACHE_STATUS:-0}"
2794+
fi
2795+
if [ "${1-}" = "-v" ]; then
2796+
exit "${SUDO_ALERT_TEST_AUTH_STATUS:-0}"
2797+
fi
2798+
exit "${SUDO_ALERT_TEST_COMMAND_STATUS:-0}"
2799+
EOF
2800+
chmod +x "$bin_dir/sudo"
2801+
2802+
cat > "$bin_dir/pw-play" <<'EOF'
2803+
#!/usr/bin/env bash
2804+
set -eu
2805+
printf 'alert:%s\n' "$*" >> "$SUDO_ALERT_TEST_LOG"
2806+
exit "${SUDO_ALERT_TEST_SOUND_STATUS:-0}"
2807+
EOF
2808+
chmod +x "$bin_dir/pw-play"
2809+
2810+
: > "$log_file"
2811+
PATH="$bin_dir:$HOST_TOOL_PATH" SUDO_ALERT_TEST_LOG="$log_file" \
2812+
"$wrapper" true
2813+
assert_occurrence_count "$log_file" '^sudo:true$' 1
2814+
assert_not_contains "$log_file" 'sudo:-n -v'
2815+
assert_not_contains "$log_file" 'alert:'
2816+
2817+
: > "$log_file"
2818+
CODEX_SUDO_ALERT=1 PATH="$bin_dir:$HOST_TOOL_PATH" SUDO_ALERT_TEST_LOG="$log_file" \
2819+
"$wrapper" true
2820+
assert_contains "$log_file" 'sudo:-n -v'
2821+
assert_not_contains "$log_file" 'alert:'
2822+
assert_contains "$log_file" 'sudo:true'
2823+
2824+
: > "$log_file"
2825+
CODEX_SUDO_ALERT=1 SUDO_ALERT_TEST_CACHE_STATUS=1 \
2826+
PATH="$bin_dir:$HOST_TOOL_PATH" SUDO_ALERT_TEST_LOG="$log_file" \
2827+
"$wrapper" true
2828+
[ "$(sed -n '1p' "$log_file")" = 'sudo:-n -v' ] || fail "Expected cached sudo check first"
2829+
[ "$(sed -n '2p' "$log_file")" = 'alert:/usr/share/sounds/freedesktop/stereo/dialog-warning.oga' ] \
2830+
|| fail "Expected alert before authentication"
2831+
[ "$(sed -n '3p' "$log_file")" = 'sudo:-v' ] || fail "Expected sudo authentication after alert"
2832+
[ "$(sed -n '4p' "$log_file")" = 'sudo:true' ] || fail "Expected command after authentication"
2833+
2834+
: > "$log_file"
2835+
CODEX_SUDO_ALERT=1 SUDO_ALERT_TEST_CACHE_STATUS=1 SUDO_ALERT_TEST_SOUND_STATUS=1 \
2836+
PATH="$bin_dir:$HOST_TOOL_PATH" SUDO_ALERT_TEST_LOG="$log_file" \
2837+
"$wrapper" true 2>/dev/null
2838+
assert_contains "$log_file" 'sudo:-v'
2839+
assert_contains "$log_file" 'sudo:true'
2840+
2841+
: > "$log_file"
2842+
local status=0
2843+
CODEX_SUDO_ALERT=1 SUDO_ALERT_TEST_CACHE_STATUS=1 SUDO_ALERT_TEST_AUTH_STATUS=23 \
2844+
PATH="$bin_dir:$HOST_TOOL_PATH" SUDO_ALERT_TEST_LOG="$log_file" \
2845+
"$wrapper" true 2>/dev/null || status=$?
2846+
[ "$status" -eq 23 ] || fail "Expected sudo authentication failure status, got $status"
2847+
assert_not_contains "$log_file" 'sudo:true'
2848+
2849+
: > "$log_file"
2850+
status=0
2851+
CODEX_SUDO_ALERT=1 SUDO_ALERT_TEST_COMMAND_STATUS=17 \
2852+
PATH="$bin_dir:$HOST_TOOL_PATH" SUDO_ALERT_TEST_LOG="$log_file" \
2853+
"$wrapper" true || status=$?
2854+
[ "$status" -eq 17 ] || fail "Expected privileged command status, got $status"
2855+
}
2856+
2857+
test_native_sudo_alert_wiring() {
2858+
info "Checking native targets route sudo through the alert wrapper"
2859+
local install_log="$TMP_DIR/make-install-sudo-alert.log"
2860+
local bootstrap_log="$TMP_DIR/make-bootstrap-sudo-alert.log"
2861+
local update_log="$TMP_DIR/make-update-sudo-alert.log"
2862+
2863+
CODEX_SUDO_ALERT=1 make -n -C "$REPO_DIR" install >"$install_log"
2864+
assert_occurrence_count "$install_log" 'scripts/sudo-with-alert.sh' 5
2865+
2866+
CODEX_SUDO_ALERT=1 make -n -C "$REPO_DIR" bootstrap-native >"$bootstrap_log"
2867+
assert_contains "$bootstrap_log" 'bash scripts/install-deps.sh'
2868+
assert_contains "$bootstrap_log" 'install-native'
2869+
2870+
CODEX_SUDO_ALERT=1 make -n -C "$REPO_DIR" update-native >"$update_log"
2871+
assert_contains "$update_log" 'git pull --ff-only'
2872+
assert_contains "$update_log" 'install-native'
2873+
2874+
assert_contains "$REPO_DIR/scripts/install-deps.sh" 'sudo-with-alert.sh'
2875+
assert_contains "$REPO_DIR/Makefile" 'CODEX_SUDO_ALERT=1'
2876+
}
2877+
27802878
test_fedora_dependency_bootstrap_installs_rpmbuild() {
27812879
info "Checking Fedora dependency bootstrap includes rpmbuild and C++ build tools"
27822880
local install_deps="$REPO_DIR/scripts/install-deps.sh"
@@ -9082,6 +9180,8 @@ main() {
90829180
test_transactional_install_uses_managed_node_and_isolated_reports
90839181
test_installer_cleanup_handles_readonly_trees
90849182
test_native_shortcut_targets_compose_existing_flows
9183+
test_sudo_alert_wrapper
9184+
test_native_sudo_alert_wiring
90859185
test_fedora_dependency_bootstrap_installs_rpmbuild
90869186
test_fedora_atomic_rpm_ostree_target_detection
90879187
test_setup_native_wizard_noninteractive_feature_writer

0 commit comments

Comments
 (0)