Write browser theme colour through a passwordless helper
Managed policy dirs are enterprise trust roots, so they stay 0755 root:root. The menu path takes root for that one write through a sudoers glob of six hex digits, the same shape as omarchy-dns, and falls back to pkexec where the grant is not installed. Drop omarchy-browser-policy; a group member could plant any JSON, not just a colour.
This commit is contained in:
@@ -9,8 +9,6 @@ set -e
|
|||||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||||
|
|
||||||
setup_chromium_policy_directory() {
|
setup_chromium_policy_directory() {
|
||||||
browser_policy_setup_group
|
|
||||||
browser_policy_grant_user "${USER:-$(id -un)}"
|
|
||||||
browser_policy_setup_dir "$1"
|
browser_policy_setup_dir "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -744,11 +744,8 @@ create_user() {
|
|||||||
chmod 440 /etc/sudoers.d/00-omarchy-wheel
|
chmod 440 /etc/sudoers.d/00-omarchy-wheel
|
||||||
|
|
||||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||||
OMARCHY_INSTALL_USER=$username
|
|
||||||
OMARCHY_PROVISIONING_DIR=$PROVISIONING_DIR
|
|
||||||
browser_policy_setup_group
|
|
||||||
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
||||||
[[ -d $dir ]] || continue
|
[[ -d $dir || -L $dir ]] || continue
|
||||||
browser_policy_setup_dir "$dir"
|
browser_policy_setup_dir "$dir"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,6 @@ if [[ -f $CHROMIUM_THEME ]]; then
|
|||||||
THEME_HEX_COLOR=$(browser_policy_theme_hex "$(<$CHROMIUM_THEME)")
|
THEME_HEX_COLOR=$(browser_policy_theme_hex "$(<$CHROMIUM_THEME)")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
set_browser_policy() {
|
|
||||||
browser_policy_write_color "$1" "$THEME_HEX_COLOR"
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh_running_browser() {
|
refresh_running_browser() {
|
||||||
local process="$1"
|
local process="$1"
|
||||||
local command="$2"
|
local command="$2"
|
||||||
@@ -27,16 +23,11 @@ refresh_running_browser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
failed=0
|
failed=0
|
||||||
set_browser_policy /etc/chromium/policies/managed || failed=1
|
omarchy-theme-set-browser-policy "${THEME_HEX_COLOR#\#}" || failed=1
|
||||||
|
|
||||||
refresh_running_browser chromium chromium
|
refresh_running_browser chromium chromium
|
||||||
|
|
||||||
set_browser_policy /etc/opt/chrome/policies/managed || failed=1
|
|
||||||
refresh_running_browser chrome google-chrome-stable || refresh_running_browser chrome google-chrome
|
refresh_running_browser chrome google-chrome-stable || refresh_running_browser chrome google-chrome
|
||||||
|
|
||||||
set_browser_policy /etc/opt/edge/policies/managed || failed=1
|
|
||||||
refresh_running_browser msedge microsoft-edge-stable
|
refresh_running_browser msedge microsoft-edge-stable
|
||||||
|
|
||||||
set_browser_policy /etc/brave/policies/managed || failed=1
|
|
||||||
refresh_running_browser brave brave
|
refresh_running_browser brave brave
|
||||||
# Match on the binary path: the running process is named plain "brave", and a
|
# Match on the binary path: the running process is named plain "brave", and a
|
||||||
# bare -f brave-origin pattern would also match the installer's own terminal.
|
# bare -f brave-origin pattern would also match the installer's own terminal.
|
||||||
|
|||||||
Executable
+118
@@ -0,0 +1,118 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Write the current theme color into the browser policy directories
|
||||||
|
# omarchy:args=<rrggbb>
|
||||||
|
# omarchy:hidden=true
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Whenever this runs as root — invoked directly through the passwordless
|
||||||
|
# sudoers rule, or re-execed by require_root below — sudo's secure_path decides
|
||||||
|
# where a bare helper resolves, and a dev link (etc/sudoers.d/omarchy-dev-path)
|
||||||
|
# prepends a user-writable checkout bin/ to it. Every helper this script calls
|
||||||
|
# by bare name (printf's builtin aside: install, mktemp, rm) is a system tool,
|
||||||
|
# never an omarchy-* command, so pin PATH to trusted system directories and keep
|
||||||
|
# root from resolving one out of that checkout. The unprivileged wrapper phase
|
||||||
|
# keeps the caller's PATH so it can still find sudo/pkexec.
|
||||||
|
if (( EUID == 0 )); then
|
||||||
|
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enterprise policy trust roots. The list is fixed here rather than taken from
|
||||||
|
# the caller: the caller chooses a color, never a path.
|
||||||
|
POLICY_DIRS=(
|
||||||
|
/etc/chromium/policies/managed
|
||||||
|
/etc/opt/chrome/policies/managed
|
||||||
|
/etc/opt/edge/policies/managed
|
||||||
|
/etc/brave/policies/managed
|
||||||
|
)
|
||||||
|
|
||||||
|
# The path etc/sudoers.d/omarchy-theme-browser names. The privileged half always
|
||||||
|
# runs from there rather than from whichever copy was invoked, so the rule
|
||||||
|
# matches even where $OMARCHY_PATH points at a checkout.
|
||||||
|
PACKAGED_PATH=/usr/bin/omarchy-theme-set-browser-policy
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: omarchy-theme-set-browser-policy <rrggbb>" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( $# != 1 )); then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
color="$1"
|
||||||
|
|
||||||
|
# Six lowercase hex digits is the whole of what this accepts. The leading "#"
|
||||||
|
# is added when the JSON is written rather than passed in: "#" opens a comment
|
||||||
|
# in sudoers, and keeping it out of argv lets the sudoers rule spell the
|
||||||
|
# argument as a plain six-character glob.
|
||||||
|
if [[ ! $color =~ ^[0-9a-f]{6}$ ]]; then
|
||||||
|
echo "omarchy-theme-set-browser-policy: expected six lowercase hex digits, got '$color'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# True when sudo would run this exact command without stopping for a password.
|
||||||
|
# `sudo -l` on its own reports whether a command is permitted, which the blanket
|
||||||
|
# %wheel rule answers yes to for everything; the long listing prints the matched
|
||||||
|
# entry's tags, so !authenticate is the grant in
|
||||||
|
# etc/sudoers.d/omarchy-theme-browser and nothing else. Listing runs nothing
|
||||||
|
# and, under -n, prompts for nothing.
|
||||||
|
sudo_grants_passwordless() {
|
||||||
|
sudo -n -l -l "$PACKAGED_PATH" "$@" 2>/dev/null | grep -q '!authenticate'
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
if (( EUID == 0 )); then
|
||||||
|
return
|
||||||
|
elif [[ -t 0 ]] || sudo_grants_passwordless "$@"; then
|
||||||
|
exec sudo "$PACKAGED_PATH" "$@"
|
||||||
|
else
|
||||||
|
exec pkexec "$PACKAGED_PATH" "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root "$color"
|
||||||
|
|
||||||
|
failed=0
|
||||||
|
staged=""
|
||||||
|
cleanup() {
|
||||||
|
[[ -n $staged ]] && rm -f "$staged"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
for policy_dir in "${POLICY_DIRS[@]}"; do
|
||||||
|
# Only browsers Omarchy has installed have a policy directory. Creating one
|
||||||
|
# here would hand a browser a managed-policy root it does not otherwise have.
|
||||||
|
[[ -d $policy_dir && ! -L $policy_dir ]] || continue
|
||||||
|
|
||||||
|
dest=$policy_dir/color.json
|
||||||
|
staged=$(mktemp) || {
|
||||||
|
failed=1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
printf '{"BrowserThemeColor": "#%s", "BrowserColorScheme": "device"}\n' "$color" >"$staged"
|
||||||
|
|
||||||
|
if [[ -L $dest || -d $dest ]]; then
|
||||||
|
if ! rm -rf -- "$dest"; then
|
||||||
|
rm -f "$staged"
|
||||||
|
staged=""
|
||||||
|
echo "omarchy-theme-set-browser-policy: cannot replace $dest" >&2
|
||||||
|
failed=1
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! install -m 0644 -o root -g root -T "$staged" "$dest"; then
|
||||||
|
rm -f "$staged"
|
||||||
|
staged=""
|
||||||
|
echo "omarchy-theme-set-browser-policy: cannot write $dest" >&2
|
||||||
|
failed=1
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$staged"
|
||||||
|
staged=""
|
||||||
|
done
|
||||||
|
|
||||||
|
exit "$failed"
|
||||||
@@ -1316,14 +1316,13 @@ apply_system_transition() {
|
|||||||
if ! as_root test -f "$browser_policy_helper"; then
|
if ! as_root test -f "$browser_policy_helper"; then
|
||||||
warn "$browser_policy_helper is unavailable; Chromium policy directories were not hardened."
|
warn "$browser_policy_helper is unavailable; Chromium policy directories were not hardened."
|
||||||
else
|
else
|
||||||
as_root env OMARCHY_PATH=/usr/share/omarchy OMARCHY_INSTALL_USER="$target_user" \
|
as_root env OMARCHY_PATH=/usr/share/omarchy \
|
||||||
bash -euo pipefail -c '
|
bash -euo pipefail -c '
|
||||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||||
browser_policy_setup_group
|
|
||||||
browser_policy_setup_dir /etc/chromium/policies/managed
|
browser_policy_setup_dir /etc/chromium/policies/managed
|
||||||
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
||||||
[[ $dir == "/etc/chromium/policies/managed" ]] && continue
|
[[ $dir == "/etc/chromium/policies/managed" ]] && continue
|
||||||
[[ -d $dir ]] || continue
|
[[ -d $dir || -L $dir ]] || continue
|
||||||
browser_policy_setup_dir "$dir"
|
browser_policy_setup_dir "$dir"
|
||||||
done
|
done
|
||||||
'
|
'
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Theme switching is a menu action with no terminal to carry a password prompt,
|
||||||
|
# and it repaints the browser accent on every switch, so this one write must not
|
||||||
|
# stop for a password. The argument is spelled out as six hex digits rather than
|
||||||
|
# a wildcard: the grant covers a color and nothing else, and sudoers matches a
|
||||||
|
# command's arguments exactly, so it cannot be stretched into extra ones. The
|
||||||
|
# helper revalidates the same shape, since the terminal path does not come
|
||||||
|
# through this rule.
|
||||||
|
%wheel ALL=(root) NOPASSWD: /usr/bin/omarchy-theme-set-browser-policy [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||||
browser_policy_setup_group
|
|
||||||
browser_policy_setup_dir /etc/chromium/policies/managed
|
browser_policy_setup_dir /etc/chromium/policies/managed
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
# Chromium-family machine policy is mandatory for every profile. A dedicated
|
# Chromium-family machine policy is mandatory for every profile. Directories
|
||||||
# group at 2775 lets every Omarchy user write color.json and every other uid
|
# stay 0755 root:root; omarchy-theme-set-browser-policy is the privileged
|
||||||
# read; other-write stays off. Setgid so new files inherit the group.
|
# write for color.json.
|
||||||
|
|
||||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/as-root.sh"
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/as-root.sh"
|
||||||
|
|
||||||
BROWSER_POLICY_GROUP=omarchy-browser-policy
|
|
||||||
|
|
||||||
BROWSER_POLICY_MANAGED_DIRS=(
|
BROWSER_POLICY_MANAGED_DIRS=(
|
||||||
/etc/chromium/policies/managed
|
/etc/chromium/policies/managed
|
||||||
/etc/opt/chrome/policies/managed
|
/etc/opt/chrome/policies/managed
|
||||||
@@ -33,47 +31,12 @@ BROWSER_POLICY_FIREFOX_DIRS=(
|
|||||||
|
|
||||||
BROWSER_POLICY_DEFAULT_COLOR="#1c2027"
|
BROWSER_POLICY_DEFAULT_COLOR="#1c2027"
|
||||||
|
|
||||||
browser_policy_setup_group() {
|
|
||||||
local provisioning_dir="${OMARCHY_PROVISIONING_DIR:-/var/lib/omarchy/provisioning}"
|
|
||||||
|
|
||||||
as_root groupadd --system --force "$BROWSER_POLICY_GROUP"
|
|
||||||
as_root mkdir -p "$provisioning_dir"
|
|
||||||
if ! grep -qxF "$BROWSER_POLICY_GROUP" "$provisioning_dir/groups" 2>/dev/null; then
|
|
||||||
printf '%s\n' "$BROWSER_POLICY_GROUP" | as_root tee -a "$provisioning_dir/groups" >/dev/null
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n ${OMARCHY_INSTALL_USER:-} ]] && getent passwd "$OMARCHY_INSTALL_USER" >/dev/null; then
|
|
||||||
as_root usermod -aG "$BROWSER_POLICY_GROUP" "$OMARCHY_INSTALL_USER"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
browser_policy_grant_user() {
|
|
||||||
local user=${1:-}
|
|
||||||
|
|
||||||
if [[ -z $user || $user == "root" ]]; then
|
|
||||||
user=${SUDO_USER:-}
|
|
||||||
fi
|
|
||||||
|
|
||||||
[[ -n $user && $user != "root" ]] || return 0
|
|
||||||
getent passwd "$user" >/dev/null || return 0
|
|
||||||
as_root usermod -aG "$BROWSER_POLICY_GROUP" "$user"
|
|
||||||
}
|
|
||||||
|
|
||||||
browser_policy_purge_dir() {
|
browser_policy_purge_dir() {
|
||||||
local dir=$1
|
local dir=$1
|
||||||
|
|
||||||
as_root find "$dir" -mindepth 1 -maxdepth 1 ! -user root -exec rm -rf -- {} +
|
as_root find "$dir" -mindepth 1 -maxdepth 1 ! -user root -exec rm -rf -- {} +
|
||||||
}
|
}
|
||||||
|
|
||||||
browser_policy_dir_hardened() {
|
|
||||||
local dir=$1
|
|
||||||
|
|
||||||
[[ -d $dir && ! -L $dir ]] || return 1
|
|
||||||
[[ $(stat -c '%a' "$dir") == "2775" ]] || return 1
|
|
||||||
[[ $(stat -c '%U' "$dir") == "root" ]] || return 1
|
|
||||||
[[ $(stat -c '%G' "$dir") == $BROWSER_POLICY_GROUP ]] || return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
browser_policy_parent_hardened() {
|
browser_policy_parent_hardened() {
|
||||||
local dir=$1
|
local dir=$1
|
||||||
|
|
||||||
@@ -82,6 +45,10 @@ browser_policy_parent_hardened() {
|
|||||||
[[ $(stat -c '%U' "$dir") == "root" ]] || return 1
|
[[ $(stat -c '%U' "$dir") == "root" ]] || return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
browser_policy_dir_hardened() {
|
||||||
|
browser_policy_parent_hardened "$1"
|
||||||
|
}
|
||||||
|
|
||||||
browser_policy_parents_hardened() {
|
browser_policy_parents_hardened() {
|
||||||
local dir=$1
|
local dir=$1
|
||||||
local parent
|
local parent
|
||||||
@@ -116,10 +83,7 @@ browser_policy_setup_dir() {
|
|||||||
local dir=$1
|
local dir=$1
|
||||||
|
|
||||||
browser_policy_setup_parents_for "$dir"
|
browser_policy_setup_parents_for "$dir"
|
||||||
if [[ -L $dir || ( -e $dir && ! -d $dir ) ]]; then
|
browser_policy_setup_parent "$dir"
|
||||||
as_root rm -rf -- "$dir"
|
|
||||||
fi
|
|
||||||
as_root install -d -m 2775 -o root -g "$BROWSER_POLICY_GROUP" "$dir"
|
|
||||||
browser_policy_purge_dir "$dir"
|
browser_policy_purge_dir "$dir"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,82 +100,31 @@ browser_policy_theme_hex() {
|
|||||||
printf '%s' "$BROWSER_POLICY_DEFAULT_COLOR"
|
printf '%s' "$BROWSER_POLICY_DEFAULT_COLOR"
|
||||||
}
|
}
|
||||||
|
|
||||||
browser_policy_file_owner() {
|
browser_policy_install_color() {
|
||||||
local user
|
|
||||||
|
|
||||||
if [[ -n ${OMARCHY_INSTALL_USER:-} && $OMARCHY_INSTALL_USER != "root" ]]; then
|
|
||||||
printf '%s\n' "$OMARCHY_INSTALL_USER"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
if [[ -n ${SUDO_USER:-} && $SUDO_USER != "root" ]]; then
|
|
||||||
printf '%s\n' "$SUDO_USER"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
if [[ -n ${PKEXEC_UID:-} ]]; then
|
|
||||||
user=$(getent passwd "$PKEXEC_UID" | cut -d: -f1)
|
|
||||||
if [[ -n $user && $user != "root" ]]; then
|
|
||||||
printf '%s\n' "$user"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
user=${USER:-$(id -un)}
|
|
||||||
if [[ $user != "root" ]]; then
|
|
||||||
printf '%s\n' "$user"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# sudo when this process has a controlling terminal (fd 0 is /dev/null under
|
|
||||||
# `bash -lc cmd &`, but /dev/tty still works). pkexec when it does not.
|
|
||||||
browser_policy_elevate() {
|
|
||||||
if (( EUID == 0 )); then
|
|
||||||
"$@"
|
|
||||||
elif { exec 3</dev/tty; } 2>/dev/null; then
|
|
||||||
exec 3<&-
|
|
||||||
sudo "$@"
|
|
||||||
else
|
|
||||||
pkexec "$@"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
browser_policy_write_color() {
|
|
||||||
local policy_dir=$1
|
local policy_dir=$1
|
||||||
local hex=$2
|
local hex=$2
|
||||||
local dest=$policy_dir/color.json
|
local dest=$policy_dir/color.json
|
||||||
local payload
|
|
||||||
local tmp
|
local tmp
|
||||||
local owner
|
|
||||||
|
|
||||||
[[ -d $policy_dir ]] || return 0
|
[[ -d $policy_dir && ! -L $policy_dir ]] || return 0
|
||||||
|
[[ $hex =~ ^#[0-9a-f]{6}$ ]] || return 1
|
||||||
|
|
||||||
payload=$(printf '{"BrowserThemeColor": "%s", "BrowserColorScheme": "device"}\n' "$hex")
|
|
||||||
tmp=$(mktemp) || return 1
|
tmp=$(mktemp) || return 1
|
||||||
printf '%s' "$payload" >"$tmp"
|
printf '{"BrowserThemeColor": "%s", "BrowserColorScheme": "device"}\n' "$hex" >"$tmp"
|
||||||
|
|
||||||
# A planted symlink or directory must not be written through or into.
|
|
||||||
if [[ -L $dest || -d $dest ]]; then
|
if [[ -L $dest || -d $dest ]]; then
|
||||||
if ! rm -rf -- "$dest" 2>/dev/null; then
|
if ! rm -rf -- "$dest" 2>/dev/null; then
|
||||||
if ! browser_policy_elevate rm -rf -- "$dest"; then
|
rm -f "$tmp"
|
||||||
rm -f "$tmp"
|
return 1
|
||||||
echo "omarchy-theme-set-browser: cannot replace $dest (need group $BROWSER_POLICY_GROUP)" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if install -m 664 -T "$tmp" "$dest" 2>/dev/null; then
|
if install -m 0644 -T "$tmp" "$dest" 2>/dev/null; then
|
||||||
rm -f "$tmp"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
owner=$(browser_policy_file_owner)
|
|
||||||
[[ -n $owner ]] || owner=root
|
|
||||||
if browser_policy_elevate install -m 664 -o "$owner" -g "$BROWSER_POLICY_GROUP" -T "$tmp" "$dest"; then
|
|
||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
echo "omarchy-theme-set-browser: cannot write $dest (need group $BROWSER_POLICY_GROUP)" >&2
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,9 @@ echo "Stop world-writable Chromium and Firefox policy directories"
|
|||||||
|
|
||||||
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||||
|
|
||||||
browser_policy_setup_group
|
|
||||||
browser_policy_grant_user "${USER:-$(id -un)}"
|
|
||||||
|
|
||||||
repaired=0
|
repaired=0
|
||||||
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
||||||
[[ -d $dir || -L $dir ]] || continue
|
[[ -d $dir || -L $dir ]] || continue
|
||||||
if browser_policy_dir_hardened "$dir" && browser_policy_parents_hardened "$dir"; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
browser_policy_setup_dir "$dir"
|
browser_policy_setup_dir "$dir"
|
||||||
repaired=1
|
repaired=1
|
||||||
done
|
done
|
||||||
@@ -21,7 +15,10 @@ fi
|
|||||||
|
|
||||||
for dir in "${BROWSER_POLICY_FIREFOX_DIRS[@]}"; do
|
for dir in "${BROWSER_POLICY_FIREFOX_DIRS[@]}"; do
|
||||||
[[ -d $dir || -L $dir ]] || continue
|
[[ -d $dir || -L $dir ]] || continue
|
||||||
browser_policy_firefox_hardened "$dir" && continue
|
if browser_policy_firefox_hardened "$dir"; then
|
||||||
|
browser_policy_purge_dir "$dir"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
browser_policy_setup_parent "$dir"
|
browser_policy_setup_parent "$dir"
|
||||||
browser_policy_purge_dir "$dir"
|
browser_policy_purge_dir "$dir"
|
||||||
if ! browser_policy_firefox_policy_file_ok "$dir/policies.json"; then
|
if ! browser_policy_firefox_policy_file_ok "$dir/policies.json"; then
|
||||||
|
|||||||
@@ -7,24 +7,6 @@ source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|||||||
test_tmp=$(mktemp -d)
|
test_tmp=$(mktemp -d)
|
||||||
trap 'rm -rf "$test_tmp"' EXIT
|
trap 'rm -rf "$test_tmp"' EXIT
|
||||||
|
|
||||||
mock_bin=$test_tmp/bin
|
|
||||||
mkdir -p "$mock_bin"
|
|
||||||
elev_log=$test_tmp/elev.log
|
|
||||||
cat >"$mock_bin/sudo" <<SH
|
|
||||||
#!/bin/bash
|
|
||||||
printf 'SUDO %s\\n' "\$*" >>"$elev_log"
|
|
||||||
[[ \${OMARCHY_TEST_SUDO_FAIL:-} == 1 ]] && exit 1
|
|
||||||
exit 0
|
|
||||||
SH
|
|
||||||
cat >"$mock_bin/pkexec" <<SH
|
|
||||||
#!/bin/bash
|
|
||||||
printf 'PKEXEC %s\\n' "\$*" >>"$elev_log"
|
|
||||||
[[ \${OMARCHY_TEST_SUDO_FAIL:-} == 1 ]] && exit 1
|
|
||||||
exit 0
|
|
||||||
SH
|
|
||||||
chmod +x "$mock_bin/sudo" "$mock_bin/pkexec"
|
|
||||||
export PATH="$mock_bin:$PATH"
|
|
||||||
: >"$elev_log"
|
|
||||||
export OMARCHY_PATH="$ROOT"
|
export OMARCHY_PATH="$ROOT"
|
||||||
export OMARCHY_PROVISIONING_DIR="$test_tmp/provisioning"
|
export OMARCHY_PROVISIONING_DIR="$test_tmp/provisioning"
|
||||||
|
|
||||||
@@ -55,20 +37,20 @@ unprivileged_as_root() {
|
|||||||
|
|
||||||
write_dir=$test_tmp/writable
|
write_dir=$test_tmp/writable
|
||||||
mkdir -p "$write_dir"
|
mkdir -p "$write_dir"
|
||||||
browser_policy_write_color "$write_dir" "#aabbcc" ||
|
browser_policy_install_color "$write_dir" "#aabbcc" ||
|
||||||
fail "theme colour writes into a writable policy directory"
|
fail "theme colour writes into a writable policy directory"
|
||||||
grep -F '"BrowserThemeColor": "#aabbcc"' "$write_dir/color.json" >/dev/null ||
|
grep -F '"BrowserThemeColor": "#aabbcc"' "$write_dir/color.json" >/dev/null ||
|
||||||
fail "theme colour writes BrowserThemeColor"
|
fail "theme colour writes BrowserThemeColor"
|
||||||
mode=$(stat -c '%a' "$write_dir/color.json")
|
mode=$(stat -c '%a' "$write_dir/color.json")
|
||||||
[[ $mode == "664" ]] || fail "theme colour creates a group-writable policy file" "mode=$mode"
|
[[ $mode == "644" ]] || fail "theme colour creates a root-mode policy file" "mode=$mode"
|
||||||
pass "theme colour writes a group-writable color.json"
|
pass "theme colour writes a 0644 color.json"
|
||||||
|
|
||||||
if (( EUID == 0 )); then
|
if (( EUID == 0 )); then
|
||||||
pass "running as root; skipping the mktemp-failure check"
|
pass "running as root; skipping the mktemp-failure check"
|
||||||
else
|
else
|
||||||
chmod u+w "$write_dir"
|
chmod u+w "$write_dir"
|
||||||
export TMPDIR=$test_tmp/missing-tmp
|
export TMPDIR=$test_tmp/missing-tmp
|
||||||
if browser_policy_write_color "$write_dir" "#dead00" 2>/dev/null; then
|
if browser_policy_install_color "$write_dir" "#dead00" 2>/dev/null; then
|
||||||
fail "theme colour fails when mktemp cannot create a file"
|
fail "theme colour fails when mktemp cannot create a file"
|
||||||
fi
|
fi
|
||||||
unset TMPDIR
|
unset TMPDIR
|
||||||
@@ -80,7 +62,7 @@ fi
|
|||||||
printf 'original\n' >"$test_tmp/pwn"
|
printf 'original\n' >"$test_tmp/pwn"
|
||||||
rm -f "$write_dir/color.json"
|
rm -f "$write_dir/color.json"
|
||||||
ln -s "$test_tmp/pwn" "$write_dir/color.json"
|
ln -s "$test_tmp/pwn" "$write_dir/color.json"
|
||||||
browser_policy_write_color "$write_dir" "#aabbcc" ||
|
browser_policy_install_color "$write_dir" "#aabbcc" ||
|
||||||
fail "theme colour replaces a planted color.json symlink"
|
fail "theme colour replaces a planted color.json symlink"
|
||||||
[[ -f $write_dir/color.json && ! -L $write_dir/color.json ]] ||
|
[[ -f $write_dir/color.json && ! -L $write_dir/color.json ]] ||
|
||||||
fail "theme colour unlinks a planted color.json symlink instead of writing through it"
|
fail "theme colour unlinks a planted color.json symlink instead of writing through it"
|
||||||
@@ -90,62 +72,25 @@ pass "theme colour does not follow a planted color.json symlink"
|
|||||||
plant_write=$test_tmp/plant-dir
|
plant_write=$test_tmp/plant-dir
|
||||||
mkdir -p "$plant_write/color.json/nested"
|
mkdir -p "$plant_write/color.json/nested"
|
||||||
printf 'inside\n' >"$plant_write/color.json/nested/x"
|
printf 'inside\n' >"$plant_write/color.json/nested/x"
|
||||||
browser_policy_write_color "$plant_write" "#aabbcc" ||
|
browser_policy_install_color "$plant_write" "#aabbcc" ||
|
||||||
fail "theme colour replaces a planted color.json directory"
|
fail "theme colour replaces a planted color.json directory"
|
||||||
[[ -f $plant_write/color.json && ! -d $plant_write/color.json ]] ||
|
[[ -f $plant_write/color.json && ! -d $plant_write/color.json ]] ||
|
||||||
fail "theme colour does not write into a planted color.json directory"
|
fail "theme colour does not write into a planted color.json directory"
|
||||||
pass "theme colour does not write into a planted color.json directory"
|
pass "theme colour does not write into a planted color.json directory"
|
||||||
|
|
||||||
missing_dir=$test_tmp/missing
|
missing_dir=$test_tmp/missing
|
||||||
browser_policy_write_color "$missing_dir" "#aabbcc" ||
|
browser_policy_install_color "$missing_dir" "#aabbcc" ||
|
||||||
fail "theme colour skips a policy directory that does not exist"
|
fail "theme colour skips a policy directory that does not exist"
|
||||||
[[ ! -e $missing_dir ]] || fail "theme colour does not create a missing policy directory"
|
[[ ! -e $missing_dir ]] || fail "theme colour does not create a missing policy directory"
|
||||||
pass "theme colour skips a missing policy directory"
|
pass "theme colour skips a missing policy directory"
|
||||||
|
|
||||||
if (( EUID == 0 )); then
|
if browser_policy_install_color "$write_dir" "aabbcc" 2>/dev/null; then
|
||||||
pass "running as root; skipping elevation checks"
|
fail "theme colour rejects hex without a leading #"
|
||||||
else
|
|
||||||
denied_dir=$test_tmp/denied
|
|
||||||
mkdir -p "$denied_dir"
|
|
||||||
chmod a-w "$denied_dir"
|
|
||||||
owner=${USER:-$(id -un)}
|
|
||||||
: >"$elev_log"
|
|
||||||
browser_policy_write_color "$denied_dir" "#aabbcc" ||
|
|
||||||
fail "elevated install reports success from pkexec"
|
|
||||||
grep -E "^PKEXEC install -m 664 -o $owner -g omarchy-browser-policy -T .+ $denied_dir/color.json$" "$elev_log" >/dev/null ||
|
|
||||||
fail "without a controlling tty, colour write elevates through pkexec as the owner" "$(cat "$elev_log")"
|
|
||||||
if grep -E '^SUDO ' "$elev_log" >/dev/null; then
|
|
||||||
fail "without a controlling tty, colour write does not call sudo" "$(cat "$elev_log")"
|
|
||||||
fi
|
|
||||||
pass "without a controlling tty, colour write elevates through pkexec"
|
|
||||||
|
|
||||||
: >"$elev_log"
|
|
||||||
export OMARCHY_TEST_SUDO_FAIL=1
|
|
||||||
if browser_policy_write_color "$denied_dir" "#aabbcc" 2>"$test_tmp/write.err"; then
|
|
||||||
fail "theme colour fails when the policy directory is not writable"
|
|
||||||
fi
|
|
||||||
unset OMARCHY_TEST_SUDO_FAIL
|
|
||||||
grep -F 'omarchy-browser-policy' "$test_tmp/write.err" >/dev/null ||
|
|
||||||
fail "theme colour names the group when the write is denied"
|
|
||||||
pass "theme colour reports a denied policy write"
|
|
||||||
|
|
||||||
if command -v script >/dev/null; then
|
|
||||||
: >"$elev_log"
|
|
||||||
cat >"$test_tmp/tty-write.sh" <<EOF
|
|
||||||
source "$ROOT/install/helpers/browser-policy.sh"
|
|
||||||
browser_policy_write_color "$denied_dir" "#aabbcc"
|
|
||||||
EOF
|
|
||||||
script -q -c "PATH='$mock_bin:$PATH' OMARCHY_PATH='$ROOT' bash '$test_tmp/tty-write.sh'" /dev/null >/dev/null
|
|
||||||
grep -E "^SUDO install -m 664 -o $owner -g omarchy-browser-policy -T .+ $denied_dir/color.json$" "$elev_log" >/dev/null ||
|
|
||||||
fail "with a controlling tty, colour write elevates through sudo" "$(cat "$elev_log")"
|
|
||||||
if grep -E '^PKEXEC ' "$elev_log" >/dev/null; then
|
|
||||||
fail "with a controlling tty, colour write does not call pkexec" "$(cat "$elev_log")"
|
|
||||||
fi
|
|
||||||
pass "with a controlling tty, colour write elevates through sudo"
|
|
||||||
else
|
|
||||||
pass "script(1) unavailable; skipping the controlling-tty elevation check"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
if browser_policy_install_color "$write_dir" "#AABBCC" 2>/dev/null; then
|
||||||
|
fail "theme colour rejects uppercase hex"
|
||||||
|
fi
|
||||||
|
pass "theme colour accepts only # plus six lowercase hex digits"
|
||||||
|
|
||||||
planted_dir=$test_tmp/planted
|
planted_dir=$test_tmp/planted
|
||||||
mkdir -p "$planted_dir/evil"
|
mkdir -p "$planted_dir/evil"
|
||||||
@@ -156,16 +101,16 @@ browser_policy_setup_dir "$planted_dir"
|
|||||||
[[ ! -e $planted_dir/evil ]] || fail "policy setup drops a non-empty non-root subdirectory"
|
[[ ! -e $planted_dir/evil ]] || fail "policy setup drops a non-empty non-root subdirectory"
|
||||||
[[ ! -e $planted_dir/color.json ]] || fail "policy setup drops a non-root color.json"
|
[[ ! -e $planted_dir/color.json ]] || fail "policy setup drops a non-root color.json"
|
||||||
[[ -d $planted_dir ]] || fail "policy setup leaves the managed directory in place"
|
[[ -d $planted_dir ]] || fail "policy setup leaves the managed directory in place"
|
||||||
|
mode=$(stat -c '%a' "$planted_dir")
|
||||||
|
[[ $mode == "755" ]] || fail "policy setup leaves the managed directory 0755" "mode=$mode"
|
||||||
pass "policy setup drops non-root files and non-empty subdirectories"
|
pass "policy setup drops non-root files and non-empty subdirectories"
|
||||||
|
|
||||||
owned=$test_tmp/not-root
|
owned=$test_tmp/not-root
|
||||||
mkdir -p "$owned"
|
mkdir -p "$owned"
|
||||||
chmod 2775 "$owned"
|
chmod 755 "$owned"
|
||||||
BROWSER_POLICY_GROUP=$(id -gn)
|
|
||||||
if browser_policy_dir_hardened "$owned"; then
|
if browser_policy_dir_hardened "$owned"; then
|
||||||
fail "a user-owned 2775 directory is not treated as hardened"
|
fail "a user-owned 0755 directory is not treated as hardened"
|
||||||
fi
|
fi
|
||||||
BROWSER_POLICY_GROUP=omarchy-browser-policy
|
|
||||||
pass "a hardened directory must be root-owned"
|
pass "a hardened directory must be root-owned"
|
||||||
|
|
||||||
saved_parent_dirs=("${BROWSER_POLICY_PARENT_DIRS[@]}")
|
saved_parent_dirs=("${BROWSER_POLICY_PARENT_DIRS[@]}")
|
||||||
@@ -173,7 +118,7 @@ parent_root=$test_tmp/parents
|
|||||||
mkdir -p "$parent_root/etc/chromium/policies/managed/keep"
|
mkdir -p "$parent_root/etc/chromium/policies/managed/keep"
|
||||||
printf 'keep\n' >"$parent_root/etc/chromium/policies/managed/keep/x"
|
printf 'keep\n' >"$parent_root/etc/chromium/policies/managed/keep/x"
|
||||||
chmod 0777 "$parent_root/etc/chromium" "$parent_root/etc/chromium/policies"
|
chmod 0777 "$parent_root/etc/chromium" "$parent_root/etc/chromium/policies"
|
||||||
chmod 2775 "$parent_root/etc/chromium/policies/managed"
|
chmod 755 "$parent_root/etc/chromium/policies/managed"
|
||||||
BROWSER_POLICY_PARENT_DIRS=(
|
BROWSER_POLICY_PARENT_DIRS=(
|
||||||
"$parent_root/etc/chromium"
|
"$parent_root/etc/chromium"
|
||||||
"$parent_root/etc/chromium/policies"
|
"$parent_root/etc/chromium/policies"
|
||||||
@@ -270,7 +215,7 @@ pass "Firefox setup does not follow a planted distribution symlink"
|
|||||||
for malformed in "" "not,a,color" "1,2" "1,2,3,4" "256,0,0" "999,999,999" "-1,0,0" \
|
for malformed in "" "not,a,color" "1,2" "1,2,3,4" "256,0,0" "999,999,999" "-1,0,0" \
|
||||||
"1,2,3;id" '1,2,$(id)' "0x10,0,0" "1,2,3 4,5,6"; do
|
"1,2,3;id" '1,2,$(id)' "0x10,0,0" "1,2,3 4,5,6"; do
|
||||||
[[ $(browser_policy_theme_hex "$malformed") == "#1c2027" ]] ||
|
[[ $(browser_policy_theme_hex "$malformed") == "#1c2027" ]] ||
|
||||||
fail "theme colour falls back to the neutral grey for '$malformed'"
|
fail "theme colour falls back to the stock grey for '$malformed'"
|
||||||
done
|
done
|
||||||
pass "theme colour is six hex digits or the stock grey"
|
pass "theme colour is six hex digits or the stock grey"
|
||||||
|
|
||||||
@@ -288,6 +233,8 @@ pass "shipped chromium.theme files parse as RGB triples"
|
|||||||
|
|
||||||
grep -F 'browser_policy_theme_hex' "$ROOT/bin/omarchy-theme-set-browser" >/dev/null ||
|
grep -F 'browser_policy_theme_hex' "$ROOT/bin/omarchy-theme-set-browser" >/dev/null ||
|
||||||
fail "omarchy-theme-set-browser parses chromium.theme through browser_policy_theme_hex"
|
fail "omarchy-theme-set-browser parses chromium.theme through browser_policy_theme_hex"
|
||||||
|
grep -F 'omarchy-theme-set-browser-policy' "$ROOT/bin/omarchy-theme-set-browser" >/dev/null ||
|
||||||
|
fail "omarchy-theme-set-browser writes colour through omarchy-theme-set-browser-policy"
|
||||||
if grep -E 'printf.*THEME_RGB_COLOR' "$ROOT/bin/omarchy-theme-set-browser" >/dev/null; then
|
if grep -E 'printf.*THEME_RGB_COLOR' "$ROOT/bin/omarchy-theme-set-browser" >/dev/null; then
|
||||||
fail "omarchy-theme-set-browser does not hand unvetted theme words to printf"
|
fail "omarchy-theme-set-browser does not hand unvetted theme words to printf"
|
||||||
fi
|
fi
|
||||||
@@ -328,27 +275,6 @@ fi
|
|||||||
[[ -d $dir_dist/policies.json ]] || fail "Firefox policy install leaves a planted policies.json directory in place"
|
[[ -d $dir_dist/policies.json ]] || fail "Firefox policy install leaves a planted policies.json directory in place"
|
||||||
pass "Firefox policy install does not write into a planted policies.json directory"
|
pass "Firefox policy install does not write into a planted policies.json directory"
|
||||||
|
|
||||||
grant_log=$test_tmp/usermod.calls
|
|
||||||
as_root() {
|
|
||||||
if [[ $1 == "usermod" ]]; then
|
|
||||||
printf '%s\n' "$*" >>"$grant_log"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
unprivileged_as_root "$@"
|
|
||||||
}
|
|
||||||
invoker=${USER:-$(id -un)}
|
|
||||||
: >"$grant_log"
|
|
||||||
SUDO_USER=$invoker
|
|
||||||
browser_policy_grant_user root
|
|
||||||
unset SUDO_USER
|
|
||||||
grep -qx -- "usermod -aG omarchy-browser-policy $invoker" "$grant_log" ||
|
|
||||||
fail "granting as root uses SUDO_USER" "$(cat "$grant_log")"
|
|
||||||
: >"$grant_log"
|
|
||||||
OMARCHY_INSTALL_USER=""
|
|
||||||
browser_policy_grant_user ""
|
|
||||||
[[ ! -s $grant_log ]] || fail "an empty grant does not usermod"
|
|
||||||
pass "sudo install browser grants the invoking user, not root"
|
|
||||||
|
|
||||||
grep -F 'exit "$failed"' "$ROOT/bin/omarchy-theme-set-browser" >/dev/null ||
|
grep -F 'exit "$failed"' "$ROOT/bin/omarchy-theme-set-browser" >/dev/null ||
|
||||||
fail "omarchy-theme-set-browser exits non-zero when a policy write fails"
|
fail "omarchy-theme-set-browser exits non-zero when a policy write fails"
|
||||||
pass "omarchy-theme-set-browser exits non-zero when a policy write fails"
|
pass "omarchy-theme-set-browser exits non-zero when a policy write fails"
|
||||||
@@ -357,25 +283,25 @@ policy_files=(
|
|||||||
"$ROOT/bin/omarchy-install-browser"
|
"$ROOT/bin/omarchy-install-browser"
|
||||||
"$ROOT/bin/omarchy-provision-owner"
|
"$ROOT/bin/omarchy-provision-owner"
|
||||||
"$ROOT/bin/omarchy-theme-set-browser"
|
"$ROOT/bin/omarchy-theme-set-browser"
|
||||||
|
"$ROOT/bin/omarchy-theme-set-browser-policy"
|
||||||
"$ROOT/bin/omarchy-upgrade-to-quattro"
|
"$ROOT/bin/omarchy-upgrade-to-quattro"
|
||||||
"$ROOT/install/config/theme-system.sh"
|
"$ROOT/install/config/theme-system.sh"
|
||||||
"$ROOT/install/config/browser-policy.sh"
|
"$ROOT/install/config/browser-policy.sh"
|
||||||
"$ROOT/install/helpers/browser-policy.sh"
|
"$ROOT/install/helpers/browser-policy.sh"
|
||||||
"$ROOT/migrations/1787515927.sh"
|
"$ROOT/migrations/1787515927.sh"
|
||||||
)
|
)
|
||||||
if grep -nE 'chmod a\+rwx\b|chmod a\+rw\b|chmod a\+w\b|chmod o\+w|chmod ugo\+w|chmod 2777\b|chmod 0777\b|chmod 777\b|install -d -m 0?[27]?777' "${policy_files[@]}" >/dev/null; then
|
if grep -nE 'chmod a\+rwx\b|chmod a\+rw\b|chmod a\+w\b|chmod o\+w|chmod ugo\+w|chmod 2775\b|chmod 2777\b|chmod 0777\b|chmod 777\b|install -d -m 0?[27]?777|omarchy-browser-policy' "${policy_files[@]}" >/dev/null; then
|
||||||
fail "browser policy setup is not world-writable"
|
fail "browser policy setup is not world-writable and does not use omarchy-browser-policy"
|
||||||
fi
|
fi
|
||||||
pass "browser policy setup is not world-writable"
|
pass "browser policy setup is not world-writable"
|
||||||
|
|
||||||
mapfile -t migrations < <(rg -l 'Stop world-writable Chromium and Firefox policy directories' "$ROOT/migrations")
|
mapfile -t migrations < <(rg -l 'Stop world-writable Chromium and Firefox policy directories' "$ROOT/migrations")
|
||||||
(( ${#migrations[@]} == 1 )) || fail "exactly one migration locks existing policy directories" "${migrations[*]}"
|
(( ${#migrations[@]} == 1 )) || fail "exactly one migration locks existing policy directories" "${migrations[*]}"
|
||||||
grep -F 'browser_policy_dir_hardened' "${migrations[0]}" >/dev/null ||
|
grep -F 'browser_policy_setup_dir' "${migrations[0]}" >/dev/null ||
|
||||||
fail "the policy-directory migration no-ops a machine already repaired"
|
fail "the policy-directory migration repairs managed directories"
|
||||||
grep -F 'browser_policy_parents_hardened' "${migrations[0]}" >/dev/null ||
|
if grep -F 'browser_policy_grant_user' "${migrations[0]}" >/dev/null; then
|
||||||
fail "the policy-directory migration repairs a world-writable parent of a hardened leaf"
|
fail "the policy-directory migration does not grant a browser-policy group"
|
||||||
grep -F 'browser_policy_grant_user' "${migrations[0]}" >/dev/null ||
|
fi
|
||||||
fail "the policy-directory migration still grants the current user the group"
|
|
||||||
grep -F 'BROWSER_POLICY_FIREFOX_DIRS' "${migrations[0]}" >/dev/null ||
|
grep -F 'BROWSER_POLICY_FIREFOX_DIRS' "${migrations[0]}" >/dev/null ||
|
||||||
fail "the policy-directory migration covers Firefox and Zen"
|
fail "the policy-directory migration covers Firefox and Zen"
|
||||||
grep -F 'browser_policy_firefox_policy_file_ok' "${migrations[0]}" >/dev/null ||
|
grep -F 'browser_policy_firefox_policy_file_ok' "${migrations[0]}" >/dev/null ||
|
||||||
|
|||||||
Executable
+184
@@ -0,0 +1,184 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source "$(dirname "$0")/base-test.sh"
|
||||||
|
|
||||||
|
helper="$ROOT/bin/omarchy-theme-set-browser-policy"
|
||||||
|
setter="$ROOT/bin/omarchy-theme-set-browser"
|
||||||
|
sudoers_file="$ROOT/etc/sudoers.d/omarchy-theme-browser"
|
||||||
|
rule='%wheel ALL=(root) NOPASSWD: /usr/bin/omarchy-theme-set-browser-policy [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
|
||||||
|
|
||||||
|
# Exactly one rule, matched whole. Dropping the argument -- which sudoers reads
|
||||||
|
# as "any arguments" -- or widening the glob to `*` would let the grant carry
|
||||||
|
# something other than a color while leaving this line looking right.
|
||||||
|
rules=$(grep -vE '^[[:space:]]*(#|$)' "$sudoers_file")
|
||||||
|
[[ $rules == "$rule" ]] ||
|
||||||
|
fail "browser policy sudoers file carries exactly the six-hex-digit rule and nothing else" "got: $rules"
|
||||||
|
|
||||||
|
if command -v visudo >/dev/null; then
|
||||||
|
visudo -cf "$sudoers_file" >/dev/null || fail "browser policy sudoers rule parses"
|
||||||
|
fi
|
||||||
|
|
||||||
|
grep -Fx 'PACKAGED_PATH=/usr/bin/omarchy-theme-set-browser-policy' "$helper" >/dev/null ||
|
||||||
|
fail "omarchy-theme-set-browser-policy elevates the path the sudoers rule names"
|
||||||
|
|
||||||
|
grep -E 'sudo -n -l -l' "$helper" >/dev/null ||
|
||||||
|
fail "omarchy-theme-set-browser-policy reads the grant from the long sudo listing"
|
||||||
|
|
||||||
|
grep -Eq '^\s*export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin' "$helper" ||
|
||||||
|
fail "omarchy-theme-set-browser-policy pins PATH to trusted system directories when it holds root"
|
||||||
|
gated=$(grep -A1 -E '^if \(\( EUID == 0 \)\); then$' "$helper" || true)
|
||||||
|
[[ $gated == *"export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin"* ]] ||
|
||||||
|
fail "omarchy-theme-set-browser-policy gates the trusted-PATH pin on holding root"
|
||||||
|
|
||||||
|
pass "browser policy sudoers rule is scoped to a single color argument"
|
||||||
|
|
||||||
|
for dir in /etc/chromium/policies/managed /etc/opt/chrome/policies/managed \
|
||||||
|
/etc/opt/edge/policies/managed /etc/brave/policies/managed; do
|
||||||
|
grep -Fx " $dir" "$helper" >/dev/null ||
|
||||||
|
fail "omarchy-theme-set-browser-policy names $dir in its fixed policy directory list"
|
||||||
|
done
|
||||||
|
|
||||||
|
policy_dir_count=$(sed -n '/^POLICY_DIRS=(/,/^)/p' "$helper" | grep -c '^ /')
|
||||||
|
((policy_dir_count == 4)) ||
|
||||||
|
fail "omarchy-theme-set-browser-policy writes only the four known policy directories" \
|
||||||
|
"got: $policy_dir_count"
|
||||||
|
|
||||||
|
grep -F 'install -m 0644 -o root -g root -T' "$helper" >/dev/null ||
|
||||||
|
fail "omarchy-theme-set-browser-policy installs color.json with install -T"
|
||||||
|
if grep -E 'mv -f' "$helper" >/dev/null; then
|
||||||
|
fail "omarchy-theme-set-browser-policy does not mv into a planted color.json directory"
|
||||||
|
fi
|
||||||
|
|
||||||
|
pass "browser policy helper writes a fixed set of policy directories"
|
||||||
|
|
||||||
|
test_tmp=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$test_tmp"' EXIT
|
||||||
|
|
||||||
|
stub_bin="$test_tmp/bin"
|
||||||
|
mkdir -p "$stub_bin"
|
||||||
|
|
||||||
|
cat >"$stub_bin/pkexec" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
printf 'pkexec %s\n' "$*" >"$ELEVATION_LOG"
|
||||||
|
SH
|
||||||
|
chmod +x "$stub_bin/pkexec"
|
||||||
|
|
||||||
|
# STUB_GRANTED empty stands for an install whose omarchy-settings predates the
|
||||||
|
# sudoers file. The default is granted, matching a current Omarchy.
|
||||||
|
cat >"$stub_bin/sudo" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
if [[ $1 == -n && $2 == -l ]]; then
|
||||||
|
if [[ ${STUB_GRANTED-granted} == "granted" ]]; then
|
||||||
|
echo " Options: !authenticate"
|
||||||
|
else
|
||||||
|
echo " Matched: ${!#}"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
printf 'sudo %s\n' "$*" >"$ELEVATION_LOG"
|
||||||
|
SH
|
||||||
|
chmod +x "$stub_bin/sudo"
|
||||||
|
|
||||||
|
if ((EUID == 0)); then
|
||||||
|
pass "running as root; skipping the elevation checks, which would rewrite this machine's browser policy"
|
||||||
|
else
|
||||||
|
elevation_for() {
|
||||||
|
: >"$test_tmp/elevation"
|
||||||
|
ELEVATION_LOG="$test_tmp/elevation" \
|
||||||
|
PATH="$stub_bin:$PATH" \
|
||||||
|
bash "$helper" "$@" </dev/null >/dev/null 2>&1 || true
|
||||||
|
cat "$test_tmp/elevation"
|
||||||
|
}
|
||||||
|
|
||||||
|
elevation=$(elevation_for 1c2027)
|
||||||
|
[[ $elevation == "sudo /usr/bin/omarchy-theme-set-browser-policy 1c2027" ]] ||
|
||||||
|
fail "omarchy-theme-set-browser-policy takes the passwordless sudo grant without a terminal" \
|
||||||
|
"got: $elevation"
|
||||||
|
|
||||||
|
dev_linked=$(OMARCHY_PATH="$test_tmp/checkout" elevation_for 1c2027)
|
||||||
|
[[ $dev_linked == "sudo /usr/bin/omarchy-theme-set-browser-policy 1c2027" ]] ||
|
||||||
|
fail "omarchy-theme-set-browser-policy elevates the system install wherever OMARCHY_PATH points" \
|
||||||
|
"got: $dev_linked"
|
||||||
|
|
||||||
|
pass "browser policy helper elevates a valid color through the sudo grant"
|
||||||
|
|
||||||
|
ungranted=$(STUB_GRANTED="" elevation_for 1c2027)
|
||||||
|
[[ $ungranted == "pkexec /usr/bin/omarchy-theme-set-browser-policy 1c2027" ]] ||
|
||||||
|
fail "omarchy-theme-set-browser-policy falls back to polkit where the grant does not reach" \
|
||||||
|
"got: $ungranted"
|
||||||
|
|
||||||
|
pass "browser policy helper falls back to polkit wherever the grant does not reach"
|
||||||
|
|
||||||
|
for bad in "" "1C2027" "abc12" "abc1234" "1c202g" "../../etc/passwd" "1c2027 1c2027" \
|
||||||
|
'$(id)' "1c2027;id" "#1c2027"; do
|
||||||
|
if PATH="$stub_bin:$PATH" ELEVATION_LOG="$test_tmp/elevation" \
|
||||||
|
bash "$helper" "$bad" </dev/null >/dev/null 2>&1; then
|
||||||
|
fail "omarchy-theme-set-browser-policy rejects '$bad'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rejected=$(elevation_for "$bad")
|
||||||
|
[[ -z $rejected ]] ||
|
||||||
|
fail "omarchy-theme-set-browser-policy rejects '$bad' before elevating" "got: $rejected"
|
||||||
|
done
|
||||||
|
|
||||||
|
if PATH="$stub_bin:$PATH" bash "$helper" 1c2027 ffffff </dev/null >/dev/null 2>&1; then
|
||||||
|
fail "omarchy-theme-set-browser-policy rejects more than one argument"
|
||||||
|
fi
|
||||||
|
|
||||||
|
pass "browser policy helper accepts nothing but six lowercase hex digits"
|
||||||
|
fi
|
||||||
|
|
||||||
|
setter_bin="$test_tmp/setter-bin"
|
||||||
|
mkdir -p "$setter_bin"
|
||||||
|
|
||||||
|
cat >"$setter_bin/omarchy-theme-set-browser-policy" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
printf '%s\n' "$*" >"$COLOR_LOG"
|
||||||
|
SH
|
||||||
|
chmod +x "$setter_bin/omarchy-theme-set-browser-policy"
|
||||||
|
|
||||||
|
cat >"$setter_bin/omarchy-cmd-present" <<'SH'
|
||||||
|
#!/bin/bash
|
||||||
|
exit 1
|
||||||
|
SH
|
||||||
|
chmod +x "$setter_bin/omarchy-cmd-present"
|
||||||
|
|
||||||
|
setter_home="$test_tmp/home"
|
||||||
|
theme_dir="$setter_home/.local/state/omarchy/current/theme"
|
||||||
|
mkdir -p "$theme_dir"
|
||||||
|
|
||||||
|
color_for_theme() {
|
||||||
|
: >"$test_tmp/color"
|
||||||
|
if [[ $# -gt 0 ]]; then
|
||||||
|
printf '%s' "$1" >"$theme_dir/chromium.theme"
|
||||||
|
else
|
||||||
|
rm -f "$theme_dir/chromium.theme"
|
||||||
|
fi
|
||||||
|
|
||||||
|
HOME="$setter_home" COLOR_LOG="$test_tmp/color" PATH="$setter_bin:$stub_bin:$PATH" \
|
||||||
|
OMARCHY_PATH="$ROOT" bash "$setter" </dev/null >/dev/null 2>&1 || true
|
||||||
|
cat "$test_tmp/color"
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ $(color_for_theme "242,240,229") == "f2f0e5" ]] ||
|
||||||
|
fail "omarchy-theme-set-browser converts an RGB triple to six hex digits"
|
||||||
|
[[ $(color_for_theme $'14,31,41\n') == "0e1f29" ]] ||
|
||||||
|
fail "omarchy-theme-set-browser accepts a trailing newline"
|
||||||
|
[[ $(color_for_theme "0,0,0") == "000000" ]] ||
|
||||||
|
fail "omarchy-theme-set-browser pads single-digit components"
|
||||||
|
[[ $(color_for_theme " 12 , 11 , 12 ") == "0c0b0c" ]] ||
|
||||||
|
fail "omarchy-theme-set-browser tolerates surrounding whitespace"
|
||||||
|
|
||||||
|
for malformed in "" "not,a,color" "1,2" "1,2,3,4" "256,0,0" "999,999,999" "-1,0,0" \
|
||||||
|
"1,2,3;id" '1,2,$(id)' "0x10,0,0" "1,2,3 4,5,6"; do
|
||||||
|
color=$(color_for_theme "$malformed")
|
||||||
|
[[ $color == "1c2027" ]] ||
|
||||||
|
fail "omarchy-theme-set-browser falls back to the stock colour for '$malformed'" "got: $color"
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ $(color_for_theme) == "1c2027" ]] ||
|
||||||
|
fail "omarchy-theme-set-browser falls back to the stock colour with no theme file"
|
||||||
|
|
||||||
|
pass "browser theme color is derived as six hex digits or falls back to the stock grey"
|
||||||
@@ -208,18 +208,17 @@ OMARCHY_TEST_REAL_BROWSER_INSTALL=true omarchy-default-browser --install chromiu
|
|||||||
[[ $(omarchy-default-browser) == "chromium" ]] || fail "Chromium becomes the default after its full installer succeeds"
|
[[ $(omarchy-default-browser) == "chromium" ]] || fail "Chromium becomes the default after its full installer succeeds"
|
||||||
cmp -s "$ROOT/config/chromium-flags.conf" "$test_home/.config/chromium-flags.conf" ||
|
cmp -s "$ROOT/config/chromium-flags.conf" "$test_home/.config/chromium-flags.conf" ||
|
||||||
fail "Chromium browser installer copies the default flags"
|
fail "Chromium browser installer copies the default flags"
|
||||||
grep -Fxq 'sudo:groupadd --system --force omarchy-browser-policy' "$setup_log" ||
|
|
||||||
fail "Chromium browser installer creates the browser-policy group"
|
|
||||||
grep -Fxq 'sudo:install -d -m 0755 -o root -g root /etc/chromium' "$setup_log" ||
|
grep -Fxq 'sudo:install -d -m 0755 -o root -g root /etc/chromium' "$setup_log" ||
|
||||||
fail "Chromium browser installer creates a root-owned Chromium policy parent"
|
fail "Chromium browser installer creates a root-owned Chromium policy parent"
|
||||||
grep -Fxq 'sudo:install -d -m 0755 -o root -g root /etc/chromium/policies' "$setup_log" ||
|
grep -Fxq 'sudo:install -d -m 0755 -o root -g root /etc/chromium/policies' "$setup_log" ||
|
||||||
fail "Chromium browser installer creates a root-owned Chromium policies parent"
|
fail "Chromium browser installer creates a root-owned Chromium policies parent"
|
||||||
grep -Fxq 'sudo:install -d -m 2775 -o root -g omarchy-browser-policy /etc/chromium/policies/managed' "$setup_log" ||
|
grep -Fxq 'sudo:install -d -m 0755 -o root -g root /etc/chromium/policies/managed' "$setup_log" ||
|
||||||
fail "Chromium browser installer creates a group-writable managed policy directory"
|
fail "Chromium browser installer creates a root-owned managed policy directory"
|
||||||
grep -Fxq 'sudo:find /etc/chromium/policies/managed -mindepth 1 -maxdepth 1 ! -user root -exec rm -rf -- {} +' "$setup_log" ||
|
grep -Fxq 'sudo:find /etc/chromium/policies/managed -mindepth 1 -maxdepth 1 ! -user root -exec rm -rf -- {} +' "$setup_log" ||
|
||||||
fail "Chromium browser installer drops non-root files from its policy directory"
|
fail "Chromium browser installer drops non-root files from its policy directory"
|
||||||
grep -Fxq "sudo:usermod -aG omarchy-browser-policy ${USER:-$(id -un)}" "$setup_log" ||
|
if grep -E 'groupadd|usermod|omarchy-browser-policy' "$setup_log" >/dev/null; then
|
||||||
fail "Chromium browser installer grants the installing user the browser-policy group"
|
fail "Chromium browser installer does not create a browser-policy group" "$(cat "$setup_log")"
|
||||||
|
fi
|
||||||
grep -Fxq 'omarchy-install-chromium-copy-url:' "$setup_log" ||
|
grep -Fxq 'omarchy-install-chromium-copy-url:' "$setup_log" ||
|
||||||
fail "Chromium browser installer registers the Copy URL host"
|
fail "Chromium browser installer registers the Copy URL host"
|
||||||
grep -Fxq 'omarchy-install-chromium-ytdlp:' "$setup_log" ||
|
grep -Fxq 'omarchy-install-chromium-ytdlp:' "$setup_log" ||
|
||||||
|
|||||||
@@ -55,12 +55,13 @@ OMARCHY_INSTALL_USER="" bash -eE "$ROOT/install/config/browser-policy.sh"
|
|||||||
|
|
||||||
[[ -f $OMARCHY_PROVISIONING_DIR/groups ]] || fail "groups file written without an install user"
|
[[ -f $OMARCHY_PROVISIONING_DIR/groups ]] || fail "groups file written without an install user"
|
||||||
grep -qxF input "$OMARCHY_PROVISIONING_DIR/groups" || fail "input group recorded"
|
grep -qxF input "$OMARCHY_PROVISIONING_DIR/groups" || fail "input group recorded"
|
||||||
grep -qxF omarchy-browser-policy "$OMARCHY_PROVISIONING_DIR/groups" || fail "browser-policy group recorded"
|
! grep -qxF omarchy-browser-policy "$OMARCHY_PROVISIONING_DIR/groups" ||
|
||||||
|
fail "browser-policy group must not be recorded"
|
||||||
[[ ! -f $TMPDIR/usermod.calls ]] || fail "usermod not called without an install user"
|
[[ ! -f $TMPDIR/usermod.calls ]] || fail "usermod not called without an install user"
|
||||||
grep -F -- '--system --force omarchy-browser-policy' "$TMPDIR/groupadd.calls" >/dev/null ||
|
[[ ! -f $TMPDIR/groupadd.calls ]] || ! grep -F omarchy-browser-policy "$TMPDIR/groupadd.calls" >/dev/null ||
|
||||||
fail "browser-policy group is created as a system group"
|
fail "browser-policy group is not created"
|
||||||
grep -F -- '-d -m 2775 -o root -g omarchy-browser-policy /etc/chromium/policies/managed' "$TMPDIR/install.calls" >/dev/null ||
|
grep -F -- '-d -m 0755 -o root -g root /etc/chromium/policies/managed' "$TMPDIR/install.calls" >/dev/null ||
|
||||||
fail "browser-policy directory is created group-writable"
|
fail "browser-policy directory is created root-owned"
|
||||||
pass "deferred provisioning records groups without calling usermod"
|
pass "deferred provisioning records groups without calling usermod"
|
||||||
|
|
||||||
# The docker group is root-equivalent and must never be granted automatically.
|
# The docker group is root-equivalent and must never be granted automatically.
|
||||||
@@ -77,8 +78,6 @@ pass "missing install user defers group grants"
|
|||||||
OMARCHY_INSTALL_USER="" bash -eE "$ROOT/install/hardware/input-group.sh"
|
OMARCHY_INSTALL_USER="" bash -eE "$ROOT/install/hardware/input-group.sh"
|
||||||
[[ $(grep -cxF input "$OMARCHY_PROVISIONING_DIR/groups") == 1 ]] || fail "input group recorded once"
|
[[ $(grep -cxF input "$OMARCHY_PROVISIONING_DIR/groups") == 1 ]] || fail "input group recorded once"
|
||||||
OMARCHY_INSTALL_USER="" bash -eE "$ROOT/install/config/browser-policy.sh"
|
OMARCHY_INSTALL_USER="" bash -eE "$ROOT/install/config/browser-policy.sh"
|
||||||
[[ $(grep -cxF omarchy-browser-policy "$OMARCHY_PROVISIONING_DIR/groups") == 1 ]] ||
|
|
||||||
fail "browser-policy group recorded once"
|
|
||||||
pass "group recording is idempotent"
|
pass "group recording is idempotent"
|
||||||
|
|
||||||
# Existing user: usermod applies the recorded groups, and docker is never among them.
|
# Existing user: usermod applies the recorded groups, and docker is never among them.
|
||||||
@@ -86,7 +85,7 @@ OMARCHY_INSTALL_USER=existing bash -eE "$ROOT/install/config/docker.sh"
|
|||||||
OMARCHY_INSTALL_USER=existing bash -eE "$ROOT/install/hardware/input-group.sh"
|
OMARCHY_INSTALL_USER=existing bash -eE "$ROOT/install/hardware/input-group.sh"
|
||||||
OMARCHY_INSTALL_USER=existing bash -eE "$ROOT/install/config/browser-policy.sh"
|
OMARCHY_INSTALL_USER=existing bash -eE "$ROOT/install/config/browser-policy.sh"
|
||||||
grep -qx -- "-aG input existing" "$TMPDIR/usermod.calls" || fail "usermod grants input to the install user"
|
grep -qx -- "-aG input existing" "$TMPDIR/usermod.calls" || fail "usermod grants input to the install user"
|
||||||
grep -qx -- "-aG omarchy-browser-policy existing" "$TMPDIR/usermod.calls" ||
|
! grep -q -- "omarchy-browser-policy" "$TMPDIR/usermod.calls" ||
|
||||||
fail "usermod grants browser-policy to the install user"
|
fail "usermod must not grant browser-policy to the install user"
|
||||||
! grep -q -- "docker" "$TMPDIR/usermod.calls" || fail "usermod must not grant docker to the install user"
|
! grep -q -- "docker" "$TMPDIR/usermod.calls" || fail "usermod must not grant docker to the install user"
|
||||||
pass "existing install user gets input and browser-policy but never docker"
|
pass "existing install user gets input but never docker or browser-policy"
|
||||||
|
|||||||
@@ -71,18 +71,19 @@ grep -F 'install/helpers/browser-policy.sh' "$upgrade_to_quattro" >/dev/null ||
|
|||||||
fail "Omarchy 4 upgrade uses the shared browser-policy helper"
|
fail "Omarchy 4 upgrade uses the shared browser-policy helper"
|
||||||
grep -F 'as_root test -f "$browser_policy_helper"' "$upgrade_to_quattro" >/dev/null ||
|
grep -F 'as_root test -f "$browser_policy_helper"' "$upgrade_to_quattro" >/dev/null ||
|
||||||
fail "Omarchy 4 upgrade survives a packaged tree without the browser-policy helper"
|
fail "Omarchy 4 upgrade survives a packaged tree without the browser-policy helper"
|
||||||
grep -F 'browser_policy_setup_group' "$upgrade_to_quattro" >/dev/null ||
|
if grep -F 'browser_policy_setup_group' "$upgrade_to_quattro" >/dev/null; then
|
||||||
fail "Omarchy 4 upgrade creates the browser-policy group"
|
fail "Omarchy 4 upgrade does not create a browser-policy group"
|
||||||
|
fi
|
||||||
grep -F 'browser_policy_setup_dir /etc/chromium/policies/managed' "$upgrade_to_quattro" >/dev/null ||
|
grep -F 'browser_policy_setup_dir /etc/chromium/policies/managed' "$upgrade_to_quattro" >/dev/null ||
|
||||||
fail "Omarchy 4 upgrade creates a group-writable Chromium policy directory"
|
fail "Omarchy 4 upgrade creates a root-owned Chromium policy directory"
|
||||||
grep -F 'BROWSER_POLICY_MANAGED_DIRS' "$upgrade_to_quattro" >/dev/null ||
|
grep -F 'BROWSER_POLICY_MANAGED_DIRS' "$upgrade_to_quattro" >/dev/null ||
|
||||||
fail "Omarchy 4 upgrade hardens every Chromium-family policy directory"
|
fail "Omarchy 4 upgrade hardens every Chromium-family policy directory"
|
||||||
grep -F 'run_as_user_omarchy omarchy-theme-set-browser' "$upgrade_to_quattro" >/dev/null ||
|
grep -F 'run_as_user_omarchy omarchy-theme-set-browser' "$upgrade_to_quattro" >/dev/null ||
|
||||||
fail "Omarchy 4 upgrade rewrites browser theme colour after a headless theme-set"
|
fail "Omarchy 4 upgrade rewrites browser theme colour after a headless theme-set"
|
||||||
if grep -E 'install -d -m 0?[27]?777 /etc/.*/policies|chmod a\+rw' "$upgrade_to_quattro" >/dev/null; then
|
if grep -E 'install -d -m 0?[27]?777 /etc/.*/policies|chmod a\+rw|2775' "$upgrade_to_quattro" >/dev/null; then
|
||||||
fail "Omarchy 4 upgrade does not create a world-writable Chromium policy directory"
|
fail "Omarchy 4 upgrade does not create a world-writable Chromium policy directory"
|
||||||
fi
|
fi
|
||||||
pass "Omarchy 4 upgrade locks the Chromium policy directory to the browser-policy group"
|
pass "Omarchy 4 upgrade locks the Chromium policy directory to root"
|
||||||
|
|
||||||
grep -F 'OMARCHY_UPGRADE_TO_QUATTRO_LIVE=1' "$upgrade_to_quattro" >/dev/null
|
grep -F 'OMARCHY_UPGRADE_TO_QUATTRO_LIVE=1' "$upgrade_to_quattro" >/dev/null
|
||||||
grep -F 'systemd-networkd.service' "$upgrade_to_quattro" >/dev/null
|
grep -F 'systemd-networkd.service' "$upgrade_to_quattro" >/dev/null
|
||||||
|
|||||||
Reference in New Issue
Block a user