feat: detect Debian version from /etc/debian_version (#4569)

Signed-off-by: Keith Zantow <kzantow@gmail.com>
This commit is contained in:
Keith Zantow 2026-01-23 17:52:21 -05:00
parent 0773492f84
commit c744873ac9
No known key found for this signature in database
GPG Key ID: 735988DA57708682
6 changed files with 82 additions and 1 deletions

View File

@ -54,6 +54,12 @@ var identityFiles = []parseEntry{
// ///////////////////////////////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////////////////////////////
} }
// after a parser function returns a Release, it may have incomplete information; supplementers can be used to
// fill in missing details based on other files present in the filesystem
var supplementers = []func(file.Resolver, *Release){
supplementDebianVersion,
}
// IdentifyRelease parses distro-specific files to discover and raise linux distribution release details. // IdentifyRelease parses distro-specific files to discover and raise linux distribution release details.
func IdentifyRelease(resolver file.Resolver) *Release { func IdentifyRelease(resolver file.Resolver) *Release {
logger := log.Nested("operation", "identify-release") logger := log.Nested("operation", "identify-release")
@ -67,6 +73,9 @@ func IdentifyRelease(resolver file.Resolver) *Release {
for _, location := range locations { for _, location := range locations {
release := tryParseReleaseInfo(resolver, location, logger, entry) release := tryParseReleaseInfo(resolver, location, logger, entry)
if release != nil { if release != nil {
for _, supplementer := range supplementers {
supplementer(resolver, release)
}
return release return release
} }
} }

View File

@ -74,7 +74,7 @@ func TestIdentifyRelease(t *testing.T) {
}, },
}, },
{ {
fixture: "test-fixtures/os/debian", fixture: "test-fixtures/os/debian/from-os-release",
release: &Release{ release: &Release{
PrettyName: "Debian GNU/Linux 8 (jessie)", PrettyName: "Debian GNU/Linux 8 (jessie)",
Name: "Debian GNU/Linux", Name: "Debian GNU/Linux",
@ -87,6 +87,20 @@ func TestIdentifyRelease(t *testing.T) {
BugReportURL: "https://bugs.debian.org/", BugReportURL: "https://bugs.debian.org/",
}, },
}, },
{
fixture: "test-fixtures/os/debian/from-debian_version",
release: &Release{
PrettyName: "Distroless",
Name: "Debian GNU/Linux",
ID: "debian",
IDLike: nil,
Version: "10.8",
VersionID: "10.8",
HomeURL: "https://github.com/GoogleContainerTools/distroless",
SupportURL: "https://github.com/GoogleContainerTools/distroless/blob/master/README.md",
BugReportURL: "https://github.com/GoogleContainerTools/distroless/issues/new",
},
},
{ {
fixture: "test-fixtures/os/fedora", fixture: "test-fixtures/os/fedora",
release: &Release{ release: &Release{

View File

@ -0,0 +1,51 @@
package linux
import (
"io"
"regexp"
"strings"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file"
)
func supplementDebianVersion(resolver file.Resolver, release *Release) {
// we're only looking for version information for debian when none is present in /etc/os-release
if release.Version != "" || release.VersionID != "" || !strings.EqualFold(release.ID, "debian") {
return
}
// if we have a debian release with no version, look for a debian_version
locations, err := resolver.FilesByGlob("/etc/debian_version")
if err != nil {
log.Debugf("error reading /etc/debian_version: %v", err)
return
}
for _, location := range locations {
version := readDebianVersionFile(resolver, location)
if version != "" {
release.Version = version
release.VersionID = version
return // keep the first result
}
}
}
func readDebianVersionFile(resolver file.Resolver, location file.Location) string {
rdr, err := resolver.FileContentsByLocation(location)
if err != nil {
log.Debugf("error getting contents for %s: %v", location.RealPath, err)
return ""
}
defer internal.CloseAndLogError(rdr, location.RealPath)
contents, err := io.ReadAll(rdr)
if err != nil {
log.Debugf("error reading %s: %v", location.RealPath, err)
return ""
}
version := strings.TrimSpace(string(contents))
if regexp.MustCompile(`^\d+(?:\.\d+)?$`).MatchString(version) {
return version
}
return ""
}

View File

@ -0,0 +1,6 @@
PRETTY_NAME="Distroless"
NAME="Debian GNU/Linux"
ID="debian"
HOME_URL="https://github.com/GoogleContainerTools/distroless"
SUPPORT_URL="https://github.com/GoogleContainerTools/distroless/blob/master/README.md"
BUG_REPORT_URL="https://github.com/GoogleContainerTools/distroless/issues/new"