Skip to content

Commit

Permalink
finalize label version and add release id to OS model (#2349)
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Goodman <[email protected]>
  • Loading branch information
wagoodman authored Dec 23, 2024
1 parent bf44e25 commit 3baa3d2
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 126 deletions.
30 changes: 9 additions & 21 deletions grype/db/v6/affected_package_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ type OSSpecifiers []*OSSpecifier

// OSSpecifier is a struct that represents a distro in a way that can be used to query the affected package store.
type OSSpecifier struct {
// Name of the distro as identified by the ID field in /etc/os-release
// Name of the distro as identified by the ID field in /etc/os-release (or similar normalized name, e.g. "oracle" instead of "ol")
Name string

// MajorVersion is the first field in the VERSION_ID field in /etc/os-release (e.g. 7 in "7.0.1406")
Expand All @@ -100,13 +100,9 @@ type OSSpecifier struct {
// MinorVersion is the second field in the VERSION_ID field in /etc/os-release (e.g. 0 in "7.0.1406")
MinorVersion string

// LabelVersion is mutually exclusive to MajorVersion and MinorVersion and tends to represent the
// VERSION_ID when it is not a version number (e.g. "edge" or "unstable")
// LabelVersion is a string that represents a floating version (e.g. "edge" or "unstable") or is the CODENAME field in /etc/os-release (e.g. "wheezy" for debian 7)
LabelVersion string

// Codename is the CODENAME field in /etc/os-release (e.g. "wheezy" for debian 7)
Codename string

// AllowMultiple specifies whether we intend to allow for multiple distro identities to be matched.
AllowMultiple bool
}
Expand All @@ -127,15 +123,15 @@ func (d *OSSpecifier) String() string {
version += "." + d.MinorVersion
}
} else {
version = d.Codename
version = d.LabelVersion
}

distroDisplayName := d.Name
if version != "" {
distroDisplayName += "@" + version
}
if version == d.MajorVersion && d.Codename != "" {
distroDisplayName += " (" + d.Codename + ")"
if version == d.MajorVersion && d.LabelVersion != "" {
distroDisplayName += " (" + d.LabelVersion + ")"
}

return distroDisplayName
Expand All @@ -154,10 +150,6 @@ func (d OSSpecifier) version() string {
return d.LabelVersion
}

if d.Codename != "" {
return d.Codename
}

return ""
}

Expand Down Expand Up @@ -397,7 +389,7 @@ func (s *affectedPackageStore) handleOSOptions(query *gorm.DB, configs []*OSSpec
}

func (s *affectedPackageStore) resolveDistro(d OSSpecifier) ([]OperatingSystem, error) {
if d.Name == "" && d.Codename == "" {
if d.Name == "" && d.LabelVersion == "" {
return nil, ErrMissingDistroIdentification
}

Expand All @@ -411,15 +403,11 @@ func (s *affectedPackageStore) resolveDistro(d OSSpecifier) ([]OperatingSystem,
query := s.db.Model(&OperatingSystem{})

if d.Name != "" {
query = query.Where("name = ?", d.Name)
}

if d.Codename != "" {
query = query.Where("codename = ?", d.Codename)
query = query.Where("name = ? OR release_id = ?", d.Name, d.Name)
}

if d.LabelVersion != "" {
query = query.Where("label_version = ?", d.LabelVersion)
query = query.Where("codename = ? OR label_version = ?", d.LabelVersion, d.LabelVersion)
}

return s.searchForDistroVersionVariants(query, d)
Expand Down Expand Up @@ -496,7 +484,7 @@ func (s *affectedPackageStore) applyAlias(d *OSSpecifier) error {
var alias *OperatingSystemAlias

for _, a := range aliases {
if a.Codename != "" && a.Codename != d.Codename {
if a.Codename != "" && a.Codename != d.LabelVersion {
continue
}

Expand Down
64 changes: 38 additions & 26 deletions grype/db/v6/affected_package_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,8 @@ func TestAffectedPackageStore_GetAffectedPackages(t *testing.T) {
pkg: pkgFromName(pkg2d1.Package.Name),
options: &GetAffectedPackageOptions{
OSs: []*OSSpecifier{{
Name: "ubuntu",
Codename: "groovy",
Name: "ubuntu",
LabelVersion: "groovy",
}},
},
expected: []AffectedPackageHandle{*pkg2d2},
Expand Down Expand Up @@ -747,17 +747,19 @@ func TestAffectedPackageStore_ResolveDistro(t *testing.T) {
bs := newBlobStore(db)
s := newAffectedPackageStore(db, bs)

ubuntu2004 := &OperatingSystem{Name: "ubuntu", MajorVersion: "20", MinorVersion: "04", Codename: "focal"}
ubuntu2010 := &OperatingSystem{Name: "ubuntu", MajorVersion: "20", MinorVersion: "10", Codename: "groovy"}
rhel8 := &OperatingSystem{Name: "rhel", MajorVersion: "8"}
rhel81 := &OperatingSystem{Name: "rhel", MajorVersion: "8", MinorVersion: "1"}
debian10 := &OperatingSystem{Name: "debian", MajorVersion: "10"}
alpine318 := &OperatingSystem{Name: "alpine", MajorVersion: "3", MinorVersion: "18"}
alpineEdge := &OperatingSystem{Name: "alpine", LabelVersion: "edge"}
debianTrixie := &OperatingSystem{Name: "debian", Codename: "trixie"}
debian7 := &OperatingSystem{Name: "debian", MajorVersion: "7", Codename: "wheezy"}
wolfi := &OperatingSystem{Name: "wolfi", MajorVersion: "20230201"}
arch := &OperatingSystem{Name: "arch", MajorVersion: "20241110", MinorVersion: "0"}
ubuntu2004 := &OperatingSystem{Name: "ubuntu", ReleaseID: "ubuntu", MajorVersion: "20", MinorVersion: "04", LabelVersion: "focal"}
ubuntu2010 := &OperatingSystem{Name: "ubuntu", MajorVersion: "20", MinorVersion: "10", LabelVersion: "groovy"}
rhel8 := &OperatingSystem{Name: "rhel", ReleaseID: "rhel", MajorVersion: "8"}
rhel81 := &OperatingSystem{Name: "rhel", ReleaseID: "rhel", MajorVersion: "8", MinorVersion: "1"}
debian10 := &OperatingSystem{Name: "debian", ReleaseID: "debian", MajorVersion: "10"}
alpine318 := &OperatingSystem{Name: "alpine", ReleaseID: "alpine", MajorVersion: "3", MinorVersion: "18"}
alpineEdge := &OperatingSystem{Name: "alpine", ReleaseID: "alpine", LabelVersion: "edge"}
debianTrixie := &OperatingSystem{Name: "debian", ReleaseID: "debian", LabelVersion: "trixie"}
debian7 := &OperatingSystem{Name: "debian", ReleaseID: "debian", MajorVersion: "7", LabelVersion: "wheezy"}
wolfi := &OperatingSystem{Name: "wolfi", ReleaseID: "wolfi", MajorVersion: "20230201"}
arch := &OperatingSystem{Name: "arch", ReleaseID: "arch", MajorVersion: "20241110", MinorVersion: "0"}
oracle5 := &OperatingSystem{Name: "oracle", ReleaseID: "ol", MajorVersion: "5"}
oracle6 := &OperatingSystem{Name: "oracle", ReleaseID: "ol", MajorVersion: "6"}

operatingSystems := []*OperatingSystem{
ubuntu2004,
Expand All @@ -771,6 +773,8 @@ func TestAffectedPackageStore_ResolveDistro(t *testing.T) {
debian7,
wolfi,
arch,
oracle5,
oracle6,
}
require.NoError(t, db.Create(&operatingSystems).Error)

Expand Down Expand Up @@ -817,8 +821,8 @@ func TestAffectedPackageStore_ResolveDistro(t *testing.T) {
{
name: "codename resolution",
distro: OSSpecifier{
Name: "ubuntu",
Codename: "focal",
Name: "ubuntu",
LabelVersion: "focal",
},
expected: []OperatingSystem{*ubuntu2004},
},
Expand All @@ -828,7 +832,7 @@ func TestAffectedPackageStore_ResolveDistro(t *testing.T) {
Name: "ubuntu",
MajorVersion: "20",
MinorVersion: "04",
Codename: "focal",
LabelVersion: "focal",
},
expected: []OperatingSystem{*ubuntu2004},
},
Expand All @@ -838,7 +842,7 @@ func TestAffectedPackageStore_ResolveDistro(t *testing.T) {
Name: "ubuntu",
MajorVersion: "20",
MinorVersion: "04",
Codename: "fake",
LabelVersion: "fake",
},
},
{
Expand Down Expand Up @@ -871,15 +875,15 @@ func TestAffectedPackageStore_ResolveDistro(t *testing.T) {
distro: OSSpecifier{
Name: "debian",
MajorVersion: "13",
Codename: "trixie", // TODO: what about sid status indication from pretty-name or /etc/debian_version?
LabelVersion: "trixie",
},
expected: []OperatingSystem{*debianTrixie},
},
{
name: "debian by codename",
distro: OSSpecifier{
Name: "debian",
Codename: "wheezy",
Name: "debian",
LabelVersion: "wheezy",
},
expected: []OperatingSystem{*debian7},
},
Expand Down Expand Up @@ -909,6 +913,14 @@ func TestAffectedPackageStore_ResolveDistro(t *testing.T) {
},
expected: []OperatingSystem{*alpine318},
},
{
name: "lookup by release ID (not name)",
distro: OSSpecifier{
Name: "ol",
MajorVersion: "5",
},
expected: []OperatingSystem{*oracle5},
},
{
name: "missing distro name",
distro: OSSpecifier{
Expand Down Expand Up @@ -988,15 +1000,15 @@ func TestDistroSpecifier_String(t *testing.T) {
distro: &OSSpecifier{
Name: "ubuntu",
MajorVersion: "20",
Codename: "focal",
LabelVersion: "focal",
},
expected: "ubuntu@20 (focal)",
},
{
name: "name and codename specified",
distro: &OSSpecifier{
Name: "ubuntu",
Codename: "focal",
Name: "ubuntu",
LabelVersion: "focal",
},
expected: "ubuntu@focal",
},
Expand All @@ -1006,7 +1018,7 @@ func TestDistroSpecifier_String(t *testing.T) {
Name: "ubuntu",
MajorVersion: "20",
MinorVersion: "04",
Codename: "focal",
LabelVersion: "focal",
},
expected: "[email protected]",
},
Expand Down Expand Up @@ -1041,7 +1053,7 @@ func testDistro1AffectedPackage2Handle() *AffectedPackageHandle {
Name: "ubuntu",
MajorVersion: "20",
MinorVersion: "04",
Codename: "focal",
LabelVersion: "focal",
},
BlobValue: &AffectedPackageBlob{
CVEs: []string{"CVE-2023-1234"},
Expand Down Expand Up @@ -1069,7 +1081,7 @@ func testDistro2AffectedPackage2Handle() *AffectedPackageHandle {
Name: "ubuntu",
MajorVersion: "20",
MinorVersion: "10",
Codename: "groovy",
LabelVersion: "groovy",
},
BlobValue: &AffectedPackageBlob{
CVEs: []string{"CVE-2023-4567"},
Expand Down
29 changes: 17 additions & 12 deletions grype/db/v6/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,17 +278,15 @@ func (p *Package) BeforeCreate(tx *gorm.DB) (err error) {
type OperatingSystem struct {
ID ID `gorm:"column:id;primaryKey"`

Name string `gorm:"column:name;index:os_idx,unique"`
MajorVersion string `gorm:"column:major_version;index:os_idx,unique"`
MinorVersion string `gorm:"column:minor_version;index:os_idx,unique"`
LabelVersion string `gorm:"column:label_version;index:os_idx,unique"`
Codename string `gorm:"column:codename"` // TODO: should this be removed and use label-version instead?
Name string `gorm:"column:name;index:os_idx,unique;index"`
ReleaseID string `gorm:"column:release_id;index:os_idx,unique;index"`
MajorVersion string `gorm:"column:major_version;index:os_idx,unique;index"`
MinorVersion string `gorm:"column:minor_version;index:os_idx,unique;index"`
LabelVersion string `gorm:"column:label_version;index:os_idx,unique;index"`
Codename string `gorm:"column:codename;index"`
}

func (os *OperatingSystem) BeforeCreate(tx *gorm.DB) (err error) {
if (os.MajorVersion != "" || os.MinorVersion != "") && os.LabelVersion != "" {
return fmt.Errorf("cannot have both label_version and major_version/minor_version set")
}
// if the name, major version, and minor version already exist in the table then we should not insert a new record
var existing OperatingSystem
result := tx.Where("name = ? AND major_version = ? AND minor_version = ?", os.Name, os.MajorVersion, os.MinorVersion).First(&existing)
Expand All @@ -303,10 +301,17 @@ type OperatingSystemAlias struct {
// Name is the matching name as found in the ID field if the /etc/os-release file
Name string `gorm:"column:name;primaryKey;index:os_alias_idx"`

// Version is the matching version as found in the ID field if the /etc/os-release file
Version string `gorm:"column:version;primaryKey"`
VersionPattern string `gorm:"column:version_pattern;primaryKey"`
Codename string `gorm:"column:codename"`
// Version is the matching version as found in the VERSION_ID field if the /etc/os-release file
Version string `gorm:"column:version;primaryKey"`

// VersionPattern is a regex pattern to match against the VERSION_ID field if the /etc/os-release file
VersionPattern string `gorm:"column:version_pattern;primaryKey"`

// Codename is the matching codename as found in the VERSION_CODENAME field if the /etc/os-release file
Codename string `gorm:"column:codename"`

// below are the fields that should be used as replacement for fields in the OperatingSystem table

ReplacementName *string `gorm:"column:replacement;primaryKey"`
ReplacementMajorVersion *string `gorm:"column:replacement_major_version;primaryKey"`
ReplacementMinorVersion *string `gorm:"column:replacement_minor_version;primaryKey"`
Expand Down
67 changes: 0 additions & 67 deletions grype/db/v6/models_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,73 +7,6 @@ import (
"github.com/stretchr/testify/require"
)

func TestOperatingSystem_LabelVersionMutualExclusivity(t *testing.T) {
msg := "cannot have both label_version and major_version/minor_version set"
db := setupTestStore(t).db

tests := []struct {
name string
input *OperatingSystem
errMsg string
}{
{
name: "label version and major version are mutually exclusive",
input: &OperatingSystem{
Name: "ubuntu",
MajorVersion: "20",
LabelVersion: "something",
},
errMsg: msg,
},
{
name: "label version and major.minor version are mutually exclusive",
input: &OperatingSystem{
Name: "ubuntu",
MajorVersion: "20",
MinorVersion: "04",
LabelVersion: "something",
},
errMsg: msg,
},
{
name: "label version and minor version are mutually exclusive",
input: &OperatingSystem{
Name: "ubuntu",
MinorVersion: "04",
LabelVersion: "something",
},
errMsg: msg,
},
{
name: "label version set",
input: &OperatingSystem{
Name: "ubuntu",
LabelVersion: "something",
},
},
{
name: "major/minor version set",
input: &OperatingSystem{
Name: "ubuntu",
MajorVersion: "20",
MinorVersion: "04",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := db.Create(tt.input).Error
if tt.errMsg == "" {
assert.NoError(t, err)
return
}
require.Error(t, err)
assert.Contains(t, err.Error(), tt.errMsg)
})
}
}

func TestOperatingSystemAlias_VersionMutualExclusivity(t *testing.T) {
db := setupTestStore(t).db

Expand Down

0 comments on commit 3baa3d2

Please sign in to comment.