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
4 changes: 3 additions & 1 deletion bin/omarchy
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ register_route() {
register_command() {
local file="$1"
local file_binary="${file##*/}"
local metadata_file="$OMARCHY_BIN_DIR/../share/omarchy/command-metadata/$file_binary"
local group=""
local name=""
local summary=""
Expand All @@ -187,6 +188,7 @@ register_command() {
local has_summary="false"
local metadata_errors=""

[[ -f $metadata_file && -r $metadata_file ]] || metadata_file="$file"
while IFS= read -r line && (( line_count < METADATA_SCAN_LIMIT )); do
line_count=$((line_count + 1))

Expand Down Expand Up @@ -249,7 +251,7 @@ register_command() {
fallback_summary=""
fi
fi
done <"$file"
done <"$metadata_file"

local stem="${file_binary#omarchy-}"
local fallback_group="$stem"
Expand Down
139 changes: 81 additions & 58 deletions bin/omarchy-debug
Original file line number Diff line number Diff line change
@@ -1,40 +1,59 @@
#!/bin/bash
#!/bin/bash -p

# omarchy:summary=Print debugging information
# omarchy:args=[--no-sudo] [--print]
# omarchy:examples=omarchy debug --print --no-sudo
# omarchy:requires-sudo=true

NO_SUDO=false
PRINT_ONLY=false
# The settings package installs a root-owned, non-setuid static launcher as
# /usr/bin/omarchy-debug and this Bash payload at a fixed /usr/lib path. The
# launcher is the first process: it cold-revokes sudo, performs only the fixed
# `sudo -N dmesg` operation, revokes again, then starts this collector with a
# sanitized shell. Running this source payload directly is unsupported and
# cannot reach a sudo operation because the collector itself contains none.
if [[ ${1:-} == "--omarchy-debug-native-boundary-v1" &&
${OMARCHY_DEBUG_NATIVE_BOUNDARY:-} == 1 && -r /proc/self/fd/3 ]]; then
shift
DMESG_OUTPUT=$(/usr/bin/cat <&3) || {
echo "Could not read the private kernel log staging descriptor." >&2
exit 1
}
exec 3<&-
exec 4<&-

while (( $# > 0 )); do
case "$1" in
--no-sudo)
NO_SUDO=true
shift
;;
--print)
PRINT_ONLY=true
shift
;;
*)
echo "Unknown option: $1"
echo "Usage: omarchy-debug [--no-sudo] [--print]"
exit 1
;;
esac
done
set -uo pipefail

LOG_FILE="/tmp/omarchy-debug.log"
NO_SUDO=false
PRINT_ONLY=false

if [[ $NO_SUDO = "true" ]]; then
DMESG_OUTPUT="(skipped - --no-sudo flag used)"
else
DMESG_OUTPUT="$(sudo dmesg)"
fi
while (( $# > 0 )); do
case "$1" in
--no-sudo)
NO_SUDO=true
shift
;;
--print)
PRINT_ONLY=true
shift
;;
*)
echo "Unknown option: $1"
echo "Usage: omarchy-debug [--no-sudo] [--print]"
exit 1
;;
esac
done

cat > "$LOG_FILE" <<EOF
# OM-SEC-06 owns this private staging change in #8370. Keep its -T guard so a
# directory at the fixed log name is rejected rather than accepted by install.
log_dir="${XDG_RUNTIME_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/omarchy}"
LOG_FILE="$log_dir/omarchy-debug.log"
if ! /usr/bin/mkdir -p "$log_dir" || ! /usr/bin/install -T -m 600 /dev/null "$LOG_FILE"; then
echo "Error: Failed to create $LOG_FILE" >&2
exit 1
fi

cat >"$LOG_FILE" <<EOF
Date: $(date)
Hostname: $(hostname)
Omarchy Package: $(pacman -Q omarchy-dev 2>/dev/null || pacman -Q omarchy 2>/dev/null || echo "unknown")
Expand All @@ -60,37 +79,41 @@ INSTALLED PACKAGES
$({ expac -S '%n %v (%r)' $(pacman -Qqe) 2>/dev/null; comm -13 <(pacman -Sql | sort) <(pacman -Qqe | sort) | xargs -r expac -Q '%n %v (AUR)'; } | sort)
EOF

if [[ $PRINT_ONLY = "true" ]]; then
cat "$LOG_FILE"
exit 0
fi
if [[ $PRINT_ONLY == true ]]; then
cat "$LOG_FILE"
exit 0
fi

OPTIONS=("View log" "Save in current directory")
if ping -c 1 8.8.8.8 >/dev/null 2>&1; then
OPTIONS=("Upload log" "${OPTIONS[@]}")
fi
OPTIONS=("View log" "Save in current directory")
if ping -c 1 8.8.8.8 >/dev/null 2>&1; then
OPTIONS=("Upload log" "${OPTIONS[@]}")
fi

ACTION=$(gum choose "${OPTIONS[@]}")
ACTION=$(gum choose "${OPTIONS[@]}")

case "$ACTION" in
"Upload log")
echo "Uploading debug log to logs.omarchy.org..."
URL=$(curl -sf -F "file=@$LOG_FILE" -Fexpires=24 https://logs.omarchy.org/)
if (( $? == 0 )) && [[ -n $URL ]]; then
echo "✓ Log uploaded successfully!"
echo "Share this URL:"
echo ""
echo " $URL"
else
echo "Error: Failed to upload log file"
exit 1
fi
;;
"View log")
less "$LOG_FILE"
;;
"Save in current directory")
cp "$LOG_FILE" "./omarchy-debug.log"
echo "✓ Log saved to $(pwd)/omarchy-debug.log"
;;
esac
case "$ACTION" in
"Upload log")
echo "Uploading debug log to logs.omarchy.org..."
if URL=$(curl -sf -F "file=@$LOG_FILE" -Fexpires=24 https://logs.omarchy.org/) && [[ -n $URL ]]; then
echo "✓ Log uploaded successfully!"
echo "Share this URL:"
echo ""
echo " $URL"
else
echo "Error: Failed to upload log file"
exit 1
fi
;;
"View log")
less "$LOG_FILE"
;;
"Save in current directory")
cp "$LOG_FILE" "./omarchy-debug.log"
echo "✓ Log saved to $(pwd)/omarchy-debug.log"
;;
esac
else
echo "Run the packaged omarchy-debug command directly; do not invoke its Bash payload." >&2
return 126 2>/dev/null
exit 126
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