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 (
"fmt"
"github.com/OneOfOne/xxhash"
"github.com/anchore/grype/internal/log"
)
func BlobDigest(content string) string {
h := xxhash.New64()
h.Write([]byte(content)) // TODO: handle error?
return fmt.Sprintf("xx64:%x", h.Sum(nil))
h.Write([]byte(content)) // TODO: handle error?
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 (
"fmt"
"gorm.io/datatypes"
"gorm.io/gorm"
"time"
)
func All() []any {
return []any{
&CpeWithoutVersion{},
&Cpe{},
&PlatformCpe{},
&Digest{},
&Affected{},
&Alias{},
&Blob{},
&DbMetadata{},
&DbSpecificNvd{},
//&DbSpecificNvd{},
&Epss{},
&KnownExploitedVulnerability{},
&OperatingSystem{},
&Provider{},
&Reference{},
&Severity{},
&Affected{},
&Vulnerability{},
}
}
// core vulnerability types
type Advisory struct {
ID string
// 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"`
}
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
@ -62,8 +73,10 @@ type Vulnerability struct {
Status string `gorm:"column:status"` // example: "active, withdrawn, rejected, ..." could be an enum
Summary string `gorm:"column:summary"`
Detail string `gorm:"column:detail;index,unique"`
//Summary string `gorm:"column:summary"`
//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 *[]Reference `gorm:"foreignKey:VulnerabilityID"`
@ -75,10 +88,10 @@ type Vulnerability struct {
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 *datatypes.JSONSlice[Severity] `gorm:"column:severities"`
Severities *[]Severity `gorm:"many2many:vulnerability_severities"`
// 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 *[]Affected `gorm:"foreignKey:VulnerabilityID"`
@ -86,6 +99,36 @@ type Vulnerability struct {
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 {
// 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 {
@ -181,18 +224,29 @@ type Severity struct {
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 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 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 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 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 {
ID int64 `gorm:"column:id;primaryKey"`
VulnerabilityID int64 `gorm:"column:vulnerability_id;not null"`
@ -226,24 +280,90 @@ type Affected struct {
// package qualifiers
PlatformCpeID *int64 `gorm:"column:platform_cpe_id"`
PlatformCpe *CpeWithoutVersion `gorm:"foreignKey:PlatformCpeID"`
PlatformCpes *[]PlatformCpe `gorm:"many2many:affected_platform_cpes"`
RpmModularity string `gorm:"column:rpm_modularity"`
// identifiers
Package *Package `gorm:"embedded;embeddedPrefix:package_"`
Digest *Digest `gorm:"embedded;embeddedPrefix:digest_"`
//Cpe *Cpe `gorm:"embedded;embeddedPrefix:cpe_"`
CpeID *int64 `gorm:"column:cpe_id"`
Cpe *CpeWithoutVersion `gorm:"foreignKey:CpeID"`
//Digests *[]Digest `gorm:"many2many:affected_digests"`
Cpes *[]Cpe `gorm:"many2many:affected_cpes"`
// 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
//type NotAffected struct {
// ID int64 `gorm:"column:id;primaryKey"`
@ -257,7 +377,7 @@ type Affected struct {
// Range *[]Range `gorm:"foreignKey:AffectedID"`
//
// // 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 {
@ -277,7 +397,7 @@ type Fix struct {
// primary package identifiers (search entrypoints)
type CpeWithoutVersion struct {
type Cpe struct {
// TODO: what about different CPE versions?
ID int64 `gorm:"primaryKey"`
@ -292,14 +412,46 @@ type CpeWithoutVersion struct {
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)
}
//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
// 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 the record already exists, then we should use the existing record
// *c = existing
@ -324,7 +476,7 @@ type Package struct {
//OperatingSystem *OperatingSystem `gorm:"foreignKey:OperatingSystemID"`
//
//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"`
}
// 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...
//type Comment struct {
// ID int64 `gorm:"column:id;primaryKey"`

View file

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

View file

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