Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finalize label version and add release id to OS model #2349

Merged
merged 1 commit into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
wagoodman marked this conversation as resolved.
Show resolved Hide resolved
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)
wagoodman marked this conversation as resolved.
Show resolved Hide resolved
}

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
Loading