fix(updater): anchor on the version sentinel, not the first image line

Stoat wore MongoDB's identity. Its services are named database / api /
events / …, so there is no stoat-service for updaterPrimaryImage to
match, and the fallback took the FIRST image line — mongo:8.0. Every
downstream fact inherited that: the app's version read 8.0 instead of
v0.15.1, its "8.3 available" chip was a MongoDB major dressed as a Stoat
release, its CVE scan covered mongo and none of the nine Stoat images,
and pressing Upgrade would have laddered the database 8.0 -> 8.3 beneath
a live sixteen-service stack.

The compose already says which image is the app's: every image line
carries a #LIBREPORTAL|<KEY>_VERSION_TAG| marker, and the one keyed on
the BARE app name (STOAT_VERSION_TAG, not STOAT_MONGO_VERSION_TAG) is by
construction the app's own version. 37 of 38 apps have exactly one; only
libreportal lacks it, and the scan skips that app anyway.

Ask the sentinel first, keep <slug>-service and first-line as fallbacks.
Verified across the catalogue: identical anchor for every app except
stoat, which is corrected. This is the ollama mislabel of P0 recurring
through a different hole — positional guessing — closed with the
metadata that was already there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
librelad 2026-08-19 04:49:03 +01:00
parent 9a7df822dc
commit 1d0f043bb5

View File

@ -35,12 +35,33 @@
# sentinel added to anchor lines) plus surrounding whitespace.
_updaterCleanImageRef() { sed -E 's/^\s*image:\s*//; s/["'"'"']//g; s/[[:space:]]*#.*$//; s/[[:space:]]*$//'; }
updaterPrimaryImage() {
local app="$1" compose="$2" svc img
svc="${app//_/-}-service"
img="$(awk -v s="$svc" '
m && /^[[:space:]]*image:/ { print; exit }
$0 ~ ("^[[:space:]]*" s ":") { m=1 }
' "$compose" 2>/dev/null | _updaterCleanImageRef)"
local app="$1" compose="$2" svc img up
up="$(printf '%s' "$app" | tr '[:lower:]' '[:upper:]')"
# The app's own version sentinel names the anchor outright. Every image line
# carries a #LIBREPORTAL|<KEY>_VERSION_TAG|<tag> marker, and the one whose
# key is the BARE app name (STOAT_VERSION_TAG, not STOAT_MONGO_VERSION_TAG)
# is by construction the app's own version — 37 of 38 apps have exactly one.
#
# Ask it first, because the <slug>-service convention below is not universal
# and fails silently when it is absent. Stoat names its services database /
# api / events / …, so the fallback picked the FIRST image line — mongo:8.0 —
# and the whole app then wore MongoDB's identity: version 8.0 instead of
# v0.15.1, "8.3 available" meaning a MongoDB major, CVE scans covering mongo
# and none of the nine Stoat images, and an Upgrade button that would have
# walked the database 8.0 → 8.3 under a live sixteen-service stack.
img="$(grep -E "#LIBREPORTAL\|${up}_VERSION_TAG\|" "$compose" 2>/dev/null | head -1 | _updaterCleanImageRef)"
# Then the <slug>-service convention (underscores become hyphens), which
# resolves ollama to ollama-service rather than its open-webui companion.
if [ -z "$img" ]; then
svc="${app//_/-}-service"
img="$(awk -v s="$svc" '
m && /^[[:space:]]*image:/ { print; exit }
$0 ~ ("^[[:space:]]*" s ":") { m=1 }
' "$compose" 2>/dev/null | _updaterCleanImageRef)"
fi
[ -n "$img" ] || img="$(grep -m1 -E '^\s*image:' "$compose" 2>/dev/null | _updaterCleanImageRef)"
printf '%s' "$img"
}