Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Three documentation trees, split by genre and audience:
- Prefer `(( ))` over numeric operators inside `[[ ]]` (e.g., `(( count < 50 ))`, not `[[ $count -lt 50 ]]`)
- Prefer a full `if`/`else` conditional for simple two-path control flow; don't rely on `exec` or `exit` in one branch to make following statements unreachable
- For strings/paths with spaces, quote them instead of escaping spaces with `\ ` (e.g., `"$APP_DIR/Disk Usage.desktop"`, not `$APP_DIR/Disk\ Usage.desktop`)
- Shebangs must use `#!/bin/bash` consistently (never `#!/usr/bin/env bash`)
- Shebangs must use `#!/bin/bash` consistently (never `#!/usr/bin/env bash`). A security-sensitive entrypoint may use the exact `#!/bin/bash -p` form only when it must suppress `BASH_ENV` and exported-function startup injection before its first command; that exception must be explained at the boundary and covered by a regression that rejects an ordinary Bash launch with a decoy `-p` argument.
- Scripts under `install/` and `migrations/` may be sourced and intentionally omit shebangs

# Command Naming
Expand Down
69 changes: 61 additions & 8 deletions bin/omarchy-pkg-install
Original file line number Diff line number Diff line change
@@ -1,11 +1,39 @@
#!/bin/bash
#!/bin/bash -p

# omarchy:summary=Show a fuzzy-finder TUI for picking new Arch and OPR packages to install.
# omarchy:requires-sudo=true

if [[ $- != *p* ]]; then
echo "Refusing an unsafe Bash startup." >&2
exit 126
fi

security_entrypoint=$(/usr/bin/readlink -e -- "${BASH_SOURCE[0]}") || exit 126
source "${security_entrypoint%/*}/omarchy-security-functions" || exit 126
omarchy_security_require_privileged_bash_startup || exit 126
set -e
omarchy_security_sanitize_bash_environment "$0" "$@"
omarchy_security_require_source_root "$0"

set -uo pipefail
PATH=/usr/bin:/usr/sbin:/bin:/sbin
export PATH
unset BASH_ENV ENV CDPATH GLOBIGNORE

omarchy_security_install_sudo_cleanup_traps "Failed to invalidate sudo credentials after the package picker."

if ! omarchy_security_revoke_sudo_timestamp; then
echo "Unable to start package discovery with a cold sudo credential state." >&2
exit 1
fi
if ! omarchy_security_sudo_supports_no_update; then
echo "This sudo does not support --no-update; refusing to run the package picker." >&2
exit 1
fi

fzf_args=(
--multi
--preview 'pacman -Sii {1}'
--preview '/usr/bin/pacman -Sii -- {1}'
--preview-label='alt-p: toggle description, alt-j/k: scroll, tab: multi-select'
--preview-label-pos='bottom'
--preview-window 'down:65%:wrap'
Expand All @@ -15,12 +43,37 @@ fzf_args=(
--color 'pointer:green,marker:green'
)

pkg_names=$(pacman -Slq | fzf "${fzf_args[@]}")
set +e
package_candidates=$(/usr/bin/pacman -Slq)
query_status=$?
set -e
if (( query_status != 0 )); then
echo "Package discovery failed." >&2
exit "$query_status"
fi

if [[ -n $pkg_names ]]; then
source omarchy-sudo-keepalive
set +e
pkg_names=$(printf '%s' "$package_candidates" | /usr/bin/fzf "${fzf_args[@]}")
picker_status=$?
set -e
case "$picker_status" in
0) ;;
1) pkg_names="" ;;
130) exit 0 ;;
*)
echo "Package discovery failed." >&2
exit "$picker_status"
;;
esac

# Convert newline-separated selections to space-separated for pacman
echo "$pkg_names" | tr '\n' ' ' | xargs sudo pacman -S --noconfirm
omarchy-show-done
if [[ -n $pkg_names ]]; then
mapfile -t packages <<<"$pkg_names"
for package in "${packages[@]}"; do
[[ $package =~ ^[a-z0-9][a-z0-9@._+:-]*$ ]] || {
echo "Invalid package selection: $package" >&2
exit 2
}
done
/usr/bin/sudo -N -- /usr/bin/pacman -S --noconfirm -- "${packages[@]}"
/usr/bin/omarchy-show-done
fi
67 changes: 61 additions & 6 deletions bin/omarchy-pkg-remove
Original file line number Diff line number Diff line change
@@ -1,11 +1,39 @@
#!/bin/bash
#!/bin/bash -p

# omarchy:summary=Show a fuzzy-finder TUI for picking packages installed on the system to be removed.
# omarchy:requires-sudo=true

if [[ $- != *p* ]]; then
echo "Refusing an unsafe Bash startup." >&2
exit 126
fi

security_entrypoint=$(/usr/bin/readlink -e -- "${BASH_SOURCE[0]}") || exit 126
source "${security_entrypoint%/*}/omarchy-security-functions" || exit 126
omarchy_security_require_privileged_bash_startup || exit 126
set -e
omarchy_security_sanitize_bash_environment "$0" "$@"
omarchy_security_require_source_root "$0"

set -uo pipefail
PATH=/usr/bin:/usr/sbin:/bin:/sbin
export PATH
unset BASH_ENV ENV CDPATH GLOBIGNORE

omarchy_security_install_sudo_cleanup_traps "Failed to invalidate sudo credentials after the package remover."

if ! omarchy_security_revoke_sudo_timestamp; then
echo "Unable to start package discovery with a cold sudo credential state." >&2
exit 1
fi
if ! omarchy_security_sudo_supports_no_update; then
echo "This sudo does not support --no-update; refusing to run the package remover." >&2
exit 1
fi

fzf_args=(
--multi
--preview 'yay -Qi {1}'
--preview '/usr/bin/yay -Qi -- {1}'
--preview-label='alt-p: toggle description, alt-j/k: scroll, tab: multi-select'
--preview-label-pos='bottom'
--preview-window 'down:65%:wrap'
Expand All @@ -15,10 +43,37 @@ fzf_args=(
--color 'pointer:red,marker:red'
)

pkg_names=$(yay -Qqe | fzf "${fzf_args[@]}")
set +e
package_candidates=$(/usr/bin/yay -Qqe)
query_status=$?
set -e
if (( query_status != 0 )); then
echo "Package discovery failed." >&2
exit "$query_status"
fi

set +e
pkg_names=$(printf '%s' "$package_candidates" | /usr/bin/fzf "${fzf_args[@]}")
picker_status=$?
set -e
case "$picker_status" in
0) ;;
1) pkg_names="" ;;
130) exit 0 ;;
*)
echo "Package discovery failed." >&2
exit "$picker_status"
;;
esac

if [[ -n $pkg_names ]]; then
# Convert newline-separated selections to space-separated for yay
echo "$pkg_names" | tr '\n' ' ' | xargs sudo pacman -Rns --noconfirm
omarchy-show-done
mapfile -t packages <<<"$pkg_names"
for package in "${packages[@]}"; do
[[ $package =~ ^[a-z0-9][a-z0-9@._+:-]*$ ]] || {
echo "Invalid package selection: $package" >&2
exit 2
}
done
/usr/bin/sudo -N -- /usr/bin/pacman -Rns --noconfirm -- "${packages[@]}"
/usr/bin/omarchy-show-done
fi
137 changes: 137 additions & 0 deletions bin/omarchy-security-functions
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/bin/bash

# omarchy:hidden=true
# omarchy:summary=Provide internal helpers for command-scoped sudo authentication

if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
echo "omarchy-security-functions is an internal function library." >&2
exit 64
fi

omarchy_security_require_privileged_bash_startup() {
[[ $- == *p* ]] || return 1
/usr/bin/env -i /usr/bin/bash -p -c '
mapfile -d "" -t argv <"/proc/$1/cmdline" || exit 1
executable=$(/usr/bin/readlink -e -- "/proc/$1/exe") || exit 1
[[ $executable == "/usr/bin/bash" &&
( ${argv[0]:-} == "/bin/bash" || ${argv[0]:-} == "/usr/bin/bash" ) &&
${argv[1]:-} == "-p" ]]
' omarchy-bash-startup "$$"
}

omarchy_security_sanitize_bash_environment() {
local script=$1
shift
local entry name environment_fd environment_pid
local -a unsets=()

# Read the raw environment: privileged Bash ignores exported functions, but
# leaves their records for ordinary child interpreters to import later.
exec {environment_fd}< <(/usr/bin/env -0)
environment_pid=$!
while IFS= read -r -d '' entry <&"$environment_fd"; do
name=${entry%%=*}
case "$name" in
BASH_ENV|ENV|SHELLOPTS|BASHOPTS|PS4|CDPATH|GLOBIGNORE|BASH_FUNC_*%%)
unsets+=(-u "$name")
;;
esac
done
exec {environment_fd}<&-
wait "$environment_pid" || return 1
if (( ${#unsets[@]} > 0 )); then
exec /usr/bin/env "${unsets[@]}" /usr/bin/bash -p -- "$script" "$@"
fi
}

omarchy_security_require_source_root() {
local command_source command_name=${1##*/}
command_source=$(/usr/bin/readlink -e -- "$1") || return 1

# A runtime root selects the code used by this invocation. Accept the
# canonical checkout containing the entrypoint or the package's bin links.
if [[ ${OMARCHY_PATH:-} != /* || $(/usr/bin/realpath -e -- "$OMARCHY_PATH") != "$OMARCHY_PATH" ]] ||
! { [[ $command_source == "$OMARCHY_PATH/bin/$command_name" ]] ||
[[ $OMARCHY_PATH == "/usr/share/omarchy" && $command_source == "/usr/bin/$command_name" ]]; }; then
echo "OMARCHY_PATH does not match this Omarchy command." >&2
return 1
fi
}

omarchy_security_sudo_supports_no_update() {
local help
help=$(LC_ALL=C /usr/bin/sudo -h 2>&1) || return 1
/usr/bin/grep -Eq '^usage: sudo .*\[[^]]*N[^]]*\]' <<< "$help"
}

omarchy_security_revoke_sudo_timestamp() {
/usr/bin/sudo -k
}

omarchy_security_exit_with_revoked_sudo() {
local status=$1
local message=${2:-Could not invalidate cached sudo authorization.}

trap - EXIT HUP INT TERM
if ! omarchy_security_revoke_sudo_timestamp; then
echo "$message" >&2
(( status != 0 )) || status=1
fi
exit "$status"
}

omarchy_security_install_signal_exit_traps() {
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
}

omarchy_security_install_sudo_cleanup_traps() {
OMARCHY_SECURITY_SUDO_CLEANUP_MESSAGE=${1:-Could not invalidate cached sudo authorization.}
trap omarchy_security_run_sudo_cleanup_trap EXIT
omarchy_security_install_signal_exit_traps
}

omarchy_security_enable_no_update_sudo() {
local wrapper_dir="$OMARCHY_PATH/default/omarchy/sudo-no-update"
if ! omarchy_security_sudo_supports_no_update; then
echo "This sudo does not support --no-update; refusing mixed-trust work." >&2
return 1
fi
if [[ ! -f $wrapper_dir/sudo || ! -x $wrapper_dir/sudo ]]; then
echo "The command-scoped sudo wrapper is missing." >&2
return 1
fi
PATH="$wrapper_dir:$OMARCHY_PATH/bin:/usr/bin:/usr/sbin:/bin:/sbin"
OMARCHY_SUDO_NO_UPDATE=1
export PATH OMARCHY_SUDO_NO_UPDATE
}

omarchy_security_run_sudo_cleanup_trap() {
local status=$?

omarchy_security_exit_with_revoked_sudo "$status" \
"${OMARCHY_SECURITY_SUDO_CLEANUP_MESSAGE:-Could not invalidate cached sudo authorization.}"
}

omarchy_security_assert_root_directory() {
local path=$1 expected_mode=$2 canonical owner actual_mode

[[ $path == /* && -d $path && ! -L $path ]] || return 1
canonical=$(/usr/bin/realpath -e -- "$path") || return 1
[[ $canonical == "$path" ]] || return 1
read -r owner actual_mode < <(/usr/bin/stat -Lc '%u %a' -- "$path") || return 1
[[ $owner == "0" && $actual_mode == "$expected_mode" ]]
}

omarchy_security_prepare_private_root_directory() {
local path=$1 parent=$2

omarchy_security_assert_root_directory "$parent" 755 || return 1
if [[ -e $path || -L $path ]]; then
omarchy_security_assert_root_directory "$path" 700
else
/usr/bin/install -d -o root -g root -m 0700 -- "$path" || return 1
omarchy_security_assert_root_directory "$path" 700
fi
}
Loading