fix(binary): correct traefik version detection on big-endian arches (#5281)

The traefik-binary classifier regex required a NUL byte or replacement
character immediately before the version string. On some architectures
(e.g. s390x), the real version string in the binary has no such prefix,
so the classifier instead matched an earlier, unrelated NUL-prefixed
numeric string elsewhere in the binary (e.g. '2.7.0' from an unrelated
printf-style format constant), producing an incorrect version.

This adds an additional pattern, tried first via MatchAny, that matches
version strings terminated by two NUL bytes without requiring a NUL
prefix. The original pattern remains as a fallback for older traefik
binaries (e.g. v1.x) that use single-NUL termination.

Fixes #4980

Signed-off-by: Mayank Sekhar <mayankshekharsingh@gmail.com>
This commit is contained in:
Mayank Sekhar
2026-09-30 16:51:03 +00:00
committed by GitHub
parent 0bbcff1ae3
commit d61c2cada3
4 changed files with 33 additions and 2 deletions
@@ -407,6 +407,30 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("traefik-binary"),
},
},
{
// regression test: big-endian architectures (e.g. s390x) previously matched an unrelated
// single-NUL-terminated numeric string elsewhere in the binary instead of the real version
logicalFixture: "traefik/3.6.8/linux-s390x",
expected: pkg.Package{
Name: "traefik",
Version: "3.6.8",
Type: "binary",
PURL: "pkg:generic/traefik@3.6.8",
Locations: locations("traefik"),
Metadata: metadata("traefik-binary"),
},
},
{
logicalFixture: "traefik/2.11.1/linux-arm",
expected: pkg.Package{
Name: "traefik",
Version: "2.11.1",
Type: "binary",
PURL: "pkg:generic/traefik@2.11.1",
Locations: locations("traefik"),
Metadata: metadata("traefik-binary"),
},
},
{
logicalFixture: "memcached/1.6.18/linux-amd64",
expected: pkg.Package{
+9 -2
View File
@@ -284,11 +284,18 @@ func DefaultClassifiers() []binutils.Classifier {
{
Class: "traefik-binary",
FileGlob: "**/traefik",
EvidenceMatcher: m.FileContentsVersionMatcher(
EvidenceMatcher: binutils.MatchAny(
// on some architectures (e.g. s390x) the real version string isn't NUL-prefixed
// and is instead terminated by two NUL bytes; checking this pattern first avoids
// matching an unrelated single-NUL-terminated numeric string elsewhere in the binary
m.FileContentsVersionMatcher(
`(?m)(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00\x00`),
// [NUL]v1.7.34[NUL]
// [NUL]2.9.6[NUL]
// 3.0.4[NUL]
`(?m)(\x00v?|\x{FFFD}.?)(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00`),
m.FileContentsVersionMatcher(
`(?m)(\x00v?|\x{FFFD}.?)(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00`),
),
Package: "traefik",
PURL: mustPURL("pkg:generic/traefik@version"),
CPEs: singleCPE("cpe:2.3:a:traefik:traefik:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),