Refuse a theme name that is shell syntax, and quote the one the unlock picker returns
(cherry picked from commit 9da8824098)
206 lines
8.4 KiB
Bash
Executable File
206 lines
8.4 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
# omarchy-theme-install feeds a pasted URL to git and a name derived from it to
|
|
# rm, and omarchy-theme-remove feeds its argument to rm. Both are exercised here
|
|
# with git and the themes directory stubbed, so a guard that stopped working
|
|
# shows up as a clone or a removal that should never have been reached.
|
|
|
|
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"
|
|
|
|
cat >"$mock_bin/git" <<'SH'
|
|
#!/bin/bash
|
|
printf '%s\n' "$*" >>"$OMARCHY_TEST_GIT_CALLS"
|
|
[[ $1 == "clone" ]] && mkdir -p "${*: -1}"
|
|
exit 0
|
|
SH
|
|
|
|
cat >"$mock_bin/gum" <<'SH'
|
|
#!/bin/bash
|
|
exit 1
|
|
SH
|
|
|
|
for command in omarchy-theme-set omarchy-notification-send omarchy-menu-select; do
|
|
printf '#!/bin/bash\nprintf "%%s\\n" "$*" >>"$OMARCHY_TEST_THEME_CALLS"\nexit 0\n' >"$mock_bin/$command"
|
|
done
|
|
|
|
chmod +x "$mock_bin"/*
|
|
|
|
git_calls="$test_tmp/git-calls"
|
|
theme_calls="$test_tmp/theme-calls"
|
|
|
|
install_theme() {
|
|
: >"$git_calls"
|
|
: >"$theme_calls"
|
|
|
|
HOME="$test_tmp/home" PATH="${2-$mock_bin:$ROOT/bin:$PATH}" \
|
|
OMARCHY_TEST_GIT_CALLS="$git_calls" OMARCHY_TEST_THEME_CALLS="$theme_calls" \
|
|
bash "$ROOT/bin/omarchy-theme-install" "$1" >"$test_tmp/out" 2>&1 || return $?
|
|
}
|
|
|
|
mkdir -p "$test_tmp/home/.config/omarchy/themes"
|
|
|
|
# A URL git would read as an option or as a remote helper to run.
|
|
for url in "-x" "--upload-pack=touch /tmp/pwned" "ext::sh -c id" "fd::0,1"; do
|
|
if install_theme "$url"; then
|
|
fail "omarchy-theme-install refuses the URL '$url'"
|
|
fi
|
|
|
|
[[ ! -s $git_calls ]] || fail "omarchy-theme-install refuses '$url' before running git" "$(cat "$git_calls")"
|
|
done
|
|
|
|
pass "a URL that names a git option or a transport helper never reaches git"
|
|
|
|
# git resolves git-remote-<scheme> for any scheme it does not implement itself,
|
|
# so the `://` spelling of a helper has to be refused as well as the `::` one.
|
|
for url in "ext://sh -c id" "fd://17" "gcrypt://example.com/x"; do
|
|
if install_theme "$url"; then
|
|
fail "omarchy-theme-install refuses the URL '$url'"
|
|
fi
|
|
|
|
[[ ! -s $git_calls ]] || fail "omarchy-theme-install refuses '$url' before running git" "$(cat "$git_calls")"
|
|
done
|
|
|
|
pass "a URL naming a transport git does not implement never reaches git"
|
|
|
|
# The checker is a separate command, so its absence has to refuse the URL rather
|
|
# than wave it through to git.
|
|
if install_theme "https://github.com/example/omarchy-cool-theme.git" "$mock_bin:$PATH"; then
|
|
fail "omarchy-theme-install refuses a URL it cannot check"
|
|
fi
|
|
|
|
[[ ! -s $git_calls ]] ||
|
|
fail "omarchy-theme-install refuses an unchecked URL before running git" "$(cat "$git_calls")"
|
|
|
|
pass "a missing url checker refuses the URL instead of cloning it"
|
|
|
|
# A URL whose derived name would escape the themes directory.
|
|
for url in "https://example.com/..git" "https://example.com/.git"; do
|
|
if install_theme "$url"; then
|
|
fail "omarchy-theme-install refuses the derived name from '$url'"
|
|
fi
|
|
|
|
[[ ! -s $git_calls ]] || fail "omarchy-theme-install refuses '$url' before running git" "$(cat "$git_calls")"
|
|
done
|
|
|
|
pass "a URL whose name would climb out of the themes directory never reaches git"
|
|
|
|
# The derived name outlives the clone: it is the theme's directory name, and
|
|
# Style > Unlock builds a command line out of the name the picker returned. A
|
|
# repo whose name carries shell syntax would hand that picker its own command,
|
|
# so the name is refused here rather than quoted at each place it lands.
|
|
for url in \
|
|
"https://example.com/omarchy-a';id;'b-theme.git" \
|
|
'https://example.com/a$(id).git' \
|
|
'https://example.com/a`id`.git' \
|
|
"https://example.com/a b.git" \
|
|
"https://example.com/-a.git"; do
|
|
if install_theme "$url"; then
|
|
fail "omarchy-theme-install refuses the derived name from '$url'"
|
|
fi
|
|
|
|
[[ ! -s $git_calls ]] || fail "omarchy-theme-install refuses '$url' before running git" "$(cat "$git_calls")"
|
|
done
|
|
|
|
pass "a URL whose name would be shell syntax never reaches git"
|
|
|
|
# And the check is an allowlist, so the punctuation a real theme name uses has
|
|
# to keep working.
|
|
install_theme "https://github.com/example/omarchy-tokyo_night.2-theme.git" ||
|
|
fail "omarchy-theme-install accepts the punctuation a theme name uses"
|
|
grep -Fq "/themes/tokyo_night.2" "$git_calls" ||
|
|
fail "omarchy-theme-install derives a name carrying an underscore and a dot" "$(cat "$git_calls")"
|
|
|
|
pass "a theme name may still hold an underscore, a dot, and a dash"
|
|
|
|
# A plus is neither path-climb nor shell syntax, and a leading underscore is
|
|
# neither the `..` climb nor the dash that reads as an option, so the allowlist
|
|
# keeps both rather than stranding a repo that names itself with them.
|
|
install_theme "https://github.com/example/omarchy-c++-theme.git" ||
|
|
fail "omarchy-theme-install accepts a name holding a plus"
|
|
grep -Fq "/themes/c++" "$git_calls" ||
|
|
fail "omarchy-theme-install derives a name carrying a plus" "$(cat "$git_calls")"
|
|
|
|
install_theme "https://github.com/example/_private.git" ||
|
|
fail "omarchy-theme-install accepts a name starting with an underscore"
|
|
grep -Fq "/themes/_private" "$git_calls" ||
|
|
fail "omarchy-theme-install derives a name starting with an underscore" "$(cat "$git_calls")"
|
|
|
|
pass "a plus and a leading underscore are still usable theme names"
|
|
|
|
# git reads a colon before any slash as the scp-style separator, so the path
|
|
# after it does not have to hold one. Without that reading, the whole URL becomes
|
|
# the theme name and the allowlist above refuses a repo that clones fine.
|
|
install_theme "git@example.com:omarchy-blue-theme.git" ||
|
|
fail "omarchy-theme-install accepts a home-relative scp-style URL"
|
|
grep -Fq "/themes/blue" "$git_calls" ||
|
|
fail "omarchy-theme-install names the theme after the repo, not the whole URL" "$(cat "$git_calls")"
|
|
|
|
# A colon that is part of a local path, not an scp separator, keeps its prefix.
|
|
install_theme "/srv/git:mirrors/omarchy-blue-theme.git" ||
|
|
fail "omarchy-theme-install accepts a local path holding a colon"
|
|
grep -Fq "/themes/blue" "$git_calls" ||
|
|
fail "omarchy-theme-install reads a colon after a slash as part of the path" "$(cat "$git_calls")"
|
|
|
|
pass "an scp-style URL with no slash after the colon still names the theme"
|
|
|
|
# The allowlist is a bracket range, and a range follows the locale's collation
|
|
# rather than ASCII: under en_US.UTF-8 an unpinned `[a-z]` takes in `é`, so the
|
|
# same URL would install on one desktop and be refused on the next.
|
|
if locale -a 2>/dev/null | grep -qix 'en_US.utf-\?8'; then
|
|
for locale_name in C en_US.UTF-8; do
|
|
if LC_ALL=$locale_name install_theme "https://github.com/example/omarchy-café-theme.git"; then
|
|
fail "omarchy-theme-install refuses a non-ASCII theme name under LC_ALL=$locale_name" "$(cat "$git_calls")"
|
|
fi
|
|
|
|
[[ ! -s $git_calls ]] ||
|
|
fail "omarchy-theme-install refuses a non-ASCII name before running git" "$(cat "$git_calls")"
|
|
done
|
|
|
|
pass "the accepted set does not move with the desktop's locale"
|
|
else
|
|
pass "no en_US.UTF-8 locale; skipping the locale-pinning check"
|
|
fi
|
|
|
|
# basename reads a leading dash as an option once the scp-style prefix is gone.
|
|
install_theme "host:-s/foo.git" || fail "omarchy-theme-install accepts a normal scp-style URL"
|
|
grep -Fq -- "-- host:-s/foo.git" "$git_calls" || fail "omarchy-theme-install passes the URL after --" "$(cat "$git_calls")"
|
|
grep -Fq "/themes/foo" "$git_calls" || fail "omarchy-theme-install derives 'foo', not '.git'" "$(cat "$git_calls")"
|
|
|
|
pass "a dash inside the path does not become a basename option"
|
|
|
|
# And the ordinary case still works.
|
|
install_theme "https://github.com/example/omarchy-cool-theme.git" || fail "omarchy-theme-install clones a normal URL"
|
|
grep -Fq "/themes/cool" "$git_calls" || fail "omarchy-theme-install derives the theme name" "$(cat "$git_calls")"
|
|
grep -Fxq "cool" "$theme_calls" || fail "omarchy-theme-install applies the theme it installed" "$(cat "$theme_calls")"
|
|
|
|
pass "an ordinary theme URL still clones and applies"
|
|
|
|
# omarchy-theme-remove joins its argument into the path it deletes.
|
|
remove_theme() {
|
|
: >"$theme_calls"
|
|
|
|
HOME="$test_tmp/home" PATH="$mock_bin:$PATH" OMARCHY_TEST_THEME_CALLS="$theme_calls" \
|
|
bash "$ROOT/bin/omarchy-theme-remove" "$1" >"$test_tmp/out" 2>&1 || return $?
|
|
}
|
|
|
|
canary="$test_tmp/home/.config/omarchy/canary"
|
|
printf 'still here\n' >"$canary"
|
|
|
|
for name in ".." "." "../../evil" ".git"; do
|
|
if remove_theme "$name"; then
|
|
fail "omarchy-theme-remove refuses the theme name '$name'"
|
|
fi
|
|
|
|
[[ -f $canary ]] || fail "omarchy-theme-remove refuses '$name' before removing anything"
|
|
done
|
|
|
|
pass "a theme name cannot climb out of the themes directory on the way to rm"
|