Quote mise-install arguments and refuse unusable command names
The wrapper is written through an unquoted heredoc, so the package and bin names land in it as shell source. A package name carrying shell characters became code that ran every time the wrapper ran. The command name is used raw as a file name under ~/.local/bin, so a slash in it wrote and removed somewhere else entirely. Quote both values with printf %q, and refuse command names that are not plain file names before anything is removed or written.
This commit is contained in:
1 parent
43bfe9b9d8
commit
b6a8f2453f
2 files changed
+105
-2
No files matched your search
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/base-test.sh"
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
|
||||
home="$tmpdir/home"
|
||||
stub_bin="$tmpdir/bin"
|
||||
mkdir -p "$home" "$stub_bin"
|
||||
|
||||
# Stands in for the real mise so a generated wrapper can be run and asked what
|
||||
# arguments it passed on.
|
||||
cat >"$stub_bin/mise" <<'SH'
|
||||
#!/bin/bash
|
||||
|
||||
printf 'mise' >>"$OMARCHY_MISE_TEST_LOG"
|
||||
for arg in "$@"; do
|
||||
printf '\t%s' "$arg" >>"$OMARCHY_MISE_TEST_LOG"
|
||||
done
|
||||
printf '\n' >>"$OMARCHY_MISE_TEST_LOG"
|
||||
SH
|
||||
chmod +x "$stub_bin/mise"
|
||||
|
||||
install_wrapper() {
|
||||
HOME="$home" "$ROOT/bin/omarchy-mise-install" "$@"
|
||||
}
|
||||
|
||||
# The ordinary case still works, and every call site in install/user/mise.sh
|
||||
# passes names of this shape.
|
||||
install_wrapper npm:playwright playwright >/dev/null
|
||||
[[ -x $home/.local/bin/playwright ]] ||
|
||||
fail "a normal install writes an executable wrapper"
|
||||
|
||||
log="$tmpdir/normal.log"
|
||||
: >"$log"
|
||||
OMARCHY_MISE_TEST_LOG="$log" PATH="$stub_bin:$PATH" "$home/.local/bin/playwright" >/dev/null
|
||||
grep -Fqx $'mise\tuse\t-g\t--quiet\tnpm:playwright' "$log" ||
|
||||
fail "the wrapper asks mise for the package it was given" "$(cat "$log")"
|
||||
|
||||
pass "a normal install writes a wrapper that names its package"
|
||||
|
||||
# A package name is data. Quoted with %q it reaches mise as one argument
|
||||
# instead of being read as shell source when the wrapper runs.
|
||||
install_wrapper 'npm:pkg$(touch '"$tmpdir"'/PWNED)end' hostile >/dev/null
|
||||
|
||||
log="$tmpdir/hostile.log"
|
||||
: >"$log"
|
||||
OMARCHY_MISE_TEST_LOG="$log" PATH="$stub_bin:$PATH" "$home/.local/bin/hostile" >/dev/null
|
||||
|
||||
[[ -e $tmpdir/PWNED ]] &&
|
||||
fail "a package name with shell characters does not run when the wrapper does" \
|
||||
"wrapper: $(cat "$home/.local/bin/hostile")"
|
||||
|
||||
grep -Fqx $'mise\tuse\t-g\t--quiet\tnpm:pkg$(touch '"$tmpdir"'/PWNED)end' "$log" ||
|
||||
fail "the package reaches mise whole" "$(cat "$log")"
|
||||
|
||||
pass "a package name with shell characters reaches mise as one argument"
|
||||
|
||||
# The command name is a file name under ~/.local/bin. These shapes escape it,
|
||||
# hide it, or make something that reads as an option.
|
||||
for name in ../escaped .hidden -dash; do
|
||||
if install_wrapper somepkg "$name" >/dev/null 2>"$tmpdir/err"; then
|
||||
fail "a command name of '$name' is refused"
|
||||
fi
|
||||
grep -Fq 'is not usable as a command name' "$tmpdir/err" ||
|
||||
fail "the refusal says why for '$name'" "$(cat "$tmpdir/err")"
|
||||
done
|
||||
|
||||
pass "command names that are not plain file names are refused"
|
||||
|
||||
# The refusal has to land before the rm, which would otherwise delete the
|
||||
# escaped path on its way to failing.
|
||||
victim="$tmpdir/victim"
|
||||
printf 'keep me\n' >"$victim"
|
||||
if install_wrapper somepkg "../../../..$victim" >/dev/null 2>&1; then
|
||||
fail "an escaping command name is refused"
|
||||
fi
|
||||
[[ -f $victim ]] ||
|
||||
fail "an escaping command name removes nothing outside ~/.local/bin"
|
||||
|
||||
pass "an escaping command name removes nothing outside ~/.local/bin"
|
||||
Reference in new issue
Block a user