mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
119 lines
2.6 KiB
Go
119 lines
2.6 KiB
Go
package distro
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/anchore/imgbom/imgbom/scope"
|
|
"github.com/anchore/imgbom/internal/log"
|
|
"github.com/anchore/stereoscope/pkg/file"
|
|
)
|
|
|
|
// returns a distro or nil
|
|
type parseFunc func(string) *Distro
|
|
|
|
// Identify parses distro-specific files to determine distro metadata like version and release
|
|
func Identify(s scope.Scope) *Distro {
|
|
identityFiles := map[file.Path]parseFunc{
|
|
"/etc/os-release": parseOsRelease,
|
|
// Debian and Debian-based distros have the same contents linked from this path
|
|
"/usr/lib/os-release": parseOsRelease,
|
|
"/bin/busybox": parseBusyBox,
|
|
}
|
|
|
|
for path, fn := range identityFiles {
|
|
refs, err := s.FilesByPath(path)
|
|
if err != nil {
|
|
log.Errorf("unable to get path refs from %s: %s", path, err)
|
|
return nil
|
|
}
|
|
|
|
if len(refs) == 0 {
|
|
continue
|
|
}
|
|
|
|
for _, ref := range refs {
|
|
contents, err := s.MultipleFileContentsByRef(ref)
|
|
content, ok := contents[ref]
|
|
|
|
if !ok {
|
|
log.Infof("no content present for ref: %s", ref)
|
|
continue
|
|
}
|
|
|
|
if err != nil {
|
|
log.Debugf("unable to get contents from %s: %s", path, err)
|
|
continue
|
|
}
|
|
|
|
if content == "" {
|
|
log.Debugf("no contents in file, skipping: %s", path)
|
|
continue
|
|
}
|
|
|
|
distro := fn(content)
|
|
|
|
if distro == nil {
|
|
continue
|
|
}
|
|
|
|
return distro
|
|
}
|
|
}
|
|
// TODO: is it useful to know partially detected distros? where the ID is known but not the version (and viceversa?)
|
|
distro := NewUnknownDistro()
|
|
return &distro
|
|
}
|
|
|
|
func assembleDistro(name, version string) *Distro {
|
|
distroType, ok := Mappings[name]
|
|
|
|
// Both distro and version must be present
|
|
if len(name) == 0 || len(version) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if ok {
|
|
distro, err := NewDistro(distroType, version)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return &distro
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseOsRelease(contents string) *Distro {
|
|
id, vers := "", ""
|
|
for _, line := range strings.Split(contents, "\n") {
|
|
parts := strings.Split(line, "=")
|
|
prefix := parts[0]
|
|
value := strings.ReplaceAll(parts[len(parts)-1], `"`, "")
|
|
|
|
switch prefix {
|
|
case "ID":
|
|
id = strings.TrimSpace(value)
|
|
case "VERSION_ID":
|
|
vers = strings.TrimSpace(value)
|
|
}
|
|
}
|
|
|
|
return assembleDistro(id, vers)
|
|
}
|
|
|
|
var busyboxVersionMatcher = regexp.MustCompile(`BusyBox v[\d\.]+`)
|
|
|
|
func parseBusyBox(contents string) *Distro {
|
|
matches := busyboxVersionMatcher.FindAllString(contents, -1)
|
|
for _, match := range matches {
|
|
parts := strings.Split(match, " ")
|
|
version := strings.ReplaceAll(parts[1], "v", "")
|
|
distro := assembleDistro("busybox", version)
|
|
if distro != nil {
|
|
return distro
|
|
}
|
|
}
|
|
return nil
|
|
}
|