Harden script approvals and add migration support

This commit is contained in:
deepend committed 2026-10-22 18:55:21 -06:00
1 parent 2afad63802
commit 426ea638ae
7 files changed
+963 -339

No files matched your search

+100
View File
@@ -0,0 +1,100 @@
#!/bin/sh
# Full test of the approval path. Requires root, runuser, and getent.
set -eu
[ "$(id -u)" -eq 0 ] || { echo 'Run as root' >&2; exit 1; }
for cmd in runuser timeout flock sha256sum getent; do
command -v "$cmd" >/dev/null || { echo "Missing $cmd" >&2; exit 1; }
done
cd "$(dirname "$0")/.."
tmp=$(mktemp -d /tmp/tilde-launcher-test.XXXXXXXX)
user=launcher-test-$$
cleanup() {
userdel "$user" >/dev/null 2>&1 || :
rm -rf "$tmp"
}
trap 'cleanup' EXIT HUP INT TERM
chmod 0755 "$tmp"
root=$tmp/tilde
home=$tmp/home
useradd -M -d "$home" -s /bin/sh "$user"
uid=$(id -u "$user")
gid=$(id -g "$user")
mkdir -m 0755 "$home"
mkdir -m 0755 "$home/bin"
chown -R "$uid:$gid" "$home"
cat > "$tmp/sendmail" <<'EOF'
#!/bin/sh
cat >/dev/null
EOF
chmod 0755 "$tmp/sendmail"
sed -e "s|^ROOT=/tilde$|ROOT=$root|" \
-e "s|/usr/sbin/sendmail|$tmp/sendmail|g" tilde > "$tmp/launcher"
chmod 0755 "$tmp/launcher"
"$tmp/launcher" init >/dev/null
cat > "$home/bin/example" <<'EOF'
#!/bin/sh
printf 'v1:%s\n' "$*"
EOF
chmod 0755 "$home/bin/example"
chown "$uid:$gid" "$home/bin/example"
printf 'y\nexample\nA test script\ny\n' |
runuser -u "$user" -- env HOME="$home" "$tmp/launcher" submit >/dev/null
printf 'a\n' | "$tmp/launcher" approve >/dev/null
[ "$("$tmp/launcher" run example argument)" = 'v1:argument' ]
[ "$("$tmp/launcher" about example | head -n 1)" = 'A test script' ]
# Changing the user's script cannot change what everyone else executes.
cat > "$home/bin/example" <<'EOF'
#!/bin/sh
printf 'v2:%s\n' "$*"
EOF
chmod 0755 "$home/bin/example"
chown "$uid:$gid" "$home/bin/example"
[ "$("$tmp/launcher" run example argument)" = 'v1:argument' ]
printf 'Updated description\ny\ny\n' |
runuser -u "$user" -- env HOME="$home" "$tmp/launcher" update example >/dev/null
printf 'a\n' | "$tmp/launcher" approve >/dev/null
[ "$("$tmp/launcher" run example argument)" = 'v2:argument' ]
# The retired version is not executable by ordinary users.
# Verify the old version's permissions without making any assumptions about its hash.
for previous in "$root/approved/example"/*; do
[ -f "$previous" ] || continue
if [ "$(readlink -f "$root/bin/example")" != "$previous" ]; then
[ "$(stat -c %a "$previous")" = 700 ]
fi
done
# Cancellation must not revoke anything.
printf 'n\n' | "$tmp/launcher" revoke example >/dev/null
[ -x "$root/bin/example" ]
printf 'y\nSecurity review\n' | "$tmp/launcher" revoke example >/dev/null
[ ! -e "$root/bin/example" ] && [ ! -L "$root/bin/example" ]
[ "$(stat -c %a "$(find "$root/approved/example" -maxdepth 1 -type f | tail -n 1)")" = 700 ]
# Reject a submission, then allow a corrected resubmission.
cat > "$home/bin/rejected" <<'EOF'
#!/bin/sh
echo rejected-v1
EOF
chmod 0755 "$home/bin/rejected"
chown "$uid:$gid" "$home/bin/rejected"
printf 'y\nrejected\nFirst version\ny\n' |
runuser -u "$user" -- env HOME="$home" "$tmp/launcher" submit >/dev/null
printf 'r\nNeeds revision\n' | "$tmp/launcher" approve >/dev/null
[ ! -e "$root/bin/rejected" ]
runuser -u "$user" -- env HOME="$home" "$tmp/launcher" status rejected | grep -q 'rejected'
printf 'y\nrejected\nFixed version\ny\ny\n' |
runuser -u "$user" -- env HOME="$home" "$tmp/launcher" submit >/dev/null
printf 'a\n' | "$tmp/launcher" approve >/dev/null
[ "$("$tmp/launcher" run rejected)" = 'rejected-v1' ]
# Migrating a legacy symlink requires review and freezes the code.
cat > "$home/bin/legacy" <<'EOF'
#!/bin/sh
echo legacy-v1
EOF
chmod 0755 "$home/bin/legacy"
chown "$uid:$gid" "$home/bin/legacy"
ln -s "$home/bin/legacy" "$root/bin/legacy"
printf 'A legacy script\n' > "$root/descriptions/legacy"
printf 'a\n' | "$tmp/launcher" migrate legacy >/dev/null
[ "$("$tmp/launcher" run legacy)" = 'legacy-v1' ]
printf '#!/bin/sh\necho legacy-v2\n' > "$home/bin/legacy"
[ "$("$tmp/launcher" run legacy)" = 'legacy-v1' ]
printf 'Integration tests passed.\n'
Executable
+15
View File
@@ -0,0 +1,15 @@
#!/bin/sh
set -eu
cd "$(dirname "$0")/.."
sh -n tilde
[ "$(./tilde --version)" = 0.2.0 ]
./tilde --help | grep -q 'migrate'
if ./tilde run '../etc/passwd' >/dev/null 2>&1; then
printf 'Path traversal was accepted\n' >&2
exit 1
fi
if ./tilde revoke '../etc/passwd' >/dev/null 2>&1; then
printf 'Unsafe revocation was accepted\n' >&2
exit 1
fi
printf 'Smoke tests passed.\n'