Skip to content

Commit

Permalink
deduplicate vulns on store write (anchore#2319)
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Goodman <[email protected]>
  • Loading branch information
wagoodman authored Dec 12, 2024
1 parent 9bd48f5 commit d054e68
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 1 deletion.
36 changes: 35 additions & 1 deletion grype/db/v6/vulnerability_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v6

import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"
Expand Down Expand Up @@ -118,7 +119,40 @@ func (s *vulnerabilityStore) AddVulnerabilities(vulnerabilities ...*Vulnerabilit
}
}

// write the vulnerability handle to the DB
if err := s.addUniqueVulnerability(v); err != nil {
return err
}
}
return nil
}

func (s *vulnerabilityStore) addUniqueVulnerability(v *VulnerabilityHandle) error {
// if this is a unique (name, status, published, modified, withdrawn, provider_id, blob_id) then it will be created
// otherwise do not create the new entry (this is to prevent duplicates)
query := s.db.Where("name = ? AND status = ? AND provider_id = ? AND blob_id = ?", v.Name, v.Status, v.ProviderID, v.BlobID)

if v.PublishedDate != nil {
query = query.Where("published_date = ?", *v.PublishedDate)
} else {
query = query.Where("published_date IS NULL")
}

if v.ModifiedDate != nil {
query = query.Where("modified_date = ?", *v.ModifiedDate)
} else {
query = query.Where("modified_date IS NULL")
}

if v.WithdrawnDate != nil {
query = query.Where("withdrawn_date = ?", *v.WithdrawnDate)
} else {
query = query.Where("withdrawn_date IS NULL")
}

if err := query.First(v).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
if err := s.db.Create(v).Error; err != nil {
return err
}
Expand Down
34 changes: 34 additions & 0 deletions grype/db/v6/vulnerability_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,40 @@ func TestVulnerabilityStore_AddVulnerabilities(t *testing.T) {
assert.Nil(t, result1.Provider) // since we're not preloading any fields on the fetch
}

func TestVulnerabilityStore_NoDuplicateVulnerabilities(t *testing.T) {
db := setupTestStore(t).db
bw := newBlobStore(db)
s := newVulnerabilityStore(db, bw)

vuln := VulnerabilityHandle{
Name: "CVE-1234-5678",
BlobValue: &VulnerabilityBlob{
ID: "CVE-1234-5678",
},
Provider: &Provider{
ID: "provider!",
},
}

err := s.AddVulnerabilities(&vuln)
require.NoError(t, err)

err = s.AddVulnerabilities(&vuln)
require.NoError(t, err)

var results []VulnerabilityHandle
err = db.Where("name = ?", "CVE-1234-5678").Preload("Provider").Find(&results).Error
require.NoError(t, err)
require.Len(t, results, 1, "expected exactly one vulnerability handle to be added")

result := results[0]
assert.NotEmpty(t, result.ProviderID)
assert.NotEmpty(t, result.BlobID)
if d := cmp.Diff(vuln, result, cmpopts.IgnoreFields(VulnerabilityHandle{}, "BlobValue")); d != "" {
t.Errorf("unexpected result (-want +got):\n%s", d)
}
}

func TestVulnerabilityStore_AddVulnerabilities_missingModifiedDate(t *testing.T) {
db := setupTestStore(t).db
bw := newBlobStore(db)
Expand Down

0 comments on commit d054e68

Please sign in to comment.