#!/bin/bash set -euo pipefail source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" test_tmp=$(mktemp -d) 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" <>"$elev_log" [[ \${OMARCHY_TEST_SUDO_FAIL:-} == 1 ]] && exit 1 exit 0 SH cat >"$mock_bin/pkexec" <>"$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_PROVISIONING_DIR="$test_tmp/provisioning" source "$ROOT/install/helpers/browser-policy.sh" # Temp dirs are user-owned; drop -o/-g so install(1) can run unprivileged. unprivileged_as_root() { if [[ $1 == "install" ]]; then shift local args=() local skip=0 local arg for arg in "$@"; do if (( skip )); then skip=0 continue fi case $arg in -o|-g) skip=1 ;; *) args+=("$arg") ;; esac done command install "${args[@]}" else "$@" fi } write_dir=$test_tmp/writable mkdir -p "$write_dir" browser_policy_write_color "$write_dir" "#aabbcc" || fail "theme colour writes into a writable policy directory" grep -F '"BrowserThemeColor": "#aabbcc"' "$write_dir/color.json" >/dev/null || fail "theme colour writes BrowserThemeColor" mode=$(stat -c '%a' "$write_dir/color.json") [[ $mode == "664" ]] || fail "theme colour creates a group-writable policy file" "mode=$mode" pass "theme colour writes a group-writable color.json" if (( EUID == 0 )); then pass "running as root; skipping the mktemp-failure check" else chmod u+w "$write_dir" export TMPDIR=$test_tmp/missing-tmp if browser_policy_write_color "$write_dir" "#dead00" 2>/dev/null; then fail "theme colour fails when mktemp cannot create a file" fi unset TMPDIR grep -F '"BrowserThemeColor": "#aabbcc"' "$write_dir/color.json" >/dev/null || fail "a failed mktemp leaves an existing color.json intact" pass "a failed mktemp does not truncate color.json" fi printf 'original\n' >"$test_tmp/pwn" rm -f "$write_dir/color.json" ln -s "$test_tmp/pwn" "$write_dir/color.json" browser_policy_write_color "$write_dir" "#aabbcc" || fail "theme colour replaces a planted color.json symlink" [[ -f $write_dir/color.json && ! -L $write_dir/color.json ]] || fail "theme colour unlinks a planted color.json symlink instead of writing through it" grep -Fxq 'original' "$test_tmp/pwn" || fail "theme colour leaves the symlink target unchanged" pass "theme colour does not follow a planted color.json symlink" plant_write=$test_tmp/plant-dir mkdir -p "$plant_write/color.json/nested" printf 'inside\n' >"$plant_write/color.json/nested/x" browser_policy_write_color "$plant_write" "#aabbcc" || fail "theme colour replaces a planted color.json directory" [[ -f $plant_write/color.json && ! -d $plant_write/color.json ]] || fail "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 browser_policy_write_color "$missing_dir" "#aabbcc" || fail "theme colour skips a policy directory that does not exist" [[ ! -e $missing_dir ]] || fail "theme colour does not create a missing policy directory" pass "theme colour skips a missing policy directory" if (( EUID == 0 )); then pass "running as root; skipping elevation checks" 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" </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 planted_dir=$test_tmp/planted mkdir -p "$planted_dir/evil" printf 'evil\n' >"$planted_dir/evil/f" printf 'old\n' >"$planted_dir/color.json" as_root() { unprivileged_as_root "$@"; } browser_policy_setup_dir "$planted_dir" [[ ! -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" [[ -d $planted_dir ]] || fail "policy setup leaves the managed directory in place" pass "policy setup drops non-root files and non-empty subdirectories" owned=$test_tmp/not-root mkdir -p "$owned" chmod 2775 "$owned" BROWSER_POLICY_GROUP=$(id -gn) if browser_policy_dir_hardened "$owned"; then fail "a user-owned 2775 directory is not treated as hardened" fi BROWSER_POLICY_GROUP=omarchy-browser-policy pass "a hardened directory must be root-owned" saved_parent_dirs=("${BROWSER_POLICY_PARENT_DIRS[@]}") parent_root=$test_tmp/parents mkdir -p "$parent_root/etc/chromium/policies/managed/keep" printf 'keep\n' >"$parent_root/etc/chromium/policies/managed/keep/x" chmod 0777 "$parent_root/etc/chromium" "$parent_root/etc/chromium/policies" chmod 2775 "$parent_root/etc/chromium/policies/managed" BROWSER_POLICY_PARENT_DIRS=( "$parent_root/etc/chromium" "$parent_root/etc/chromium/policies" ) as_root() { unprivileged_as_root "$@"; } if browser_policy_parents_hardened "$parent_root/etc/chromium/policies/managed"; then fail "a world-writable policy parent is not treated as hardened" fi browser_policy_setup_parents_for "$parent_root/etc/chromium/policies/managed" mode=$(stat -c '%a' "$parent_root/etc/chromium") [[ $mode == "755" ]] || fail "setup tightens /etc/chromium" "mode=$mode" mode=$(stat -c '%a' "$parent_root/etc/chromium/policies") [[ $mode == "755" ]] || fail "setup tightens /etc/chromium/policies" "mode=$mode" [[ -d $parent_root/etc/chromium/policies/managed/keep ]] || fail "parent repair does not purge the managed directory" pass "policy parent directories are tightened to 0755 without purging the leaf" symlink_root=$test_tmp/symlink-parents mkdir -p "$symlink_root/etc" "$symlink_root/attacker/policies/managed" printf 'planted\n' >"$symlink_root/attacker/policies/managed/evil.json" ln -s "$symlink_root/attacker" "$symlink_root/etc/chromium" BROWSER_POLICY_PARENT_DIRS=( "$symlink_root/etc/chromium" "$symlink_root/etc/chromium/policies" ) as_root() { unprivileged_as_root "$@"; } browser_policy_setup_dir "$symlink_root/etc/chromium/policies/managed" [[ ! -L $symlink_root/etc/chromium ]] || fail "setup replaces a planted /etc/chromium symlink" [[ -d $symlink_root/etc/chromium && ! -L $symlink_root/etc/chromium ]] || fail "setup recreates /etc/chromium as a real directory" [[ -d $symlink_root/etc/chromium/policies && ! -L $symlink_root/etc/chromium/policies ]] || fail "setup recreates /etc/chromium/policies as a real directory" [[ ! -e $symlink_root/etc/chromium/policies/managed/evil.json ]] || fail "setup does not keep policy that lived behind a planted parent symlink" grep -Fxq 'planted' "$symlink_root/attacker/policies/managed/evil.json" || fail "replacing a parent symlink does not delete the symlink target" BROWSER_POLICY_PARENT_DIRS=("${saved_parent_dirs[@]}") pass "policy setup does not follow a planted parent symlink" [[ $(browser_policy_theme_hex "242,240,229") == "#f2f0e5" ]] || fail "theme colour converts an RGB triple to hex" [[ $(browser_policy_theme_hex $'14,31,41\n') == "#0e1f29" ]] || fail "theme colour accepts a trailing newline" [[ $(browser_policy_theme_hex "0,0,0") == "#000000" ]] || fail "theme colour pads single-digit components" [[ $(browser_policy_theme_hex " 12 , 11 , 12 ") == "#0c0b0c" ]] || fail "theme colour tolerates surrounding whitespace" [[ $(browser_policy_theme_hex "08,09,10") == "#08090a" ]] || fail "theme colour treats leading zeros as decimal" 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 [[ $(browser_policy_theme_hex "$malformed") == "#1c2027" ]] || fail "theme colour falls back to the neutral grey for '$malformed'" done pass "theme colour is six hex digits or the stock grey" for theme in "$ROOT"/themes/*/chromium.theme; do [[ -f $theme ]] || continue rgb=$(<$theme) hex=$(browser_policy_theme_hex "$rgb") [[ $hex =~ ^#[0-9a-f]{6}$ ]] || fail "shipped $(basename "$(dirname "$theme")") chromium.theme parses as hex" "got: $hex from $(printf %q "$rgb")" if [[ $hex == "#1c2027" && ! $rgb =~ ^[[:space:]]*28[[:space:]]*,[[:space:]]*32[[:space:]]*,[[:space:]]*39[[:space:]]*$ ]]; then fail "shipped $(basename "$(dirname "$theme")") chromium.theme is a valid RGB triple" "got: $(printf %q "$rgb")" fi done pass "shipped chromium.theme files parse as RGB triples" 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" 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" fi pass "omarchy-theme-set-browser validates the theme colour" fx_policy=$test_tmp/policies.json printf '%s\n' '{"policies":{}}' >"$fx_policy" chmod 644 "$fx_policy" if browser_policy_firefox_policy_file_ok "$fx_policy"; then fail "a user-owned policies.json is not treated as hardened" fi ln -sf "$fx_policy" "$test_tmp/policies-link.json" if browser_policy_firefox_policy_file_ok "$test_tmp/policies-link.json"; then fail "a policies.json symlink is not treated as hardened" fi pass "Firefox policy files must be root-owned regular files without group or other write" dist=$test_tmp/distribution mkdir -p "$dist" printf 'original\n' >"$test_tmp/firefox-pwn" ln -s "$test_tmp/firefox-pwn" "$dist/policies.json" as_root() { unprivileged_as_root "$@"; } browser_policy_install_firefox_policies "$dist" || fail "Firefox policy install replaces a planted policies.json symlink" [[ -f $dist/policies.json && ! -L $dist/policies.json ]] || fail "Firefox policy install unlinks a planted policies.json symlink instead of writing through it" grep -Fxq 'original' "$test_tmp/firefox-pwn" || fail "Firefox policy install leaves the symlink target unchanged" grep -q '"policies"' "$dist/policies.json" || fail "Firefox policy install writes the stock policies" pass "Firefox policy install does not follow a planted policies.json symlink" dir_dist=$test_tmp/distribution-dir mkdir -p "$dir_dist" mkdir "$dir_dist/policies.json" as_root() { unprivileged_as_root "$@"; } if browser_policy_install_firefox_policies "$dir_dist" 2>/dev/null; then fail "Firefox policy install refuses a planted policies.json directory" fi [[ -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" 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 || 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" policy_files=( "$ROOT/bin/omarchy-install-browser" "$ROOT/bin/omarchy-provision-owner" "$ROOT/bin/omarchy-theme-set-browser" "$ROOT/bin/omarchy-upgrade-to-quattro" "$ROOT/install/config/theme-system.sh" "$ROOT/install/config/browser-policy.sh" "$ROOT/install/helpers/browser-policy.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 fail "browser policy setup is not world-writable" fi pass "browser policy setup is not world-writable" 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[*]}" grep -F 'browser_policy_dir_hardened' "${migrations[0]}" >/dev/null || fail "the policy-directory migration no-ops a machine already repaired" grep -F 'browser_policy_parents_hardened' "${migrations[0]}" >/dev/null || fail "the policy-directory migration repairs a world-writable parent of a hardened leaf" grep -F 'browser_policy_grant_user' "${migrations[0]}" >/dev/null || fail "the policy-directory migration still grants the current user the group" grep -F 'BROWSER_POLICY_FIREFOX_DIRS' "${migrations[0]}" >/dev/null || fail "the policy-directory migration covers Firefox and Zen" grep -F 'browser_policy_firefox_policy_file_ok' "${migrations[0]}" >/dev/null || fail "the policy-directory migration keeps a trusted Firefox policies.json" grep -F '/opt/zen-browser/distribution' "$ROOT/install/helpers/browser-policy.sh" >/dev/null || fail "the shared helper names the Zen distribution directory" pass "a migration locks existing policy directories"