Fix package-verify false negatives: symlink traversal + prefix collision
Lint / ShellCheck (push) Successful in 13s
Lint / Validate .plg XML (push) Successful in 13s
Lint / EditorConfig (push) Successful in 5s

podman-verify-packages.sh/podman-update-packages.sh reported every
package as "not installed" right after a genuinely successful install.
Root cause: /var/log/packages is itself a symlink on Unraid (->
../lib/pkgtools/packages), and GNU find's default -P mode doesn't
descend into a symlinked starting path at all without -L — confirmed
live by running the exact same find both with and without -L against a
host where the packages had just installed successfully.

Fixing that alone surfaced a second, previously-masked bug: "podman"'s
own glob also matches podman-compose's file (a literal prefix
collision), and find's unsorted output let podman-compose's record
silently win podman's own check.

Both fixed once via a new shared podman_find_installed_package_record()
helper in podman-common.sh, rather than separately in each caller.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 22:10:44 +00:00
co-authored by Claude Sonnet 5
parent d931d8e9e9
commit 9d46547ef4
4 changed files with 74 additions and 2 deletions
+49
View File
@@ -195,6 +195,55 @@ podman_path_has_real_mount_ancestor() {
[ "$parent" != "/" ] && [ "$(stat -c %d "$parent")" != "$(stat -c %d /)" ]
}
# -----------------------------------------------------------------------------
# podman_find_installed_package_record <name> <all-package-names...>
#
# Prints the /var/log/packages/<name>-... record for <name> (empty/failure
# if not installed). Two real bugs, both found live on an actual host,
# fixed here once instead of separately in every script that needs this:
#
# -L: /var/log/packages is itself a symlink on Unraid (->
# ../lib/pkgtools/packages) — without it, GNU find's default -P mode
# doesn't descend into it AT ALL (it returns just the symlink itself and
# nothing below it), so every package was reported "not installed"
# regardless of what had actually just been installed.
#
# Prefix collision: a plain `-name "$name-*"` glob for "podman" also
# matches podman-compose's file ("podman" is a literal prefix of
# "podman-compose"), and find's output order isn't sorted, so whichever
# one happened to come back first silently won — on a real host, that
# was podman-compose's record, reported as if it were podman's own.
# Explicitly skip any match that actually belongs to a different, more
# specific name also in the given package list.
# -----------------------------------------------------------------------------
podman_find_installed_package_record() {
local name="$1"
shift
local all_names=("$@")
local candidate other belongs_to_other base
while IFS= read -r candidate; do
[ -n "$candidate" ] || continue
base=$(basename "$candidate")
belongs_to_other=0
for other in "${all_names[@]}"; do
# Only a LONGER (more specific) other name can steal a match —
# "podman" is itself a prefix of "podman-compose", so without the
# length check, searching for "podman-compose" would wrongly
# exclude its own, genuinely correct record too (found live: this
# exact over-correction on the very first fix attempt).
if [ "$other" != "$name" ] && [ "${#other}" -gt "${#name}" ] && [ "${base#"$other"-}" != "$base" ]; then
belongs_to_other=1
break
fi
done
if [ "$belongs_to_other" -eq 0 ]; then
echo "$candidate"
return 0
fi
done < <(find -L /var/log/packages -maxdepth 1 -name "${name}-*" -print 2> /dev/null)
return 1
}
# -----------------------------------------------------------------------------
# podman_require_command <binary>
#