modify schema to meet wes-attempt-1

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman 2024-06-27 14:59:07 -04:00
parent 536bd2f6af
commit 7c9090a6be
5 changed files with 238 additions and 306 deletions

View file

@ -48,7 +48,7 @@ func populateFixture1(store v6.Store) error {
{Alias: "CVE-2024-12341"},
},
Severities: &[]v6.Severity{
{Type: "CVSS_V3", Score: "5.0", Source: sp("nvd@nist.gov"), Priority: sp("secondary")},
{Type: "CVSS_V3", Score: "5.0", Source: sp("nvd@nist.gov"), Rank: sp("secondary")},
},
References: &[]v6.Reference{
{Type: "ADVISORY", URL: "https://example.com/GHSA-xxxx-xxxx-xxxx1"},
@ -84,7 +84,7 @@ func populateFixture1(store v6.Store) error {
{Alias: "CVE-2024-12342"},
},
Severities: &[]v6.Severity{
{Type: "CVSS_V3", Score: "7.5", Source: sp("nvd@nist.gov"), Priority: sp("primary")},
{Type: "CVSS_V3", Score: "7.5", Source: sp("nvd@nist.gov"), Rank: sp("primary")},
},
References: &[]v6.Reference{
{Type: "ADVISORY", URL: "https://example.com/GHSA-xxxx-xxxx-xxxx2"},
@ -125,7 +125,7 @@ func populateFixture1(store v6.Store) error {
{Alias: "GHSA-xxxx-xxxx-xxxx3"},
},
Severities: &[]v6.Severity{
{Type: "CVSS_V3", Score: "6.0", Source: sp("nvd@nist.gov"), Priority: sp("primary")},
{Type: "CVSS_V3", Score: "6.0", Source: sp("nvd@nist.gov"), Rank: sp("primary")},
},
References: &[]v6.Reference{
{Type: "ADVISORY", URL: "https://example.com/CVE-2024-12343"},
@ -169,7 +169,7 @@ func populateFixture1(store v6.Store) error {
{Alias: fmt.Sprintf("CVE-2024-1234%d", i+3)},
},
Severities: &[]v6.Severity{
{Type: "CVSS_V3", Score: "4.0", Source: sp("nvd@nist.gov"), Priority: sp("primary")},
{Type: "CVSS_V3", Score: "4.0", Source: sp("nvd@nist.gov"), Rank: sp("primary")},
},
References: &[]v6.Reference{
{Type: "ADVISORY", URL: fmt.Sprintf("https://example.com/GHSA-xxxx-xxxx-xxxx%d", i+3)},

View file

@ -88,7 +88,7 @@ func run() error {
// Type: "something",
// Score: "2.0",
// Source: "nvd",
// Priority: "something-else",
// Rank: "something-else",
// },
// },
// },
@ -100,7 +100,7 @@ func run() error {
// Type: "something",
// Score: "2.0",
// Source: "nvd",
// Priority: "something-else",
// Rank: "something-else",
// },
// },
// },
@ -161,7 +161,7 @@ func severityStringer(vs *[]v6.Severity) string {
}
stringer := func(v v6.Severity) string {
return fmt.Sprintf("{Type=%q, Score=%q, Source=%q, Priority=%q}", v.Type, v.Score, v.Source, v.Priority)
return fmt.Sprintf("{Type=%q, Score=%q, Source=%q, Rank=%q}", v.Type, v.Score, v.Source, v.Rank)
}
var strs []string

View file

@ -300,7 +300,7 @@ func InsertVulnerabilities(db *gorm.DB, vulns []*Vulnerability) error {
// Type string `gorm:"<-:create;column:type;not null;index:idx_severity,unique"`
// Score string `gorm:"<-:create;column:score;not null;index:idx_severity,unique"`
// Source string `gorm:"<-:create;column:source;index:idx_severity,unique"`
// Priority string `gorm:"<-:create;column:priority;index:idx_severity,unique"`
// Rank string `gorm:"<-:create;column:priority;index:idx_severity,unique"`
//}
//
//func main() {
@ -332,19 +332,19 @@ func InsertVulnerabilities(db *gorm.DB, vulns []*Vulnerability) error {
// Type: "string",
// Score: "high",
// Source: "nvd",
// Priority: "primary",
// Rank: "primary",
// },
// {
// Type: "string",
// Score: "high",
// Source: "nvd",
// Priority: "primary",
// Rank: "primary",
// },
// {
// Type: "string",
// Score: "medium",
// Source: "cve",
// Priority: "secondary",
// Rank: "secondary",
// },
// },
// },
@ -358,19 +358,19 @@ func InsertVulnerabilities(db *gorm.DB, vulns []*Vulnerability) error {
// Type: "string",
// Score: "high",
// Source: "nvd",
// Priority: "primary",
// Rank: "primary",
// },
// {
// Type: "string",
// Score: "medium",
// Source: "cve",
// Priority: "secondary",
// Rank: "secondary",
// },
// {
// Type: "string",
// Score: "medium",
// Source: "somewhere-else",
// Priority: "secondary",
// Rank: "secondary",
// },
// },
// },
@ -388,7 +388,7 @@ func InsertVulnerabilities(db *gorm.DB, vulns []*Vulnerability) error {
// }
//
// for _, sev := range *vuln.Severities {
// //if err := db.Where("type = ? AND score = ? AND source = ? AND priority = ?", sev.Type, sev.Score, sev.Source, sev.Priority).FirstOrCreate(&sev).Error; err != nil {
// //if err := db.Where("type = ? AND score = ? AND source = ? AND priority = ?", sev.Type, sev.Score, sev.Source, sev.Rank).FirstOrCreate(&sev).Error; err != nil {
// // fmt.Println("Failed to create severity:", err)
// // return
// //}

View file

@ -9,30 +9,18 @@ import (
func All() []any {
return []any{
&Cpe{},
&CpeWithoutVersion{},
&Digest{},
&AffectedSeverity{},
&AffectedVersion{},
&Affected{},
&Alias{},
&Blob{},
//&Comment{},
&DbMetadata{},
&DbSpecificNvd{},
&Epss{},
&KnownExploitedVulnerability{},
//&LogicalPackage{},
&AffectedExcludeVersion{},
&OperatingSystem{},
&PackageQualifierPlatformCpe{},
&PackageQualifierRpmModularity{},
//&Package{},
&Provider{},
&RangeEvent{},
//&RangeEventMetadata{},
&Range{},
&Reference{},
&Related{},
&Severity{},
&Vulnerability{},
}
@ -40,6 +28,12 @@ func All() []any {
// core vulnerability types
type Advisory struct {
ID string
VulnerabilityIDs []string
}
// Vulnerability represents the core advisory record for a single known vulnerability from a specific provider. There
// may be multiple vulnerabilities with the same name.
type Vulnerability struct {
@ -48,6 +42,9 @@ type Vulnerability struct {
// ProviderID is the foreign key to the Provider table which indicates the upstream data source for this vulnerability.
ProviderID string `gorm:"column:provider_id;not null;index:idx_vulnerability_provider"`
//// ProviderNamespace is an optional field reserved for logical grouping of vulnerabilities from the same provider (e.g. OS version number)
//ProviderNamespace string `gorm:"column:provider_namespace;not null;index:idx_vulnerability_provider"`
// Provider is the result of a join with the Provider table, which represents all information about where this vulnerability record came from.
Provider *Provider
@ -55,19 +52,18 @@ type Vulnerability struct {
Name string `gorm:"column:name;not null;index;index:idx_vulnerability_provider"`
// Modified is the time the entry was last modified, as an RFC3339-formatted timestamp in UTC (ending in “Z”) (mirrors the OSV field)
Modified *string `gorm:"column:modified"`
Modified string `gorm:"column:modified"`
// Published is the time the entry should be considered to have been published, as an RFC3339-formatted time stamp in UTC (ending in “Z”) (mirrors the OSV field)
Published *string `gorm:"column:published"`
Published string `gorm:"column:published"`
// Withdrawn is the time the entry should be considered to have been withdrawn, as an RFC3339-formatted timestamp in UTC (ending in “Z”) (mirrors the OSV field)
Withdrawn *string `gorm:"column:withdrawn"`
Withdrawn string `gorm:"column:withdrawn"`
// SummaryDigest is a self describing hash (e.g. sha256:123... not 123...) of the summary field from the OSV summary field. This digest is searched against the Blob table/DB.
SummaryDigest *string `gorm:"column:summary_digest"`
Status string `gorm:"column:status"` // example: "active, withdrawn, rejected, ..." could be an enum
// DetailDigest is a self describing hash (e.g. sha256:123... not 123...) of the detail field from the OSV summary field. This digest is searched against the Blob table/DB.
DetailDigest *string `gorm:"column:detail_digest"`
Summary string `gorm:"column:summary"`
Detail string `gorm:"column:detail;index,unique"`
// References are URLs to external resources that provide more information about the vulnerability (mirrors the OSV field)
References *[]Reference `gorm:"foreignKey:VulnerabilityID"`
@ -87,13 +83,13 @@ type Vulnerability struct {
// Affected is a list of affected entries related to this vulnerability
Affected *[]Affected `gorm:"foreignKey:VulnerabilityID"`
affected *[]Affected `gorm:"-"`
batchWriteAffected *[]Affected `gorm:"-"`
}
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 {
c.affected = c.Affected
c.batchWriteAffected = c.Affected
c.Affected = nil
}
@ -101,14 +97,14 @@ func (c *Vulnerability) BeforeCreate(tx *gorm.DB) error {
}
func (c *Vulnerability) AfterCreate(tx *gorm.DB) error {
if c.affected == nil {
if c.batchWriteAffected == nil {
return nil
}
// create in batches...
var affecteds []*Affected
affs := *c.affected
affs := *c.batchWriteAffected
for i := range affs {
a := affs[i]
a.VulnerabilityID = c.ID
@ -123,7 +119,7 @@ func (c *Vulnerability) AfterCreate(tx *gorm.DB) error {
affs[i] = *affecteds[i]
}
c.Affected = &affs
c.affected = nil
c.batchWriteAffected = nil
return nil
}
@ -168,17 +164,17 @@ type Alias struct {
Alias string `gorm:"column:alias;not null,index:idx_alias,unique"`
}
// Related represents a single related vulnerability name
type Related struct {
ID int64 `gorm:"column:id;primaryKey"`
//VulnerabilityID int64 `gorm:"column:vulnerability_id;not null"`
// Name of the related vulnerability (e.g. CVE-2024-34102 or GHSA-85rg-8m6h-825p)
Name string `gorm:"column:name;not null,index:idx_related,unique"`
//// Reason is a free-form text field that describes the relationship between the two vulnerabilities ("CVE-2022-12345 might be related to CVE-2022-54321 because both affect the same software library but are distinct issues")
//Reason string `gorm:"column:reason"`
}
//type CVSS struct {
// Vector string `gorm:"column:vector"`
//
// // 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"`
//
// Vendor datatypes.JSON `gorm:"column:vendor"`
//
// // Rank is a free-form organizational field to convey priority over other severities
// Rank int `gorm:"column:priority"`
//}
// Severity represents a single severity record for a vulnerability
type Severity struct {
@ -191,10 +187,10 @@ type Severity struct {
Score string `gorm:"column:score;not null"`
// 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"`
// Priority is a free-form organizational field to convey priority over other severities (e.g. primary vs secondary or authoritative vs unverified)
Priority *string `gorm:"column:priority"` // TODO: naming is hard...
// Rank is a free-form organizational field to convey priority over other severities
Rank int `gorm:"column:priority"`
}
type Reference struct {
@ -222,21 +218,30 @@ 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
OperatingSystemID *int64 `gorm:"column:operating_system_id"`
OperatingSystem *OperatingSystem `gorm:"foreignKey:OperatingSystemID"`
Versions *datatypes.JSONSlice[AffectedVersion] `gorm:"column:versions"`
ExcludeVersions *datatypes.JSONSlice[AffectedExcludeVersion] `gorm:"column:excluded_versions"`
Severities *datatypes.JSONSlice[AffectedSeverity] `gorm:"column:severities"`
PackageQualifier *datatypes.JSON `gorm:"column:package_qualifier"`
VersionConstraint string `gorm:"column:version_constraint"`
VersionFormat string `gorm:"column:version_format"`
Range *[]Range `gorm:"foreignKey:AffectedID"`
Package *Package `gorm:"embedded;embeddedPrefix:package_"`
Digest *Digest `gorm:"embedded;embeddedPrefix:digest_"`
Cpes *datatypes.JSONSlice[Cpe] `gorm:"column:cpes"`
// package qualifiers
PlatformCpeID *int64 `gorm:"column:platform_cpe_id"`
PlatformCpe *CpeWithoutVersion `gorm:"foreignKey:PlatformCpeID"`
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"`
// fix
Fix *Fix `gorm:"embedded;embeddedPrefix:fix_"`
}
// TODO: add later and reuse existing similar tables with many2many
@ -255,71 +260,14 @@ type Affected struct {
// Digests *[]Digest `gorm:"many2many:not_affected_digests"`
//}
// TODO: reuse existing Severities tables with many2many
type AffectedSeverity struct {
ID int64 `gorm:"column:id;primaryKey"`
AffectedID int64 `gorm:"column:affected_id;not null"`
Type string `gorm:"column:type;not null"`
Score string `gorm:"column:score;not null"`
Source *string `gorm:"column:source"`
Priority *string `gorm:"column:priority"` // TODO: naming is hard...
type Fix struct {
Version string `gorm:"column:version"`
State string `gorm:"column:state"`
//Detail *FixDetail
}
type AffectedVersion struct {
ID int64 `gorm:"column:id;primaryKey"`
AffectedID int64 `gorm:"column:affected_id;not null"`
Version string `gorm:"column:version;not null"`
}
type AffectedExcludeVersion struct {
ID int64 `gorm:"column:id;primaryKey"`
AffectedID int64 `gorm:"column:affected_id;not null"`
Version string `gorm:"column:version;not null"`
}
type Range struct {
ID int64 `gorm:"primaryKey"`
AffectedID int64 `gorm:"column:affected_id;not null"`
Type string `gorm:"column:type;not null"`
Repo *string `gorm:"column:repo"`
Events *[]RangeEvent `gorm:"many2many:range_range_events"`
}
type RangeEvent struct {
ID int64 `gorm:"primaryKey"`
Introduced *string `gorm:"column:introduced;index:idx_range_event,unique"`
Fixed *string `gorm:"column:fixed;index:idx_range_event,unique"`
LastAffected *string `gorm:"column:last_affected;index:idx_range_event,unique"`
Limit *string `gorm:"column:range_limit;index:idx_range_event,unique"` // limit is a keyword in sql, so it's easier to just use range_limit instead
// non OSV...
State string `gorm:"column:state;index:idx_range_event,unique"` // TODO: this could be db specific since there will be multiple ways to represent/interpret this
// if deduplicating these, then this can't be associated
//RangeEventMetadata *[]RangeEventMetadata `gorm:"foreignKey:RangeEventID"`
}
func (re *RangeEvent) BeforeCreate(tx *gorm.DB) (err error) {
//tx = tx.Session(&gorm.Session{Logger: loggerIgnoreRecordNotFound{tx.Logger}})
// if the event already exist in the table then we should not insert a new record
var existing RangeEvent
result := tx.Where("introduced = ? AND fixed = ? AND last_affected = ? AND range_limit = ? AND state = ?", re.Introduced, re.Fixed, re.LastAffected, re.Limit, re.State).First(&existing)
if result.Error == nil {
// if the record already exists, then we should use the existing record
*re = existing
}
return nil
}
//type RangeEventMetadata struct {
// ID int64 `gorm:"primaryKey"`
// RangeEventID int64 `gorm:"column:range_event_id;not null"`
//type FixDetail struct {
// ID int64 `gorm:"primaryKey"`
//
// GitCommit *string `gorm:"column:git_commit"`
// PullRequestURL *string `gorm:"column:pull_request_url"`
@ -329,18 +277,23 @@ func (re *RangeEvent) BeforeCreate(tx *gorm.DB) (err error) {
// primary package identifiers (search entrypoints)
type Cpe struct {
type CpeWithoutVersion struct {
// TODO: what about different CPE versions?
ID int64 `gorm:"primaryKey"`
Schema string `gorm:"column:schema;not null;index:idx_cpe"` // effectively the CPE version
Type string `gorm:"column:type;not null;index:idx_cpe"`
Vendor *string `gorm:"column:vendor;index:idx_cpe"`
Product string `gorm:"column:product;not null;index:idx_cpe"`
Version *string `gorm:"column:version;index:idx_cpe"`
Update *string `gorm:"column:version_update;index:idx_cpe"` // update is a SQL keyword
TargetSoftware *string `gorm:"column:target_software;index:idx_cpe"`
Type string `gorm:"column:type;not null;index:idx_cpe,unique"`
Vendor string `gorm:"column:vendor;index:idx_cpe,unique"`
Product string `gorm:"column:product;not null;index:idx_cpe,unique"`
Edition string `gorm:"column:edition;index:idx_cpe,unique"`
Language string `gorm:"column:language;index:idx_cpe,unique"`
SoftwareEdition string `gorm:"column:software_edition;index:idx_cpe,unique"`
TargetHardware string `gorm:"column:target_hardware;index:idx_cpe,unique"`
TargetSoftware string `gorm:"column:target_software;index:idx_cpe,unique"`
Other string `gorm:"column:other;index:idx_cpe,unique"`
}
// TODO: should we also have the remaining CPE fields here?
func (c CpeWithoutVersion) 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) {
@ -364,8 +317,8 @@ type Package struct {
// TODO: setup unique indexes only for writing and drop before shipping for the best size tradeoff
// TODO: break purl out into fields here
Ecosystem *string `gorm:"column:ecosystem;index:idx_package"` // TODO: NVD doesn't have this, should this be nullable?
Name string `gorm:"column:name;index:idx_package"`
Type string `gorm:"column:type;index:idx_package"` // TODO: NVD doesn't have this, should this be nullable?
Name string `gorm:"column:name;index:idx_package"`
//OperatingSystemID *int64 `gorm:"column:operating_system_id"`
//OperatingSystem *OperatingSystem `gorm:"foreignKey:OperatingSystemID"`
@ -428,30 +381,6 @@ func (os *OperatingSystem) BeforeCreate(tx *gorm.DB) (err error) {
return nil
}
type PackageQualifierPlatformCpe struct {
ID int64 `gorm:"column:id;primaryKey"`
PackageID int64 `gorm:"column:package_id;not null"`
Cpe string `gorm:"column:cpe;not null"`
}
type PackageQualifierRpmModularity struct {
ID int64 `gorm:"column:id;primaryKey"`
PackageID int64 `gorm:"column:package_id;not null"`
Module string `gorm:"column:module;not null"`
}
// logical package info
//type LogicalPackage struct {
// ID int64 `gorm:"column:id;primaryKey"`
//
// Packages []Package `gorm:"many2many:logical_package_packages"`
//}
// aux
type DbMetadata struct {
BuildTimestamp *time.Time `gorm:"column:build_timestamp;not null"`
Model int `gorm:"column:model;not null"`

View file

@ -43,8 +43,8 @@ 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.handleRangeEvents,
//s.handleCPEs,
//s.handleRangeEvents,
s.handleCPEs,
//s.handlePackages,
} {
if err := h(vulnerabilities); err != nil {
@ -130,168 +130,171 @@ 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 []*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))
// cpeMap[cpeKey] = c.ID
// }
//
// for i, v := range vulns {
// if v.Affected == nil {
// continue
// }
// for j, a := range *v.Affected {
// if a.Cpes == nil {
// continue
// }
// 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].Cpes)[k].ID = val
// }
// }
// }
//}
//
//func ensureUniqueCPEs(db *gorm.DB, vulns []*Vulnerability) ([]*Cpe, error) {
// cpeMap := make(map[string]Cpe)
// for _, v := range vulns {
// if v.Affected == nil {
// continue
// }
// for _, a := range *v.Affected {
// if a.Cpes == nil {
// continue
// }
// 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
// }
// }
// }
//
// // extract unique CPEs
// var uniqueCPEs []*Cpe
// 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 Cpe
// err := db.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).
// 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
unique, err := ensureUniqueRangeEvents(s.db, vulns)
func (s *vulnerabilityStore) handleCPEs(vulns []*Vulnerability) error {
// ensure unique cpes
unique, err := ensureUniqueCPEs(s.db, vulns)
if err != nil {
return err
}
// update vulnerabilities with operating system IDs
updateAffectedsWithRanges(vulns, unique)
// update vulnerabilities with cpes IDs
updateAffectedsWithCPEs(vulns, unique)
return nil
}
func ensureUniqueRangeEvents(db *gorm.DB, vulns []*Vulnerability) ([]*RangeEvent, error) {
reMap := make(map[string]RangeEvent)
for _, v := range vulns {
for _, a := range *v.Affected {
if a.Range == nil {
continue
}
for _, r := range *a.Range {
if r.Events == nil {
continue
}
for _, re := range *r.Events {
reKey := fmt.Sprintf("%s:%s:%s:%s:%s", strVal(re.Introduced), strVal(re.Fixed), strVal(re.LastAffected), strVal(re.Limit), re.State)
reMap[reKey] = re
}
}
}
}
// extract unique range events
var uniqueRangeEvents []*RangeEvent
for i := range reMap {
re := reMap[i]
uniqueRangeEvents = append(uniqueRangeEvents, &re)
}
// insert unique range events into the database or fetch existing ones
for i, re := range uniqueRangeEvents {
var existing RangeEvent
err := db.Where("introduced = ? AND fixed = ? AND last_affected = ? AND range_limit = ? AND state = ?", re.Introduced, re.Fixed, re.LastAffected, re.Limit, re.State).
FirstOrCreate(&existing, re).Error
if err != nil {
return nil, err
}
uniqueRangeEvents[i] = &existing
}
return uniqueRangeEvents, nil
}
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)
reMap[reKey] = re.ID
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.Range == nil {
continue
if a.Cpe != nil {
cpeKey := a.Cpe.String()
val := cpeMap[cpeKey]
(*(*vulns[i].Affected)[j].Cpe).ID = val
}
for k, r := range *a.Range {
if r.Events == nil {
continue
}
for l, re := range *r.Events {
reKey := fmt.Sprintf("%s:%s:%s:%s:%s", strVal(re.Introduced), strVal(re.Fixed), strVal(re.LastAffected), strVal(re.Limit), re.State)
val := reMap[reKey]
(*(*(*vulns[i].Affected)[j].Range)[k].Events)[l].ID = val
}
if a.PlatformCpe != nil {
cpeKey := a.PlatformCpe.String()
val := cpeMap[cpeKey]
(*(*vulns[i].Affected)[j].PlatformCpe).ID = val
}
}
}
}
func strVal(s *string) string {
if s == nil {
return ""
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
}
}
}
return *s
// 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) handleRangeEvents(vulns []*Vulnerability) error {
// // ensure unique operating systems
// unique, err := ensureUniqueRangeEvents(s.db, vulns)
// if err != nil {
// return err
// }
//
// // update vulnerabilities with operating system IDs
// updateAffectedsWithRanges(vulns, unique)
//
// return nil
//}
//
//func ensureUniqueRangeEvents(db *gorm.DB, vulns []*Vulnerability) ([]*RangeEvent, error) {
// reMap := make(map[string]RangeEvent)
// for _, v := range vulns {
// for _, a := range *v.Affected {
// if a.Range == nil {
// continue
// }
// for _, r := range *a.Range {
// if r.Events == nil {
// continue
// }
// for _, re := range *r.Events {
// reKey := fmt.Sprintf("%s:%s:%s:%s:%s", strVal(re.Introduced), strVal(re.Fixed), strVal(re.LastAffected), strVal(re.Limit), re.State)
// reMap[reKey] = re
// }
// }
// }
// }
//
// // extract unique range events
// var uniqueRangeEvents []*RangeEvent
// for i := range reMap {
// re := reMap[i]
// uniqueRangeEvents = append(uniqueRangeEvents, &re)
// }
//
// // insert unique range events into the database or fetch existing ones
// for i, re := range uniqueRangeEvents {
// var existing RangeEvent
// err := db.Where("introduced = ? AND fixed = ? AND last_affected = ? AND range_limit = ? AND state = ?", re.Introduced, re.Fixed, re.LastAffected, re.Limit, re.State).
// FirstOrCreate(&existing, re).Error
// if err != nil {
// return nil, err
// }
// uniqueRangeEvents[i] = &existing
//
// }
//
// return uniqueRangeEvents, nil
//}
//
//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)
// reMap[reKey] = re.ID
// }
//
// for i, v := range vulns {
// for j, a := range *v.Affected {
// if a.Range == nil {
// continue
// }
// for k, r := range *a.Range {
// if r.Events == nil {
// continue
// }
// for l, re := range *r.Events {
// reKey := fmt.Sprintf("%s:%s:%s:%s:%s", strVal(re.Introduced), strVal(re.Fixed), strVal(re.LastAffected), strVal(re.Limit), re.State)
// val := reMap[reKey]
// (*(*(*vulns[i].Affected)[j].Range)[k].Events)[l].ID = val
// }
// }
// }
// }
//}
//
//func strVal(s *string) string {
// if s == nil {
// return ""
// }
// return *s
//}
func (s *vulnerabilityStore) handleOSs(vulns []*Vulnerability) error {
// ensure unique operating systems
uniqueOSList, err := ensureUniqueOperatingSystems(s.db, vulns)