mirror of
https://github.com/anchore/grype
synced 2024-11-10 06:34:13 +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{},
|
&DbSpecificNvd{},
|
||||||
&Epss{},
|
&Epss{},
|
||||||
&KnownExploitedVulnerability{},
|
&KnownExploitedVulnerability{},
|
||||||
&LogicalPackage{},
|
//&LogicalPackage{},
|
||||||
&AffectedExcludeVersion{},
|
&AffectedExcludeVersion{},
|
||||||
&OperatingSystem{},
|
&OperatingSystem{},
|
||||||
&PackageQualifierPlatformCpe{},
|
&PackageQualifierPlatformCpe{},
|
||||||
&PackageQualifierRpmModularity{},
|
&PackageQualifierRpmModularity{},
|
||||||
&Package{},
|
//&Package{},
|
||||||
&Provider{},
|
&Provider{},
|
||||||
&RangeEvent{},
|
&RangeEvent{},
|
||||||
//&RangeEventMetadata{},
|
//&RangeEventMetadata{},
|
||||||
|
@ -177,16 +177,33 @@ type Affected struct {
|
||||||
ID int64 `gorm:"column:id;primaryKey"`
|
ID int64 `gorm:"column:id;primaryKey"`
|
||||||
VulnerabilityID int64 `gorm:"column:vulnerability_id,not null"`
|
VulnerabilityID int64 `gorm:"column:vulnerability_id,not null"`
|
||||||
|
|
||||||
PackageID *int64 `gorm:"column:package_id"`
|
//PackageID *int64 `gorm:"column:package_id"`
|
||||||
Package *Package
|
//Package *Package
|
||||||
|
|
||||||
|
OperatingSystemID *int64 `gorm:"column:operating_system_id"`
|
||||||
|
OperatingSystem *OperatingSystem `gorm:"foreignKey:OperatingSystemID"`
|
||||||
|
|
||||||
Versions *[]AffectedVersion `gorm:"foreignKey:AffectedID"`
|
Versions *[]AffectedVersion `gorm:"foreignKey:AffectedID"`
|
||||||
ExcludeVersions *[]AffectedExcludeVersion `gorm:"foreignKey:AffectedID"`
|
ExcludeVersions *[]AffectedExcludeVersion `gorm:"foreignKey:AffectedID"`
|
||||||
Severities *[]AffectedSeverity `gorm:"foreignKey:AffectedID"`
|
Severities *[]AffectedSeverity `gorm:"foreignKey:AffectedID"`
|
||||||
Range *[]Range `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 that are known to correspond to this vulnerability, but cannot be closely associated with a package
|
||||||
Digests *[]Digest `gorm:"many2many:affected_digests"`
|
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
|
// TODO: add later and reuse existing similar tables with many2many
|
||||||
|
@ -320,44 +337,49 @@ type Package struct {
|
||||||
ID int64 `gorm:"column:id;primaryKey"`
|
ID int64 `gorm:"column:id;primaryKey"`
|
||||||
|
|
||||||
// TODO: break purl out into fields here
|
// TODO: break purl out into fields here
|
||||||
Ecosystem string `gorm:"column:ecosystem"` // TODO: NVD doesn't have this, should this be nullable?
|
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:package_name"`
|
PackageName string `gorm:"column:package_name;index:idx_package,unique"`
|
||||||
|
|
||||||
OperatingSystemID *int64 `gorm:"column:operating_system_id"`
|
//OperatingSystemID *int64 `gorm:"column:operating_system_id"`
|
||||||
OperatingSystem *OperatingSystem `gorm:"foreignKey:OperatingSystemID"`
|
//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 {
|
func (c *Package) BeforeCreate(tx *gorm.DB) (err error) {
|
||||||
ID int64 `gorm:"column:id;primaryKey"`
|
// if the name, major version, and minor version already exist in the table then we should not insert a new record
|
||||||
|
var existing Package
|
||||||
Scheme string `gorm:"column:scheme"`
|
result := tx.Where("package_name = ? AND ecosystem = ?", c.PackageName, c.Ecosystem).First(&existing)
|
||||||
Type string `gorm:"column:type"`
|
if result.Error == nil {
|
||||||
Namespace *string `gorm:"column:namespace"`
|
// if the record already exists, then we should use the existing record
|
||||||
Name string `gorm:"column:name"`
|
*c = existing
|
||||||
Version string `gorm:"column:version"`
|
}
|
||||||
SubPath *string `gorm:"column:subpath"`
|
return nil
|
||||||
|
|
||||||
Qualifiers *[]Qualifier `gorm:"many2many:purl_qualifiers"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//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
|
// secondary package identifier information
|
||||||
|
//
|
||||||
type Qualifier struct {
|
//type Qualifier struct {
|
||||||
ID int64 `gorm:"column:id;primaryKey"`
|
// ID int64 `gorm:"column:id;primaryKey"`
|
||||||
|
//
|
||||||
Key string `gorm:"column:key"`
|
// Key string `gorm:"column:key"`
|
||||||
Value string `gorm:"column:value"`
|
// Value string `gorm:"column:value"`
|
||||||
}
|
//}
|
||||||
|
|
||||||
type OperatingSystem struct {
|
type OperatingSystem struct {
|
||||||
ID int64 `gorm:"column:id;primaryKey"`
|
ID int64 `gorm:"column:id;primaryKey"`
|
||||||
|
@ -395,11 +417,11 @@ type PackageQualifierRpmModularity struct {
|
||||||
|
|
||||||
// logical package info
|
// logical package info
|
||||||
|
|
||||||
type LogicalPackage struct {
|
//type LogicalPackage struct {
|
||||||
ID int64 `gorm:"column:id;primaryKey"`
|
// ID int64 `gorm:"column:id;primaryKey"`
|
||||||
|
//
|
||||||
Packages []Package `gorm:"many2many:logical_package_packages"`
|
// Packages []Package `gorm:"many2many:logical_package_packages"`
|
||||||
}
|
//}
|
||||||
|
|
||||||
// aux
|
// aux
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ func (s *vulnerabilityStore) AddVulnerabilities(vulnerabilities ...*Vulnerabilit
|
||||||
s.handleOSs,
|
s.handleOSs,
|
||||||
s.handleRangeEvents,
|
s.handleRangeEvents,
|
||||||
s.handleCPEs,
|
s.handleCPEs,
|
||||||
|
s.handlePackages,
|
||||||
} {
|
} {
|
||||||
if err := h(vulnerabilities); err != nil {
|
if err := h(vulnerabilities); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -55,20 +56,94 @@ func (s *vulnerabilityStore) AddVulnerabilities(vulnerabilities ...*Vulnerabilit
|
||||||
return s.db.CreateInBatches(vulnerabilities, s.BatchSize).Error
|
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 {
|
func (s *vulnerabilityStore) handleCPEs(vulns []*Vulnerability) error {
|
||||||
// ensure unique operating systems
|
// ensure unique cpes
|
||||||
unique, err := ensureUniqueCPEs(s.db, vulns)
|
unique, err := ensureUniqueCPEs(s.db, vulns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// update vulnerabilities with operating system IDs
|
// update vulnerabilities with cpes IDs
|
||||||
updateVulnerabilitiesWithCPEs(vulns, unique)
|
updateAffectedsWithCPEs(vulns, unique)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateVulnerabilitiesWithCPEs(vulns []*Vulnerability, uniqueCPEs []*Cpe) {
|
func updateAffectedsWithCPEs(vulns []*Vulnerability, uniqueCPEs []*Cpe) {
|
||||||
cpeMap := make(map[string]int64)
|
cpeMap := make(map[string]int64)
|
||||||
for _, c := range uniqueCPEs {
|
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))
|
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
|
continue
|
||||||
}
|
}
|
||||||
for j, a := range *v.Affected {
|
for j, a := range *v.Affected {
|
||||||
if a.Package.Cpes == nil {
|
if a.Cpes == nil {
|
||||||
continue
|
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))
|
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]
|
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
|
continue
|
||||||
}
|
}
|
||||||
for _, a := range *v.Affected {
|
for _, a := range *v.Affected {
|
||||||
if a.Package.Cpes == nil {
|
if a.Cpes == nil {
|
||||||
continue
|
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))
|
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
|
cpeMap[cpeKey] = c
|
||||||
}
|
}
|
||||||
|
@ -138,7 +213,7 @@ func (s *vulnerabilityStore) handleRangeEvents(vulns []*Vulnerability) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update vulnerabilities with operating system IDs
|
// update vulnerabilities with operating system IDs
|
||||||
updateVulnerabilitiesWithRanges(vulns, unique)
|
updateAffectedsWithRanges(vulns, unique)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -184,7 +259,7 @@ func ensureUniqueRangeEvents(db *gorm.DB, vulns []*Vulnerability) ([]*RangeEvent
|
||||||
return uniqueRangeEvents, nil
|
return uniqueRangeEvents, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateVulnerabilitiesWithRanges(vulns []*Vulnerability, uniqueRangeEvents []*RangeEvent) {
|
func updateAffectedsWithRanges(vulns []*Vulnerability, uniqueRangeEvents []*RangeEvent) {
|
||||||
reMap := make(map[string]int64)
|
reMap := make(map[string]int64)
|
||||||
for _, re := range uniqueRangeEvents {
|
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)
|
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
|
// update vulnerabilities with operating system IDs
|
||||||
updateVulnerabilitiesWithOperatingSystems(vulns, uniqueOSList)
|
updateAffectedsWithOperatingSystems(vulns, uniqueOSList)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -235,11 +310,11 @@ func ensureUniqueOperatingSystems(db *gorm.DB, vulns []*Vulnerability) ([]*Opera
|
||||||
osMap := make(map[string]*OperatingSystem)
|
osMap := make(map[string]*OperatingSystem)
|
||||||
for _, v := range vulns {
|
for _, v := range vulns {
|
||||||
for _, a := range *v.Affected {
|
for _, a := range *v.Affected {
|
||||||
if a.Package.OperatingSystem == nil {
|
if a.OperatingSystem == nil {
|
||||||
continue
|
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)
|
||||||
osMap[osKey] = a.Package.OperatingSystem
|
osMap[osKey] = a.OperatingSystem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +338,7 @@ func ensureUniqueOperatingSystems(db *gorm.DB, vulns []*Vulnerability) ([]*Opera
|
||||||
return uniqueOSList, nil
|
return uniqueOSList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateVulnerabilitiesWithOperatingSystems(vulns []*Vulnerability, uniqueOSList []*OperatingSystem) {
|
func updateAffectedsWithOperatingSystems(vulns []*Vulnerability, uniqueOSList []*OperatingSystem) {
|
||||||
osMap := make(map[string]int64)
|
osMap := make(map[string]int64)
|
||||||
for _, os := range uniqueOSList {
|
for _, os := range uniqueOSList {
|
||||||
osKey := fmt.Sprintf("%s:%s:%s", os.Name, os.MajorVersion, os.MinorVersion)
|
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 i, v := range vulns {
|
||||||
for j, a := range *v.Affected {
|
for j, a := range *v.Affected {
|
||||||
if a.Package == nil || a.Package.OperatingSystem == nil {
|
if a.OperatingSystem == nil {
|
||||||
continue
|
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]
|
val := osMap[osKey]
|
||||||
(*vulns[i].Affected)[j].Package.OperatingSystemID = &val
|
(*vulns[i].Affected)[j].OperatingSystemID = &val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue