mirror of
https://github.com/anchore/grype
synced 2024-09-20 06:21:56 +00:00
use package identifiers not a package obj
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
27b4268022
commit
0f7c19811e
2 changed files with 156 additions and 59 deletions
|
@ -19,12 +19,12 @@ func All() []any {
|
|||
&DbSpecificNvd{},
|
||||
&Epss{},
|
||||
&KnownExploitedVulnerability{},
|
||||
&LogicalPackage{},
|
||||
//&LogicalPackage{},
|
||||
&AffectedExcludeVersion{},
|
||||
&OperatingSystem{},
|
||||
&PackageQualifierPlatformCpe{},
|
||||
&PackageQualifierRpmModularity{},
|
||||
&Package{},
|
||||
//&Package{},
|
||||
&Provider{},
|
||||
&RangeEvent{},
|
||||
//&RangeEventMetadata{},
|
||||
|
@ -177,16 +177,33 @@ type Affected struct {
|
|||
ID int64 `gorm:"column:id;primaryKey"`
|
||||
VulnerabilityID int64 `gorm:"column:vulnerability_id,not null"`
|
||||
|
||||
PackageID *int64 `gorm:"column:package_id"`
|
||||
Package *Package
|
||||
//PackageID *int64 `gorm:"column:package_id"`
|
||||
//Package *Package
|
||||
|
||||
OperatingSystemID *int64 `gorm:"column:operating_system_id"`
|
||||
OperatingSystem *OperatingSystem `gorm:"foreignKey:OperatingSystemID"`
|
||||
|
||||
Versions *[]AffectedVersion `gorm:"foreignKey:AffectedID"`
|
||||
ExcludeVersions *[]AffectedExcludeVersion `gorm:"foreignKey:AffectedID"`
|
||||
Severities *[]AffectedSeverity `gorm:"foreignKey:AffectedID"`
|
||||
Range *[]Range `gorm:"foreignKey:AffectedID"`
|
||||
|
||||
Packages *[]Package `gorm:"many2many:affected_packages"`
|
||||
|
||||
// Digests that are known to correspond to this vulnerability, but cannot be closely associated with a package
|
||||
Digests *[]Digest `gorm:"many2many:affected_digests"`
|
||||
|
||||
//Purls *[]Purl `gorm:"many2many:package_purls"`
|
||||
Cpes *[]Cpe `gorm:"many2many:affected_cpes"`
|
||||
|
||||
//Purls *[]Purl `gorm:"many2many:affected_packages"`
|
||||
|
||||
// Digests that are known to correspond to this package, either contained within, packaged for distribution, or normalized to a single file
|
||||
//Digests *[]Digest `gorm:"many2many:package_digests"`
|
||||
|
||||
// package qualifier info
|
||||
PackageQualifierPlatformCpes *[]PackageQualifierPlatformCpe `gorm:"foreignKey:PackageID"`
|
||||
PackageQualifierRpmModularities *[]PackageQualifierRpmModularity `gorm:"foreignKey:PackageID"` // TODO: shouldn't this be 1:1 (only a single module for a single package)
|
||||
}
|
||||
|
||||
// TODO: add later and reuse existing similar tables with many2many
|
||||
|
@ -320,44 +337,49 @@ type Package struct {
|
|||
ID int64 `gorm:"column:id;primaryKey"`
|
||||
|
||||
// TODO: break purl out into fields here
|
||||
Ecosystem string `gorm:"column:ecosystem"` // TODO: NVD doesn't have this, should this be nullable?
|
||||
PackageName string `gorm:"column:package_name;index:package_name"`
|
||||
Ecosystem *string `gorm:"column:ecosystem;index:idx_package,unique"` // TODO: NVD doesn't have this, should this be nullable?
|
||||
PackageName string `gorm:"column:package_name;index:idx_package,unique"`
|
||||
|
||||
OperatingSystemID *int64 `gorm:"column:operating_system_id"`
|
||||
OperatingSystem *OperatingSystem `gorm:"foreignKey:OperatingSystemID"`
|
||||
//OperatingSystemID *int64 `gorm:"column:operating_system_id"`
|
||||
//OperatingSystem *OperatingSystem `gorm:"foreignKey:OperatingSystemID"`
|
||||
//
|
||||
//Purls *[]Purl `gorm:"many2many:package_purls"`
|
||||
//Cpes *[]Cpe `gorm:"many2many:package_cpes"`
|
||||
|
||||
Purls *[]Purl `gorm:"many2many:package_purls"`
|
||||
Cpes *[]Cpe `gorm:"many2many:package_cpes"`
|
||||
|
||||
// Digests that are known to correspond to this package, either contained within, packaged for distribution, or normalized to a single file
|
||||
Digests *[]Digest `gorm:"many2many:package_digests"`
|
||||
|
||||
// package qualifier info
|
||||
PackageQualifierPlatformCpes *[]PackageQualifierPlatformCpe `gorm:"foreignKey:PackageID"`
|
||||
PackageQualifierRpmModularities *[]PackageQualifierRpmModularity `gorm:"foreignKey:PackageID"` // TODO: shouldn't this be 1:1 (only a single module for a single package)
|
||||
}
|
||||
|
||||
type Purl struct {
|
||||
ID int64 `gorm:"column:id;primaryKey"`
|
||||
|
||||
Scheme string `gorm:"column:scheme"`
|
||||
Type string `gorm:"column:type"`
|
||||
Namespace *string `gorm:"column:namespace"`
|
||||
Name string `gorm:"column:name"`
|
||||
Version string `gorm:"column:version"`
|
||||
SubPath *string `gorm:"column:subpath"`
|
||||
|
||||
Qualifiers *[]Qualifier `gorm:"many2many:purl_qualifiers"`
|
||||
func (c *Package) BeforeCreate(tx *gorm.DB) (err error) {
|
||||
// if the name, major version, and minor version already exist in the table then we should not insert a new record
|
||||
var existing Package
|
||||
result := tx.Where("package_name = ? AND ecosystem = ?", c.PackageName, c.Ecosystem).First(&existing)
|
||||
if result.Error == nil {
|
||||
// if the record already exists, then we should use the existing record
|
||||
*c = existing
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//type Purl struct {
|
||||
// ID int64 `gorm:"column:id;primaryKey"`
|
||||
//
|
||||
// //Schema *string `gorm:"column:schema"`
|
||||
// Type string `gorm:"column:type"`
|
||||
// //Namespace *string `gorm:"column:namespace"`
|
||||
// Name string `gorm:"column:name"`
|
||||
// //Version *string `gorm:"column:version"`
|
||||
// //SubPath *string `gorm:"column:subpath"`
|
||||
// //
|
||||
// //Qualifiers *[]Qualifier `gorm:"many2many:purl_qualifiers"`
|
||||
//}
|
||||
|
||||
// secondary package identifier information
|
||||
|
||||
type Qualifier struct {
|
||||
ID int64 `gorm:"column:id;primaryKey"`
|
||||
|
||||
Key string `gorm:"column:key"`
|
||||
Value string `gorm:"column:value"`
|
||||
}
|
||||
//
|
||||
//type Qualifier struct {
|
||||
// ID int64 `gorm:"column:id;primaryKey"`
|
||||
//
|
||||
// Key string `gorm:"column:key"`
|
||||
// Value string `gorm:"column:value"`
|
||||
//}
|
||||
|
||||
type OperatingSystem struct {
|
||||
ID int64 `gorm:"column:id;primaryKey"`
|
||||
|
@ -395,11 +417,11 @@ type PackageQualifierRpmModularity struct {
|
|||
|
||||
// logical package info
|
||||
|
||||
type LogicalPackage struct {
|
||||
ID int64 `gorm:"column:id;primaryKey"`
|
||||
|
||||
Packages []Package `gorm:"many2many:logical_package_packages"`
|
||||
}
|
||||
//type LogicalPackage struct {
|
||||
// ID int64 `gorm:"column:id;primaryKey"`
|
||||
//
|
||||
// Packages []Package `gorm:"many2many:logical_package_packages"`
|
||||
//}
|
||||
|
||||
// aux
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ func (s *vulnerabilityStore) AddVulnerabilities(vulnerabilities ...*Vulnerabilit
|
|||
s.handleOSs,
|
||||
s.handleRangeEvents,
|
||||
s.handleCPEs,
|
||||
s.handlePackages,
|
||||
} {
|
||||
if err := h(vulnerabilities); err != nil {
|
||||
return err
|
||||
|
@ -55,20 +56,94 @@ func (s *vulnerabilityStore) AddVulnerabilities(vulnerabilities ...*Vulnerabilit
|
|||
return s.db.CreateInBatches(vulnerabilities, s.BatchSize).Error
|
||||
}
|
||||
|
||||
func (s *vulnerabilityStore) handlePackages(vulns []*Vulnerability) error {
|
||||
// ensure unique packages
|
||||
unique, err := ensureUniquePackages(s.db, vulns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update vulnerabilities with package IDs
|
||||
updateAffectedsWithPackages(vulns, unique)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureUniquePackages(db *gorm.DB, vulns []*Vulnerability) ([]*Package, error) {
|
||||
// map to track unique packages
|
||||
pkgMap := make(map[string]*Package)
|
||||
for _, v := range vulns {
|
||||
for _, a := range *v.Affected {
|
||||
if a.Packages == nil {
|
||||
continue
|
||||
}
|
||||
pkgs := *a.Packages
|
||||
for i, p := range pkgs {
|
||||
pkgKey := fmt.Sprintf("%s:%s", strVal(p.Ecosystem), p.PackageName)
|
||||
val := &pkgs[i]
|
||||
pkgMap[pkgKey] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extract unique packages
|
||||
var uniquePackages []*Package
|
||||
for _, pkg := range pkgMap {
|
||||
uniquePackages = append(uniquePackages, pkg)
|
||||
}
|
||||
|
||||
// insert unique packages into the database or fetch existing ones
|
||||
for i, pkg := range uniquePackages {
|
||||
var existing Package
|
||||
err := db.Where("package_name = ? AND ecosystem = ?", pkg.PackageName, pkg.Ecosystem).
|
||||
FirstOrCreate(&existing, pkg).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uniquePackages[i].ID = existing.ID
|
||||
}
|
||||
|
||||
return uniquePackages, nil
|
||||
|
||||
}
|
||||
|
||||
func updateAffectedsWithPackages(vulns []*Vulnerability, uniquePackages []*Package) {
|
||||
pkgMap := make(map[string]int64)
|
||||
for _, p := range uniquePackages {
|
||||
pkgKey := fmt.Sprintf("%s:%s", strVal(p.Ecosystem), p.PackageName)
|
||||
pkgMap[pkgKey] = p.ID
|
||||
}
|
||||
|
||||
for i, v := range vulns {
|
||||
for j, a := range *v.Affected {
|
||||
if a.Packages == nil {
|
||||
continue
|
||||
}
|
||||
pkgs := *a.Packages
|
||||
for k, p := range pkgs {
|
||||
pkgKey := fmt.Sprintf("%s:%s", strVal(p.Ecosystem), p.PackageName)
|
||||
val := pkgMap[pkgKey]
|
||||
(*(*vulns[i].Affected)[j].Packages)[k].ID = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *vulnerabilityStore) handleCPEs(vulns []*Vulnerability) error {
|
||||
// ensure unique operating systems
|
||||
// ensure unique cpes
|
||||
unique, err := ensureUniqueCPEs(s.db, vulns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update vulnerabilities with operating system IDs
|
||||
updateVulnerabilitiesWithCPEs(vulns, unique)
|
||||
// update vulnerabilities with cpes IDs
|
||||
updateAffectedsWithCPEs(vulns, unique)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateVulnerabilitiesWithCPEs(vulns []*Vulnerability, uniqueCPEs []*Cpe) {
|
||||
func updateAffectedsWithCPEs(vulns []*Vulnerability, uniqueCPEs []*Cpe) {
|
||||
cpeMap := make(map[string]int64)
|
||||
for _, c := range uniqueCPEs {
|
||||
cpeKey := fmt.Sprintf("%s:%s:%s:%s:%s:%s:%s", c.Schema, c.Type, strVal(c.Vendor), c.Product, strVal(c.Version), strVal(c.Update), strVal(c.TargetSoftware))
|
||||
|
@ -80,13 +155,13 @@ func updateVulnerabilitiesWithCPEs(vulns []*Vulnerability, uniqueCPEs []*Cpe) {
|
|||
continue
|
||||
}
|
||||
for j, a := range *v.Affected {
|
||||
if a.Package.Cpes == nil {
|
||||
if a.Cpes == nil {
|
||||
continue
|
||||
}
|
||||
for k, c := range *a.Package.Cpes {
|
||||
for k, c := range *a.Cpes {
|
||||
cpeKey := fmt.Sprintf("%s:%s:%s:%s:%s:%s:%s", c.Schema, c.Type, strVal(c.Vendor), c.Product, strVal(c.Version), strVal(c.Update), strVal(c.TargetSoftware))
|
||||
val := cpeMap[cpeKey]
|
||||
(*(*vulns[i].Affected)[j].Package.Cpes)[k].ID = val
|
||||
(*(*vulns[i].Affected)[j].Cpes)[k].ID = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,10 +174,10 @@ func ensureUniqueCPEs(db *gorm.DB, vulns []*Vulnerability) ([]*Cpe, error) {
|
|||
continue
|
||||
}
|
||||
for _, a := range *v.Affected {
|
||||
if a.Package.Cpes == nil {
|
||||
if a.Cpes == nil {
|
||||
continue
|
||||
}
|
||||
for _, c := range *a.Package.Cpes {
|
||||
for _, c := range *a.Cpes {
|
||||
cpeKey := fmt.Sprintf("%s:%s:%s:%s:%s:%s:%s", c.Schema, c.Type, strVal(c.Vendor), c.Product, strVal(c.Version), strVal(c.Update), strVal(c.TargetSoftware))
|
||||
cpeMap[cpeKey] = c
|
||||
}
|
||||
|
@ -138,7 +213,7 @@ func (s *vulnerabilityStore) handleRangeEvents(vulns []*Vulnerability) error {
|
|||
}
|
||||
|
||||
// update vulnerabilities with operating system IDs
|
||||
updateVulnerabilitiesWithRanges(vulns, unique)
|
||||
updateAffectedsWithRanges(vulns, unique)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -184,7 +259,7 @@ func ensureUniqueRangeEvents(db *gorm.DB, vulns []*Vulnerability) ([]*RangeEvent
|
|||
return uniqueRangeEvents, nil
|
||||
}
|
||||
|
||||
func updateVulnerabilitiesWithRanges(vulns []*Vulnerability, uniqueRangeEvents []*RangeEvent) {
|
||||
func updateAffectedsWithRanges(vulns []*Vulnerability, uniqueRangeEvents []*RangeEvent) {
|
||||
reMap := make(map[string]int64)
|
||||
for _, re := range uniqueRangeEvents {
|
||||
reKey := fmt.Sprintf("%s:%s:%s:%s:%s", strVal(re.Introduced), strVal(re.Fixed), strVal(re.LastAffected), strVal(re.Limit), re.State)
|
||||
|
@ -225,7 +300,7 @@ func (s *vulnerabilityStore) handleOSs(vulns []*Vulnerability) error {
|
|||
}
|
||||
|
||||
// update vulnerabilities with operating system IDs
|
||||
updateVulnerabilitiesWithOperatingSystems(vulns, uniqueOSList)
|
||||
updateAffectedsWithOperatingSystems(vulns, uniqueOSList)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -235,11 +310,11 @@ func ensureUniqueOperatingSystems(db *gorm.DB, vulns []*Vulnerability) ([]*Opera
|
|||
osMap := make(map[string]*OperatingSystem)
|
||||
for _, v := range vulns {
|
||||
for _, a := range *v.Affected {
|
||||
if a.Package.OperatingSystem == nil {
|
||||
if a.OperatingSystem == nil {
|
||||
continue
|
||||
}
|
||||
osKey := fmt.Sprintf("%s:%s:%s", a.Package.OperatingSystem.Name, a.Package.OperatingSystem.MajorVersion, a.Package.OperatingSystem.MinorVersion)
|
||||
osMap[osKey] = a.Package.OperatingSystem
|
||||
osKey := fmt.Sprintf("%s:%s:%s", a.OperatingSystem.Name, a.OperatingSystem.MajorVersion, a.OperatingSystem.MinorVersion)
|
||||
osMap[osKey] = a.OperatingSystem
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,7 +338,7 @@ func ensureUniqueOperatingSystems(db *gorm.DB, vulns []*Vulnerability) ([]*Opera
|
|||
return uniqueOSList, nil
|
||||
}
|
||||
|
||||
func updateVulnerabilitiesWithOperatingSystems(vulns []*Vulnerability, uniqueOSList []*OperatingSystem) {
|
||||
func updateAffectedsWithOperatingSystems(vulns []*Vulnerability, uniqueOSList []*OperatingSystem) {
|
||||
osMap := make(map[string]int64)
|
||||
for _, os := range uniqueOSList {
|
||||
osKey := fmt.Sprintf("%s:%s:%s", os.Name, os.MajorVersion, os.MinorVersion)
|
||||
|
@ -272,12 +347,12 @@ func updateVulnerabilitiesWithOperatingSystems(vulns []*Vulnerability, uniqueOSL
|
|||
|
||||
for i, v := range vulns {
|
||||
for j, a := range *v.Affected {
|
||||
if a.Package == nil || a.Package.OperatingSystem == nil {
|
||||
if a.OperatingSystem == nil {
|
||||
continue
|
||||
}
|
||||
osKey := fmt.Sprintf("%s:%s:%s", a.Package.OperatingSystem.Name, a.Package.OperatingSystem.MajorVersion, a.Package.OperatingSystem.MinorVersion)
|
||||
osKey := fmt.Sprintf("%s:%s:%s", a.OperatingSystem.Name, a.OperatingSystem.MajorVersion, a.OperatingSystem.MinorVersion)
|
||||
val := osMap[osKey]
|
||||
(*vulns[i].Affected)[j].Package.OperatingSystemID = &val
|
||||
(*vulns[i].Affected)[j].OperatingSystemID = &val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue