Harden the test's temp-file handling against a symlink race

The /tmp-fallback case did check-then-create on a fixed /tmp name, a
TOCTOU/symlink race, and the EXIT trap only cleaned $TMPDIR. Create the decoy
atomically with noclobber (O_EXCL) so it refuses to overwrite an existing file
or follow a symlink at that path, and remove it on exit only when this test
created it. The fixed path is required (it is exactly the path the old code
would form), so a random mktemp name cannot replace it. Addresses the Copilot
review on #8198; the wrapper fix is unchanged.
This commit is contained in:
bastidotnet
2026-08-25 13:51:39 +02:00
parent fe56d68e90
commit e53548fae2
@@ -5,7 +5,20 @@ set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
# The /tmp-fallback case (below) must place its decoy at exactly the fixed path the
# old wrapper would have formed, so it cannot use a random mktemp name. Track whether
# we created it and remove it on exit only then -- never touch a path we did not create.
tmp_cache="/tmp/omarchy-brightness-display-apple.device"
created_tmp_cache=0
cleanup() {
rm -rf "$TMPDIR"
# Remove the /tmp decoy only if this test is the one that created it.
if (( created_tmp_cache )); then
rm -f "$tmp_cache"
fi
}
trap cleanup EXIT
# Stubs on PATH: drop sudo so asdcontrol runs directly, record every asdcontrol
# invocation, make detection deterministic by having --detect report no device,
@@ -102,14 +115,14 @@ else
fi
# --- With no XDG_RUNTIME_DIR, the predictable /tmp cache is not consulted ------
# Guard on the real path not pre-existing so we never clobber a live cache, and
# remove what we create. Old code read /tmp and would hand /dev/null to
# asdcontrol; new code has no cache path at all when XDG_RUNTIME_DIR is unset.
tmp_cache="/tmp/omarchy-brightness-display-apple.device"
if [[ -e $tmp_cache ]]; then
pass "$tmp_cache already exists on this host; skipping the /tmp-fallback case"
else
printf '%s\n' "/dev/null" >"$tmp_cache"
# Create the decoy atomically with noclobber (O_EXCL) instead of check-then-create:
# this refuses to overwrite an existing file or follow a symlink at the fixed path,
# closing the TOCTOU/symlink race. The fixed path is required -- it is exactly the
# path the old code would have formed, so a decoy anywhere else would prove nothing.
# If the path is already taken, skip rather than touch it; the EXIT trap removes the
# decoy only when this test created it.
if ( set -C; printf '%s\n' "/dev/null" >"$tmp_cache" ) 2>/dev/null; then
created_tmp_cache=1
output=$(run_wrapper "" "+5%")
used=1
grep -qF -- "/dev/null -- +5%" "$asd_log" || used=0
@@ -117,4 +130,6 @@ else
(( used == 0 )) ||
fail "wrapper consulted the world-writable /tmp cache with no XDG_RUNTIME_DIR" "$output"
pass "wrapper ignores the /tmp cache path when XDG_RUNTIME_DIR is unset"
else
pass "$tmp_cache already present or not safely creatable; skipping the /tmp-fallback case"
fi