From d61c2cada3e4b21ce44b6820bcf37ba3e861c3b8 Mon Sep 17 00:00:00 2001 From: Mayank Sekhar Date: Wed, 30 Sep 2026 22:21:03 +0530 Subject: [PATCH] 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 --- .../binary/classifier_cataloger_test.go | 24 ++++++++++++++++++ syft/pkg/cataloger/binary/classifiers.go | 11 ++++++-- .../snippets/traefik/2.11.1/linux-arm/traefik | Bin 0 -> 279 bytes .../traefik/3.6.8/linux-s390x/traefik | Bin 0 -> 280 bytes 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 syft/pkg/cataloger/binary/testdata/classifiers/snippets/traefik/2.11.1/linux-arm/traefik create mode 100644 syft/pkg/cataloger/binary/testdata/classifiers/snippets/traefik/3.6.8/linux-s390x/traefik diff --git a/syft/pkg/cataloger/binary/classifier_cataloger_test.go b/syft/pkg/cataloger/binary/classifier_cataloger_test.go index 4b4e2ed7d..54cd04f3b 100644 --- a/syft/pkg/cataloger/binary/classifier_cataloger_test.go +++ b/syft/pkg/cataloger/binary/classifier_cataloger_test.go @@ -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{ diff --git a/syft/pkg/cataloger/binary/classifiers.go b/syft/pkg/cataloger/binary/classifiers.go index 17834ab0c..26929ae2a 100644 --- a/syft/pkg/cataloger/binary/classifiers.go +++ b/syft/pkg/cataloger/binary/classifiers.go @@ -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[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[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00`), + m.FileContentsVersionMatcher( + `(?m)(\x00v?|\x{FFFD}.?)(?P[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), diff --git a/syft/pkg/cataloger/binary/testdata/classifiers/snippets/traefik/2.11.1/linux-arm/traefik b/syft/pkg/cataloger/binary/testdata/classifiers/snippets/traefik/2.11.1/linux-arm/traefik new file mode 100644 index 0000000000000000000000000000000000000000..f0be458f085acc558ee8c1f5de24b738de5318e4 GIT binary patch literal 279 zcmZvWJx;_h5JtTn@+mB(U&Z6!NdugLj&3|2vk@VQSWAdg&~Ok=z?o?2*aAAf@;&M4 zTj@zA)1OJ&{HV9q&eA7?E+YoWdXe>^pC;q8KCkn>OTRl2ggBW1&c#}ME+b2E&AJ#` zbV(`@S%gAVIJm;>gN-2oCw3JDxFkTdy3LFH#j4NMVk8QsCh(l0I8T*u%#t(2F}4ef z9KE+bp=4y2ykKERjF_|@hQXBAF6Q^9>6>ZWvTQHrqO^HG+$go*e!qQuDs_JaM{AE( Kse}4&U)Mi9Hc8e1 literal 0 HcmV?d00001 diff --git a/syft/pkg/cataloger/binary/testdata/classifiers/snippets/traefik/3.6.8/linux-s390x/traefik b/syft/pkg/cataloger/binary/testdata/classifiers/snippets/traefik/3.6.8/linux-s390x/traefik new file mode 100644 index 0000000000000000000000000000000000000000..0141469f67050af9c17f554df4a88366d320ece8 GIT binary patch literal 280 zcmX|5J5Izf6m;ztN(xI`BCY)V{7V`xK*tGeKR-4iBrDbuqN3pjT!5Q!C5r8Xk$_?} zKWQ{edaR>rPpR_sAXny7ZKDD-V8Plb=epdt!>GDEEz|K>+ub1<=SP(-6iulFj~o#k z7Ba*c$YM$qL!s&^`fM_oFz7gt4N)gk9cI?iNS@~UHx>c%K%_%~l5%#8-gC7WDi{V# zh#~Yb4Fe72Oki&*nP4$G>Y