remove desc

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman 2024-06-28 16:07:26 -04:00
parent 7c9090a6be
commit a8947447b3
4 changed files with 369 additions and 180 deletions

View file

@ -3,10 +3,46 @@ package v6
import ( import (
"fmt" "fmt"
"github.com/OneOfOne/xxhash" "github.com/OneOfOne/xxhash"
"github.com/anchore/grype/internal/log"
) )
func BlobDigest(content string) string { func BlobDigest(content string) string {
h := xxhash.New64() h := xxhash.New64()
h.Write([]byte(content)) // TODO: handle error? h.Write([]byte(content)) // TODO: handle error?
return fmt.Sprintf("xx64:%x", h.Sum(nil)) return fmt.Sprintf("%x", h.Sum(nil)) // by the size we can surmise that this is a xxh64 hash
}
type BlobStore interface {
AddBlobs(blobs ...*Blob) error
GetBlob(digest string) (*Blob, error)
}
type blobStore struct {
*StoreConfig
*state
}
func NewBlobStore(cfg *StoreConfig) BlobStore {
return &blobStore{
StoreConfig: cfg,
state: cfg.state(),
}
}
func (s *blobStore) GetBlob(digest string) (*Blob, error) {
log.WithFields("digest", digest).Trace("fetching Blob record")
var model Blob
result := s.db.Where("digest = ?", digest).Find(&model)
return &model, result.Error
}
func (s *blobStore) AddBlobs(blobs ...*Blob) error {
for _, b := range blobs {
if err := s.db.FirstOrCreate(b).Error; err != nil {
return err
}
}
return nil
} }

View file

@ -2,36 +2,47 @@ package v6
import ( import (
"fmt" "fmt"
"gorm.io/datatypes"
"gorm.io/gorm" "gorm.io/gorm"
"time" "time"
) )
func All() []any { func All() []any {
return []any{ return []any{
&CpeWithoutVersion{}, &Cpe{},
&PlatformCpe{},
&Digest{}, &Digest{},
&Affected{},
&Alias{}, &Alias{},
&Blob{}, &Blob{},
&DbMetadata{}, &DbMetadata{},
&DbSpecificNvd{}, //&DbSpecificNvd{},
&Epss{}, &Epss{},
&KnownExploitedVulnerability{}, &KnownExploitedVulnerability{},
&OperatingSystem{}, &OperatingSystem{},
&Provider{}, &Provider{},
&Reference{}, &Reference{},
&Severity{}, &Severity{},
&Affected{},
&Vulnerability{}, &Vulnerability{},
} }
} }
// core vulnerability types // core vulnerability types
type Advisory struct { // TODO: this will probably be shipped as another DB and attached on as-needed basis
ID string type Blob struct {
Digest string `gorm:"column:digest;primaryKey"`
Value string `gorm:"column:value;not null"`
}
VulnerabilityIDs []string func (b *Blob) 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 Blob
result := tx.Where("digest = ?", b.Digest).First(&existing)
if result.Error == nil {
// if the record already exists, then we should use the existing record
*b = existing
}
return nil
} }
// Vulnerability represents the core advisory record for a single known vulnerability from a specific provider. There // Vulnerability represents the core advisory record for a single known vulnerability from a specific provider. There
@ -62,8 +73,10 @@ type Vulnerability struct {
Status string `gorm:"column:status"` // example: "active, withdrawn, rejected, ..." could be an enum Status string `gorm:"column:status"` // example: "active, withdrawn, rejected, ..." could be an enum
Summary string `gorm:"column:summary"` //Summary string `gorm:"column:summary"`
Detail string `gorm:"column:detail;index,unique"` //Detail string `gorm:"column:detail;index,unique"`
DetailDigest string `gorm:"column:detail_digest;index,unique"`
//Detail *Blob `gorm:"foreignKey:DetailDigest"`
// References are URLs to external resources that provide more information about the vulnerability (mirrors the OSV field) // References are URLs to external resources that provide more information about the vulnerability (mirrors the OSV field)
References *[]Reference `gorm:"foreignKey:VulnerabilityID"` References *[]Reference `gorm:"foreignKey:VulnerabilityID"`
@ -75,10 +88,10 @@ type Vulnerability struct {
Aliases *[]Alias `gorm:"many2many:vulnerability_aliases"` Aliases *[]Alias `gorm:"many2many:vulnerability_aliases"`
// Severities is a list of severity indications (quantitative or qualitative) for the vulnerability (mirrors the OSV field, but the semantics are different. We allow for qualitative string severity, where OSV does not) // Severities is a list of severity indications (quantitative or qualitative) for the vulnerability (mirrors the OSV field, but the semantics are different. We allow for qualitative string severity, where OSV does not)
Severities *datatypes.JSONSlice[Severity] `gorm:"column:severities"` Severities *[]Severity `gorm:"many2many:vulnerability_severities"`
// DB specific info // DB specific info
DbSpecific *datatypes.JSON `gorm:"column:db_specific"` //DbSpecific *datatypes.JSON `gorm:"many2many:vulnerability_db_specific"`
// Affected is a list of affected entries related to this vulnerability // Affected is a list of affected entries related to this vulnerability
Affected *[]Affected `gorm:"foreignKey:VulnerabilityID"` Affected *[]Affected `gorm:"foreignKey:VulnerabilityID"`
@ -86,6 +99,36 @@ type Vulnerability struct {
batchWriteAffected *[]Affected `gorm:"-"` batchWriteAffected *[]Affected `gorm:"-"`
} }
func (a *Vulnerability) BeforeSave(tx *gorm.DB) (err error) {
if a.Severities != nil {
uniqueSevs, err := a.uniqueSevs(tx)
if err != nil {
return err
}
a.Severities = uniqueSevs
}
return nil
}
func (a *Vulnerability) uniqueSevs(tx *gorm.DB) (*[]Severity, error) {
var uniqueSevs []Severity
for _, sev := range *a.Severities {
var existing Severity
result := tx.Where("type = ? AND score = ? AND source = ? AND rank = ?", sev.Type, sev.Score, sev.Source, sev.Rank).First(&existing)
if result.Error == nil {
uniqueSevs = append(uniqueSevs, existing)
} else {
err := tx.Create(&sev).Error
if err != nil {
return nil, err
}
uniqueSevs = append(uniqueSevs, sev)
}
}
return &uniqueSevs, nil
}
func (c *Vulnerability) BeforeCreate(tx *gorm.DB) error { func (c *Vulnerability) BeforeCreate(tx *gorm.DB) error {
// if the len of Affected is > 500, then create those in batches and then attach those to the Vulnerability // if the len of Affected is > 500, then create those in batches and then attach those to the Vulnerability
if c.Affected != nil && len(*c.Affected) > 500 { if c.Affected != nil && len(*c.Affected) > 500 {
@ -181,18 +224,29 @@ type Severity struct {
ID int64 `gorm:"column:id;primaryKey"` ID int64 `gorm:"column:id;primaryKey"`
// Type describes the quantitative method used to determine the Score, such as "CVSS_V3". Alternatively this makes claim that Score is qualitative, such as just simply "string" // Type describes the quantitative method used to determine the Score, such as "CVSS_V3". Alternatively this makes claim that Score is qualitative, such as just simply "string"
Type string `gorm:"column:type;not null"` Type string `gorm:"column:type;index:idx_severity,unique"`
// Score is the quantitative or qualitative severity score (e.g. "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N" or "high") // Score is the quantitative or qualitative severity score (e.g. "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N" or "high")
Score string `gorm:"column:score;not null"` Score string `gorm:"column:score;not null;index:idx_severity,unique"`
// Source is the name of the source of the severity score (e.g. "nvd@nist.gov" or "security-advisories@github.com") // Source is the name of the source of the severity score (e.g. "nvd@nist.gov" or "security-advisories@github.com")
Source string `gorm:"column:source"` Source string `gorm:"column:source;index:idx_severity,unique"`
// Rank is a free-form organizational field to convey priority over other severities // Rank is a free-form organizational field to convey priority over other severities
Rank int `gorm:"column:priority"` Rank int `gorm:"column:rank;index:idx_severity,unique"`
} }
//func (c *Severity) 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 Severity
// result := tx.Where("type = ? AND score = ? AND source = ? AND rank = ?", c.Type, c.Score, c.Source, c.Rank).First(&existing)
// if result.Error == nil {
// // if the record already exists, then we should use the existing record
// *c = existing
// }
// return nil
//}
type Reference struct { type Reference 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"`
@ -226,24 +280,90 @@ type Affected struct {
// package qualifiers // package qualifiers
PlatformCpeID *int64 `gorm:"column:platform_cpe_id"` PlatformCpes *[]PlatformCpe `gorm:"many2many:affected_platform_cpes"`
PlatformCpe *CpeWithoutVersion `gorm:"foreignKey:PlatformCpeID"`
RpmModularity string `gorm:"column:rpm_modularity"` RpmModularity string `gorm:"column:rpm_modularity"`
// identifiers // identifiers
Package *Package `gorm:"embedded;embeddedPrefix:package_"` Package *Package `gorm:"embedded;embeddedPrefix:package_"`
Digest *Digest `gorm:"embedded;embeddedPrefix:digest_"` //Digests *[]Digest `gorm:"many2many:affected_digests"`
//Cpe *Cpe `gorm:"embedded;embeddedPrefix:cpe_"` Cpes *[]Cpe `gorm:"many2many:affected_cpes"`
CpeID *int64 `gorm:"column:cpe_id"`
Cpe *CpeWithoutVersion `gorm:"foreignKey:CpeID"`
// fix // fix
Fix *Fix `gorm:"embedded;embeddedPrefix:fix_"` Fix *Fix `gorm:"embedded;embeddedPrefix:fix_"`
} }
func (a *Affected) BeforeSave(tx *gorm.DB) (err error) {
if a.Cpes != nil {
uniqueCpes, err := a.uniqueCpes(tx)
if err != nil {
return err
}
a.Cpes = uniqueCpes
}
if a.PlatformCpes != nil {
uniqueCpes, err := a.uniquePlatformCpes(tx)
if err != nil {
return err
}
a.PlatformCpes = uniqueCpes
}
if a.OperatingSystem != nil {
result := tx.Where("name = ? AND major_version = ? AND minor_version = ?", a.OperatingSystem.Name, a.OperatingSystem.MajorVersion, a.OperatingSystem.MinorVersion).First(&a.OperatingSystem)
if result.Error != nil {
err := tx.Create(&a.OperatingSystem).Error
if err != nil {
return err
}
}
}
return nil
}
func (a *Affected) uniquePlatformCpes(tx *gorm.DB) (*[]PlatformCpe, error) {
var uniqueCpes []PlatformCpe
for _, cpe := range *a.PlatformCpes {
var existing PlatformCpe
result := tx.Where("type = ? AND vendor = ? AND product = ? AND edition = ? AND language = ? AND version = ? AND version_update = ? AND software_edition = ? AND target_hardware = ? AND target_software = ? AND other = ?",
cpe.Type, cpe.Vendor, cpe.Product, cpe.Edition, cpe.Language, cpe.Version, cpe.VersionUpdate, cpe.SoftwareEdition, cpe.TargetHardware, cpe.TargetSoftware, cpe.Other).First(&existing)
if result.Error == nil {
uniqueCpes = append(uniqueCpes, existing)
} else {
err := tx.Create(&cpe).Error
if err != nil {
return nil, err
}
uniqueCpes = append(uniqueCpes, cpe)
}
}
return &uniqueCpes, nil
}
func (a *Affected) uniqueCpes(tx *gorm.DB) (*[]Cpe, error) {
var uniqueCpes []Cpe
for _, cpe := range *a.Cpes {
var existing Cpe
result := tx.Where("type = ? AND vendor = ? AND product = ? AND edition = ? AND language = ? AND software_edition = ? AND target_hardware = ? AND target_software = ? AND other = ?",
cpe.Type, cpe.Vendor, cpe.Product, cpe.Edition, cpe.Language, cpe.SoftwareEdition, cpe.TargetHardware, cpe.TargetSoftware, cpe.Other).First(&existing)
if result.Error == nil {
uniqueCpes = append(uniqueCpes, existing)
} else {
err := tx.Create(&cpe).Error
if err != nil {
return nil, err
}
uniqueCpes = append(uniqueCpes, cpe)
}
}
return &uniqueCpes, nil
}
// TODO: add later and reuse existing similar tables with many2many // TODO: add later and reuse existing similar tables with many2many
//type NotAffected struct { //type NotAffected struct {
// ID int64 `gorm:"column:id;primaryKey"` // ID int64 `gorm:"column:id;primaryKey"`
@ -257,7 +377,7 @@ type Affected struct {
// Range *[]Range `gorm:"foreignKey:AffectedID"` // Range *[]Range `gorm:"foreignKey:AffectedID"`
// //
// // 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:not_affected_digests"` // Digests *[]Digests `gorm:"many2many:not_affected_digests"`
//} //}
type Fix struct { type Fix struct {
@ -277,7 +397,7 @@ type Fix struct {
// primary package identifiers (search entrypoints) // primary package identifiers (search entrypoints)
type CpeWithoutVersion struct { type Cpe struct {
// TODO: what about different CPE versions? // TODO: what about different CPE versions?
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey"`
@ -292,14 +412,46 @@ type CpeWithoutVersion struct {
Other string `gorm:"column:other;index:idx_cpe,unique"` Other string `gorm:"column:other;index:idx_cpe,unique"`
} }
func (c CpeWithoutVersion) String() string { func (c Cpe) String() string {
return fmt.Sprintf("%s:%s:%s:%s:%s:%s:%s:%s:%s", c.Type, c.Vendor, c.Product, c.Edition, c.Language, c.SoftwareEdition, c.TargetHardware, c.TargetSoftware, c.Other) return fmt.Sprintf("%s:%s:%s:%s:%s:%s:%s:%s:%s", c.Type, c.Vendor, c.Product, c.Edition, c.Language, c.SoftwareEdition, c.TargetHardware, c.TargetSoftware, c.Other)
} }
//func (c *Cpe) BeforeCreate(tx *gorm.DB) (err error) { //func (c *Cpe) 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 // // if the name, major version, and minor version already exist in the table then we should not insert a new record
// var existing Cpe // var existing Cpe
// result := tx.Where("schema = ? AND type = ? AND vendor = ? AND product = ? AND version = ? AND version_update = ? AND target_software = ?", c.Schema, c.Type, c.Vendor, c.Product, c.Version, c.Update, c.TargetSoftware).First(&existing) // result := tx.Where("type = ? AND vendor = ? AND product = ? AND edition = ? AND language = ? AND software_edition = ? AND target_hardware = ? AND target_software = ? AND other = ?", c.Type, c.Vendor, c.Product, c.Edition, c.Language, c.SoftwareEdition, c.TargetHardware, c.TargetSoftware, c.Other).First(&existing)
// if result.Error == nil {
// // if the record already exists, then we should use the existing record
// *c = existing
// }
// return nil
//}
type PlatformCpe struct {
// TODO: what about different CPE versions?
ID int64 `gorm:"primaryKey"`
Type string `gorm:"column:type;not null;index:idx_platform_cpe,unique"`
Vendor string `gorm:"column:vendor;index:idx_platform_cpe,unique"`
Product string `gorm:"column:product;not null;index:idx_platform_cpe,unique"`
Edition string `gorm:"column:edition;index:idx_platform_cpe,unique"`
Language string `gorm:"column:language;index:idx_platform_cpe,unique"`
Version string `gorm:"column:version;index:idx_platform_cpe,unique"`
VersionUpdate string `gorm:"column:version_update;index:idx_platform_cpe,unique"`
SoftwareEdition string `gorm:"column:software_edition;index:idx_platform_cpe,unique"`
TargetHardware string `gorm:"column:target_hardware;index:idx_platform_cpe,unique"`
TargetSoftware string `gorm:"column:target_software;index:idx_platform_cpe,unique"`
Other string `gorm:"column:other;index:idx_platform_cpe,unique"`
}
func (c PlatformCpe) String() string {
return fmt.Sprintf("%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s", c.Type, c.Vendor, c.Product, c.Edition, c.Language, c.Version, c.VersionUpdate, c.SoftwareEdition, c.TargetHardware, c.TargetSoftware, c.Other)
}
//func (c *PlatformCpe) 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 PlatformCpe
// result := tx.Where("type = ? AND vendor = ? AND product = ? AND edition = ? AND language = ? AND version = ? AND version_update = ? AND software_edition = ? AND target_hardware = ? AND target_software = ? AND other = ?", c.Type, c.Vendor, c.Product, c.Edition, c.Language, c.Version, c.VersionUpdate, c.SoftwareEdition, c.TargetHardware, c.TargetSoftware, c.Other).First(&existing)
// if result.Error == nil { // if result.Error == nil {
// // if the record already exists, then we should use the existing record // // if the record already exists, then we should use the existing record
// *c = existing // *c = existing
@ -324,7 +476,7 @@ type Package struct {
//OperatingSystem *OperatingSystem `gorm:"foreignKey:OperatingSystemID"` //OperatingSystem *OperatingSystem `gorm:"foreignKey:OperatingSystemID"`
// //
//Purls *[]Purl `gorm:"many2many:package_purls"` //Purls *[]Purl `gorm:"many2many:package_purls"`
//Cpes *[]Cpe `gorm:"many2many:package_cpes"` //Cpes *[]Cpes `gorm:"many2many:package_cpes"`
} }
@ -388,12 +540,6 @@ type DbMetadata struct {
Addition int `gorm:"column:addition;not null"` Addition int `gorm:"column:addition;not null"`
} }
// TODO: this will probably be shipped as another DB and attached on as-needed basis
type Blob struct {
Digest string `gorm:"column:digest;primaryKey"`
Value string `gorm:"column:value;not null"`
}
// TODO: not clear that we need this... // TODO: not clear that we need this...
//type Comment struct { //type Comment struct {
// ID int64 `gorm:"column:id;primaryKey"` // ID int64 `gorm:"column:id;primaryKey"`

View file

@ -24,6 +24,7 @@ type Store interface {
AffectedPackageStore AffectedPackageStore
ProviderStore ProviderStore
OperatingSystemStore OperatingSystemStore
BlobStore
io.Closer io.Closer
} }
@ -36,6 +37,7 @@ type store struct {
AffectedPackageStore AffectedPackageStore
ProviderStore ProviderStore
OperatingSystemStore OperatingSystemStore
BlobStore
} }
func (c *StoreConfig) state() *state { func (c *StoreConfig) state() *state {
@ -63,6 +65,7 @@ func New(cfg StoreConfig) (Store, error) {
AffectedStore: NewAffectedStore(&cfg), AffectedStore: NewAffectedStore(&cfg),
VulnerabilityStore: NewVulnerabilityStore(&cfg), VulnerabilityStore: NewVulnerabilityStore(&cfg),
AffectedPackageStore: NewAffectedPackageStore(&cfg), AffectedPackageStore: NewAffectedPackageStore(&cfg),
BlobStore: NewBlobStore(&cfg),
}, nil }, nil
} }

View file

@ -1,9 +1,7 @@
package v6 package v6
import ( import (
"fmt"
"github.com/anchore/grype/internal/log" "github.com/anchore/grype/internal/log"
"gorm.io/gorm"
) )
type VulnerabilityStore interface { type VulnerabilityStore interface {
@ -42,9 +40,9 @@ func (s *vulnerabilityStore) GetVulnerability(id string, loadAuxInfo bool) ([]Vu
func (s *vulnerabilityStore) AddVulnerabilities(vulnerabilities ...*Vulnerability) error { func (s *vulnerabilityStore) AddVulnerabilities(vulnerabilities ...*Vulnerability) error {
for _, h := range []func([]*Vulnerability) error{ for _, h := range []func([]*Vulnerability) error{
s.handleOSs, //s.handleOSs,
//s.handleRangeEvents, //s.handleRangeEvents,
s.handleCPEs, //s.handleCPEs,
//s.handlePackages, //s.handlePackages,
} { } {
if err := h(vulnerabilities); err != nil { if err := h(vulnerabilities); err != nil {
@ -53,7 +51,13 @@ func (s *vulnerabilityStore) AddVulnerabilities(vulnerabilities ...*Vulnerabilit
} }
return s.db.CreateInBatches(vulnerabilities, s.BatchSize).Error for _, v := range vulnerabilities {
if err := s.db.Create(v).Error; err != nil {
return err
}
}
return nil
} }
//func (s *vulnerabilityStore) handlePackages(vulns []*Vulnerability) error { //func (s *vulnerabilityStore) handlePackages(vulns []*Vulnerability) error {
@ -130,83 +134,83 @@ func (s *vulnerabilityStore) AddVulnerabilities(vulnerabilities ...*Vulnerabilit
// //
//} //}
func (s *vulnerabilityStore) handleCPEs(vulns []*Vulnerability) error { //func (s *vulnerabilityStore) handleCPEs(vulns []*Vulnerability) error {
// ensure unique cpes // // 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 cpes IDs // // update vulnerabilities with cpes IDs
updateAffectedsWithCPEs(vulns, unique) // updateAffectedsWithCPEs(vulns, unique)
//
return nil // return nil
} //}
//
func updateAffectedsWithCPEs(vulns []*Vulnerability, uniqueCPEs []*CpeWithoutVersion) { //func updateAffectedsWithCPEs(vulns []*Vulnerability, uniqueCPEs []*Cpes) {
cpeMap := make(map[string]int64) // cpeMap := make(map[string]int64)
for _, c := range uniqueCPEs { // for _, c := range uniqueCPEs {
cpeKey := c.String() // cpeKey := c.String()
cpeMap[cpeKey] = c.ID // cpeMap[cpeKey] = c.ID
} // }
//
for i, v := range vulns { // for i, v := range vulns {
if v.Affected == nil { // if v.Affected == nil {
continue // continue
} // }
for j, a := range *v.Affected { // for j, a := range *v.Affected {
if a.Cpe != nil { // if a.Cpes != nil {
cpeKey := a.Cpe.String() // cpeKey := a.Cpes.String()
val := cpeMap[cpeKey] // val := cpeMap[cpeKey]
(*(*vulns[i].Affected)[j].Cpe).ID = val // (*(*vulns[i].Affected)[j].Cpes).ID = val
} // }
if a.PlatformCpe != nil { // if a.PlatformCpes != nil {
cpeKey := a.PlatformCpe.String() // cpeKey := a.PlatformCpes.String()
val := cpeMap[cpeKey] // val := cpeMap[cpeKey]
(*(*vulns[i].Affected)[j].PlatformCpe).ID = val // (*(*vulns[i].Affected)[j].PlatformCpes).ID = val
} // }
} // }
} // }
} //}
//
func ensureUniqueCPEs(db *gorm.DB, vulns []*Vulnerability) ([]*CpeWithoutVersion, error) { //func ensureUniqueCPEs(db *gorm.DB, vulns []*Vulnerability) ([]*Cpes, error) {
cpeMap := make(map[string]CpeWithoutVersion) // cpeMap := make(map[string]Cpes)
for _, v := range vulns { // for _, v := range vulns {
if v.Affected == nil { // if v.Affected == nil {
continue // continue
} // }
for _, a := range *v.Affected { // for _, a := range *v.Affected {
if a.Cpe != nil { // if a.Cpes != nil {
cpeKey := a.Cpe.String() // cpeKey := a.Cpes.String()
cpeMap[cpeKey] = *a.Cpe // cpeMap[cpeKey] = *a.Cpes
} // }
if a.PlatformCpe != nil { // if a.PlatformCpes != nil {
cpeKey := a.PlatformCpe.String() // cpeKey := a.PlatformCpes.String()
cpeMap[cpeKey] = *a.PlatformCpe // cpeMap[cpeKey] = *a.PlatformCpes
} // }
} // }
} // }
//
// extract unique CPEs // // extract unique CPEs
var uniqueCPEs []*CpeWithoutVersion // var uniqueCPEs []*Cpes
for i := range cpeMap { // for i := range cpeMap {
c := cpeMap[i] // c := cpeMap[i]
uniqueCPEs = append(uniqueCPEs, &c) // uniqueCPEs = append(uniqueCPEs, &c)
} // }
//
// insert unique CPEs into the database or fetch existing ones // // insert unique CPEs into the database or fetch existing ones
for i, c := range uniqueCPEs { // for i, c := range uniqueCPEs {
var existing CpeWithoutVersion // var existing Cpes
err := db.Where("type = ? AND vendor = ? AND product = ? AND edition = ? AND language = ? AND software_edition = ? AND target_software = ? AND target_hardware = ? AND other = ?", c.Type, c.Vendor, c.Product, c.Edition, c.Language, c.SoftwareEdition, c.TargetSoftware, c.TargetHardware, c.Other). // err := db.Where("type = ? AND vendor = ? AND product = ? AND edition = ? AND language = ? AND software_edition = ? AND target_software = ? AND target_hardware = ? AND other = ?", c.Type, c.Vendor, c.Product, c.Edition, c.Language, c.SoftwareEdition, c.TargetSoftware, c.TargetHardware, c.Other).
FirstOrCreate(&existing, c).Error // FirstOrCreate(&existing, c).Error
if err != nil { // if err != nil {
return nil, err // return nil, err
} // }
uniqueCPEs[i] = &existing // uniqueCPEs[i] = &existing
} // }
//
return uniqueCPEs, nil // return uniqueCPEs, nil
} //}
//func (s *vulnerabilityStore) handleRangeEvents(vulns []*Vulnerability) error { //func (s *vulnerabilityStore) handleRangeEvents(vulns []*Vulnerability) error {
// // ensure unique operating systems // // ensure unique operating systems
@ -295,67 +299,67 @@ func ensureUniqueCPEs(db *gorm.DB, vulns []*Vulnerability) ([]*CpeWithoutVersion
// return *s // return *s
//} //}
func (s *vulnerabilityStore) handleOSs(vulns []*Vulnerability) error { //func (s *vulnerabilityStore) handleOSs(vulns []*Vulnerability) error {
// ensure unique operating systems // // ensure unique operating systems
uniqueOSList, err := ensureUniqueOperatingSystems(s.db, vulns) // uniqueOSList, err := ensureUniqueOperatingSystems(s.db, vulns)
if err != nil { // if err != nil {
return err // return err
} // }
//
// update vulnerabilities with operating system IDs // // update vulnerabilities with operating system IDs
updateAffectedsWithOperatingSystems(vulns, uniqueOSList) // updateAffectedsWithOperatingSystems(vulns, uniqueOSList)
//
return nil // return nil
} //}
//
func ensureUniqueOperatingSystems(db *gorm.DB, vulns []*Vulnerability) ([]*OperatingSystem, error) { //func ensureUniqueOperatingSystems(db *gorm.DB, vulns []*Vulnerability) ([]*OperatingSystem, error) {
// map to track unique operating systems // // map to track unique operating systems
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.OperatingSystem == nil { // if a.OperatingSystem == nil {
continue // continue
} // }
osKey := fmt.Sprintf("%s:%s:%s", a.OperatingSystem.Name, a.OperatingSystem.MajorVersion, a.OperatingSystem.MinorVersion) // osKey := fmt.Sprintf("%s:%s:%s", a.OperatingSystem.Name, a.OperatingSystem.MajorVersion, a.OperatingSystem.MinorVersion)
osMap[osKey] = a.OperatingSystem // osMap[osKey] = a.OperatingSystem
} // }
} // }
//
// extract unique operating systems // // extract unique operating systems
var uniqueOSList []*OperatingSystem // var uniqueOSList []*OperatingSystem
for _, os := range osMap { // for _, os := range osMap {
uniqueOSList = append(uniqueOSList, os) // uniqueOSList = append(uniqueOSList, os)
} // }
//
// insert unique operating systems into the database or fetch existing ones // // insert unique operating systems into the database or fetch existing ones
for i, os := range uniqueOSList { // for i, os := range uniqueOSList {
var existing OperatingSystem // var existing OperatingSystem
err := db.Where("name = ? AND major_version = ? AND minor_version = ?", os.Name, os.MajorVersion, os.MinorVersion). // err := db.Where("name = ? AND major_version = ? AND minor_version = ?", os.Name, os.MajorVersion, os.MinorVersion).
FirstOrCreate(&existing, os).Error // FirstOrCreate(&existing, os).Error
if err != nil { // if err != nil {
return nil, err // return nil, err
} // }
uniqueOSList[i].ID = existing.ID // uniqueOSList[i].ID = existing.ID
} // }
//
return uniqueOSList, nil // return uniqueOSList, nil
} //}
//
func updateAffectedsWithOperatingSystems(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)
osMap[osKey] = os.ID // osMap[osKey] = os.ID
} // }
//
for i, v := range vulns { // for i, v := range vulns {
for j, a := range *v.Affected { // for j, a := range *v.Affected {
if a.OperatingSystem == nil { // if a.OperatingSystem == nil {
continue // continue
} // }
osKey := fmt.Sprintf("%s:%s:%s", a.OperatingSystem.Name, a.OperatingSystem.MajorVersion, a.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].OperatingSystemID = &val // (*vulns[i].Affected)[j].OperatingSystemID = &val
} // }
} // }
} //}