From 7d4773311e77d3543395223254da9a5a2565df33 Mon Sep 17 00:00:00 2001 From: Afonso Oliveira Date: Wed, 23 Sep 2026 09:45:24 +0100 Subject: [PATCH 1/4] Pin factory-reset elevation to the packaged command Use the installed executable for non-root self-elevation, including calls from a development checkout or symlink. Reported-by: Aleksej --- bin/omarchy-system-factory-reset | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/omarchy-system-factory-reset b/bin/omarchy-system-factory-reset index 43348e5a..c248221c 100755 --- a/bin/omarchy-system-factory-reset +++ b/bin/omarchy-system-factory-reset @@ -29,6 +29,7 @@ set -euo pipefail PROVISIONING_DIR=/var/lib/omarchy/provisioning +PACKAGED_PATH=/usr/bin/omarchy-system-factory-reset OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}" TOP_MNT=/run/omarchy-system-factory-reset/top NEXT_NAME=@omarchy-reset-next @@ -37,9 +38,10 @@ LOG_FILE=/var/log/omarchy-system-factory-reset.log # Self-elevate so the menu entry (and a bare shell invocation) need no sudo # prefix. The caller's gum theme vars are forwarded as `env` arguments rather # than via `sudo -E`, so styling survives even under an env_reset sudoers. +# Always elevate the packaged copy, including when invoked from a dev checkout. if (( EUID != 0 )); then mapfile -t gum_env < <(env | grep '^GUM_' || true) - exec sudo env "${gum_env[@]}" "$0" "$@" + exec sudo env "${gum_env[@]}" "$PACKAGED_PATH" "$@" fi export PATH="$OMARCHY_PATH/bin:$PATH" From 4dd5414bfcd60994b492c1474fe99bbef0e8a5d5 Mon Sep 17 00:00:00 2001 From: Afonso Oliveira Date: Wed, 23 Sep 2026 09:45:24 +0100 Subject: [PATCH 2/4] Test factory-reset elevation paths and arguments --- test/shell.d/factory-reset-elevation-test.sh | 48 ++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100755 test/shell.d/factory-reset-elevation-test.sh diff --git a/test/shell.d/factory-reset-elevation-test.sh b/test/shell.d/factory-reset-elevation-test.sh new file mode 100755 index 00000000..20a894bf --- /dev/null +++ b/test/shell.d/factory-reset-elevation-test.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -euo pipefail +source "$(dirname "$0")/base-test.sh" + +if (( EUID == 0 )); then + skip "factory-reset self-elevation requires an unprivileged caller" + exit 0 +fi + +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT +mkdir -p "$tmp/bin" "$tmp/checkout with spaces" + +# Exercise the real startup, but never include the destructive reset body. +awk ' + /^export PATH=/ { found = 1; print "exit 99"; exit } + { print } + END { if (!found) exit 1 } +' "$ROOT/bin/omarchy-system-factory-reset" >"$tmp/checkout with spaces/reset" +chmod +x "$tmp/checkout with spaces/reset" +ln -s "$tmp/checkout with spaces/reset" "$tmp/reset-link" + +cat >"$tmp/bin/sudo" <<'SH' +#!/bin/bash +printf '%s\0' "$@" >"$CALL_LOG" +exit "${SUDO_STATUS:-0}" +SH +chmod +x "$tmp/bin/sudo" + +args=('argument with spaces' '' '--flag' '$(touch should-not-run)' $'two\nlines') +expected=(env 'GUM_INPUT_PROMPT=Reset this computer? ' /usr/bin/omarchy-system-factory-reset "${args[@]}") +for invocation in "$tmp/checkout with spaces/reset" './checkout with spaces/reset' "$tmp/reset-link"; do + for status in 0 1 127; do + actual_status=0 + (cd "$tmp" && env -i HOME="$tmp" PATH="$tmp/bin:/usr/bin:/bin" \ + OMARCHY_PATH="$tmp/checkout with spaces" PACKAGED_PATH="$tmp/wrong-target" \ + GUM_INPUT_PROMPT='Reset this computer? ' CALL_LOG="$tmp/call" SUDO_STATUS="$status" \ + "$invocation" "${args[@]}") || actual_status=$? + (( actual_status == status )) || fail "elevation status is preserved for $invocation" + mapfile -d '' -t actual <"$tmp/call" + (( ${#actual[@]} == ${#expected[@]} )) || fail "elevation argument count is preserved" + for i in "${!expected[@]}"; do + [[ ${actual[i]} == "${expected[i]}" ]] || fail "elevation argument $i is preserved for $invocation" + done + [[ ! -e $tmp/should-not-run ]] || fail "caller arguments were executed" + done + pass "factory reset pins $invocation to the packaged command and preserves arguments, styling and status" +done From 45a64331394f62b0b853bf5a14805f94e66bdc7c Mon Sep 17 00:00:00 2001 From: Afonso Oliveira Date: Thu, 24 Sep 2026 22:42:46 +0100 Subject: [PATCH 3/4] Refuse factory reset handoff to a different installed version --- bin/omarchy-system-factory-reset | 6 ++++++ docs/file-layout.md | 2 ++ 2 files changed, 8 insertions(+) diff --git a/bin/omarchy-system-factory-reset b/bin/omarchy-system-factory-reset index c248221c..325475f7 100755 --- a/bin/omarchy-system-factory-reset +++ b/bin/omarchy-system-factory-reset @@ -35,6 +35,12 @@ TOP_MNT=/run/omarchy-system-factory-reset/top NEXT_NAME=@omarchy-reset-next LOG_FILE=/var/log/omarchy-system-factory-reset.log +# Never replace a newer checkout reset with a different installed implementation. +if ! /usr/bin/cmp -s -- "${BASH_SOURCE[0]}" "$PACKAGED_PATH"; then + echo "Factory reset differs from the installed command. Install the matching Omarchy package before resetting." >&2 + exit 1 +fi + # Self-elevate so the menu entry (and a bare shell invocation) need no sudo # prefix. The caller's gum theme vars are forwarded as `env` arguments rather # than via `sudo -E`, so styling survives even under an env_reset sudoers. diff --git a/docs/file-layout.md b/docs/file-layout.md index 73c48c4f..19e2bfb5 100644 --- a/docs/file-layout.md +++ b/docs/file-layout.md @@ -201,6 +201,8 @@ yet and silently runs the packaged copy of one it has. The drop-in is validated with `visudo -c` before install and removed by `omarchy-dev-unlink`; unlike `/etc/omarchy.conf`, it takes effect without a reboot. +Factory reset is an exception: it always self-elevates through `/usr/bin/omarchy-system-factory-reset` and refuses a checkout copy that differs from the installed command, including when invoked with `sudo`. Install the matching package before resetting so a newer checkout cannot silently hand off to older account-scrubbing code. + ## Runtime finalization (`omarchy-provision-user`) Runs once per user. It does **not** copy `~/.config/**`, `~/.bashrc`, From 99e3c8ca2f17a89b1e34090d4dee9c59d61a00a4 Mon Sep 17 00:00:00 2001 From: Afonso Oliveira Date: Thu, 24 Sep 2026 22:42:46 +0100 Subject: [PATCH 4/4] Test factory reset version mismatches and packaged invocation --- test/shell.d/factory-reset-elevation-test.sh | 36 +++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/test/shell.d/factory-reset-elevation-test.sh b/test/shell.d/factory-reset-elevation-test.sh index 20a894bf..da47d88f 100755 --- a/test/shell.d/factory-reset-elevation-test.sh +++ b/test/shell.d/factory-reset-elevation-test.sh @@ -17,7 +17,10 @@ awk ' { print } END { if (!found) exit 1 } ' "$ROOT/bin/omarchy-system-factory-reset" >"$tmp/checkout with spaces/reset" -chmod +x "$tmp/checkout with spaces/reset" +grep -Fxq 'PACKAGED_PATH=/usr/bin/omarchy-system-factory-reset' "$ROOT/bin/omarchy-system-factory-reset" || fail "factory reset pins the packaged path" +sed -i "s|^PACKAGED_PATH=.*|PACKAGED_PATH=\"$tmp/packaged-reset\"|" "$tmp/checkout with spaces/reset" +cp "$tmp/checkout with spaces/reset" "$tmp/packaged-reset" +chmod +x "$tmp/checkout with spaces/reset" "$tmp/packaged-reset" ln -s "$tmp/checkout with spaces/reset" "$tmp/reset-link" cat >"$tmp/bin/sudo" <<'SH' @@ -28,11 +31,11 @@ SH chmod +x "$tmp/bin/sudo" args=('argument with spaces' '' '--flag' '$(touch should-not-run)' $'two\nlines') -expected=(env 'GUM_INPUT_PROMPT=Reset this computer? ' /usr/bin/omarchy-system-factory-reset "${args[@]}") -for invocation in "$tmp/checkout with spaces/reset" './checkout with spaces/reset' "$tmp/reset-link"; do +expected=(env 'GUM_INPUT_PROMPT=Reset this computer? ' "$tmp/packaged-reset" "${args[@]}") +for invocation in "$tmp/checkout with spaces/reset" './checkout with spaces/reset' "$tmp/reset-link" "$tmp/packaged-reset" reset; do for status in 0 1 127; do actual_status=0 - (cd "$tmp" && env -i HOME="$tmp" PATH="$tmp/bin:/usr/bin:/bin" \ + (cd "$tmp" && env -i HOME="$tmp" PATH="$tmp/bin:$tmp/checkout with spaces:/usr/bin:/bin" \ OMARCHY_PATH="$tmp/checkout with spaces" PACKAGED_PATH="$tmp/wrong-target" \ GUM_INPUT_PROMPT='Reset this computer? ' CALL_LOG="$tmp/call" SUDO_STATUS="$status" \ "$invocation" "${args[@]}") || actual_status=$? @@ -46,3 +49,28 @@ for invocation in "$tmp/checkout with spaces/reset" './checkout with spaces/rese done pass "factory reset pins $invocation to the packaged command and preserves arguments, styling and status" done + +if unshare -Ur true >/dev/null 2>&1; then + status=0 + unshare -Ur "$tmp/packaged-reset" || status=$? + (( status == 99 )) || fail "matching root invocation did not reach the guarded body" + pass "matching packaged root invocation reaches the reset body" +else + skip "user namespaces unavailable; root invocation coverage" +fi + +# A checkout must not hand off to older installed code, even with explicit sudo. +printf '\n# older packaged copy\n' >>"$tmp/packaged-reset" +rm -f "$tmp/call" +if CALL_LOG="$tmp/call" PATH="$tmp/bin:/usr/bin:/bin" "$tmp/reset-link" >"$tmp/mismatch" 2>&1; then + fail "mismatched reset implementation was accepted" +fi +[[ ! -e $tmp/call ]] || fail "mismatched reset requested elevation" +grep -Fq 'Install the matching Omarchy package' "$tmp/mismatch" || fail "mismatch explains recovery" +if unshare -Ur true >/dev/null 2>&1; then + status=0 + unshare -Ur "$tmp/reset-link" >"$tmp/root-mismatch" 2>&1 || status=$? + (( status == 1 )) || fail "root invocation did not refuse mismatch before the reset body" + pass "root checkout invocation refuses a mismatched packaged reset" +fi +pass "mismatched checkout refuses before elevation"