fix(e2e): refuse a target naming only a major version

`cut -f2` prints the whole line when that line carries no delimiter, so a
single-component target like "v1" came back with min=1 and was resolved as if
it were v1.1 — walking down to the v1.0 line and printing a real-looking
baseline with exit 0. The lane would then install that baseline and report on
an upgrade path nobody asked for, which is worse than failing.

Pass -s so the delimiter-less line is suppressed, min comes back empty, and the
existing parse guard rejects it. Two-component targets are unaffected: the
guard rejects a missing minor, not a missing patch, and v1.6 still resolves.

In practice the lane only ever passes "" or a target derived from a
release-X.Y.Z branch, so this was latent rather than live.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
This commit is contained in:
Myasnikov Daniil committed 2026-08-25 15:20:16 +05:00
1 parent 2bd363ccbe
commit 0c969ffcfb
2 files changed
+31 -1

No files matched your search

+6 -1
View File
@@ -53,7 +53,12 @@ fi
ver="${target#v}"
ver="${ver%%-*}" # strip the -rc.N / -beta.N suffix
maj=$(printf '%s' "$ver" | cut -d. -f1)
min=$(printf '%s' "$ver" | cut -d. -f2)
# -s is load-bearing: without it `cut -f2` on a line carrying no delimiter
# prints the WHOLE line, so a single-component target like "v1" yielded min=1
# and was silently resolved as if it were v1.1 — printing a plausible baseline
# with exit 0 instead of rejecting an unparseable target. -s suppresses the
# delimiter-less line, min comes back empty, and the guard below catches it.
min=$(printf '%s' "$ver" | cut -s -d. -f2)
case "${maj}:${min}" in
*[!0-9:]*|:*|*:) echo "ERROR: cannot parse target version '$target'" >&2; exit 1 ;;
esac
+25
View File
@@ -125,3 +125,28 @@ EOF
false
fi
}
# A target naming only a major ("v1") is not a version this resolver can act on,
# and must be refused rather than guessed at. It used to resolve: `cut -f2`
# without -s prints the whole line when it carries no delimiter, so min came
# back as "1" and v1 was silently treated as v1.1 — emitting a real-looking
# baseline from the v1.0 line with exit 0. A wrong-but-plausible baseline is
# worse than an error here, because the lane would install it and report on an
# upgrade path nobody asked for.
@test "a target naming only a major version is an error, not a guess" {
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
_fixture > "$tmp/tags"
for bad in v1 1; do
if out=$(hack/upgrade-prev-version.sh "$bad" "$tmp/tags" 2>/dev/null); then
echo "FAIL: target '$bad' should be refused, got '$out'" >&2
false
fi
done
# The two-component form is legitimate and must keep resolving — the guard
# rejects a missing minor, not a missing patch.
out=$(hack/upgrade-prev-version.sh "v1.6" "$tmp/tags")
[ -n "$out" ]
}