Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ab3f2c0
feat: use almalinux advisories to filter alma matches
willmurphyscode Sep 10, 2025
f42d716
feat: filter RPM matches on Alma by Alma advisories
willmurphyscode Sep 12, 2025
95e761d
debugsource and debuginfo never vulnerable on alma
willmurphyscode Sep 12, 2025
2fa5925
use result set instead of manual filtering
willmurphyscode Sep 12, 2025
9c1be94
update fixes to alma fixes used
willmurphyscode Sep 13, 2025
e9c4c17
add unaliased distro search capability
willmurphyscode Sep 17, 2025
49d16bf
ensure ALSAs can remove CVEs on AlmaLinux scans
willmurphyscode Sep 17, 2025
576a6ba
lint fixes
willmurphyscode Sep 17, 2025
e06bbe5
use result set merge and filter
willmurphyscode Sep 19, 2025
afb1421
simplify exact distro search
willmurphyscode Sep 20, 2025
c3993b0
wip
willmurphyscode Sep 22, 2025
b9424b8
wip
willmurphyscode Oct 7, 2025
92e23c6
always set alma advisory if available
willmurphyscode Oct 8, 2025
cb738cc
set advisories
willmurphyscode Oct 8, 2025
d41fa4e
stop adding epochs to src rpm versions in Alma matcher
willmurphyscode Oct 8, 2025
a7970ab
more alma unit tests
willmurphyscode Oct 13, 2025
6ccef84
additional alma test - module build number fp
willmurphyscode Oct 13, 2025
4a690e5
add final almalinux tests
willmurphyscode Oct 13, 2025
9034907
simplify filtering logic
willmurphyscode Oct 13, 2025
a499661
add UpdateByIdentity for result set
willmurphyscode Oct 13, 2025
85e21e4
simplify related names work
willmurphyscode Oct 14, 2025
10dd4e6
remove map of missing packages from ALSAs
willmurphyscode Oct 14, 2025
06c2481
clean up some redundant functions
willmurphyscode Oct 14, 2025
9d67c64
fix unit tests for result set
willmurphyscode Oct 15, 2025
621e7a8
remove redundant test
willmurphyscode Oct 15, 2025
f6b97d0
more test clean up in rhel disclosures
willmurphyscode Oct 15, 2025
672109c
replace UpdateByIdentity with Update
willmurphyscode Oct 16, 2025
44b54c9
also update constraint
willmurphyscode Oct 20, 2025
2398507
fix marking by exact indirect match
willmurphyscode Oct 21, 2025
95da2ce
suggestion from @wagoodman: time whitespace
willmurphyscode Oct 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions grype/db/v6/operating_system_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ type OSSpecifier struct {

// Channel is a string that represents a different feed for fix and vulnerability data (e.g. "eus" for RHEL)
Channel string

// DisableAliasing prevents OS aliasing when true (used for exact distro matching)
DisableAliasing bool
}

func (d *OSSpecifier) clean() {
Expand Down Expand Up @@ -201,8 +204,11 @@ func (s *operatingSystemStore) GetOperatingSystems(d OSSpecifier) ([]OperatingSy
// search for aliases for the given distro; we intentionally map some OSs to other OSs in terms of
// vulnerability (e.g. `centos` is an alias for `rhel`). If an alias is found always use that alias in
// searches (there will never be anything in the DB for aliased distros).
if err := s.applyOSAlias(&d); err != nil {
return nil, err
// Skip aliasing if explicitly disabled (used for exact distro matching).
if !d.DisableAliasing {
if err := s.applyOSAlias(&d); err != nil {
return nil, err
}
}

d.clean()
Expand Down
2 changes: 2 additions & 0 deletions grype/db/v6/search_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ func (b *searchQueryBuilder) handleDistro(c *search.DistroCriteria) {
RemainingVersion: d.RemainingVersion(),
LabelVersion: d.Codename,
Channel: channel,
DisableAliasing: c.Exact,
})
}
if foundChannels == 0 {
Expand All @@ -162,6 +163,7 @@ func (b *searchQueryBuilder) handleDistro(c *search.DistroCriteria) {
MinorVersion: d.MinorVersion(),
RemainingVersion: d.RemainingVersion(),
LabelVersion: d.Codename,
DisableAliasing: c.Exact,
})
}
}
Expand Down
60 changes: 60 additions & 0 deletions grype/db/v6/search_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,66 @@ func TestQueryBuilder_Build(t *testing.T) {
require.Len(t, remaining, 1)
}

func TestQueryBuilder_ExactDistroCriteria(t *testing.T) {
tests := []struct {
name string
criteria []vulnerability.Criteria
validate func(t *testing.T, query *searchQuery, remaining []vulnerability.Criteria)
}{
{
name: "exact distro criteria should be handled with DisableAliasing set",
criteria: []vulnerability.Criteria{
search.ByExactDistro(*distro.New(distro.AlmaLinux, "8", "")),
},
validate: func(t *testing.T, query *searchQuery, remaining []vulnerability.Criteria) {
require.Len(t, query.osSpecs, 1)
require.Equal(t, "almalinux", query.osSpecs[0].Name)
require.Equal(t, "8", query.osSpecs[0].MajorVersion)
require.True(t, query.osSpecs[0].DisableAliasing, "ExactDistroCriteria should set DisableAliasing=true")
require.Empty(t, remaining, "ExactDistroCriteria should be handled, not left in remaining")
},
},
{
name: "exact distro criteria should not be left in remaining criteria",
criteria: []vulnerability.Criteria{
search.ByPackageName("mariadb"),
search.ByExactDistro(*distro.New(distro.AlmaLinux, "8", "")),
search.ForUnaffected(),
},
validate: func(t *testing.T, query *searchQuery, remaining []vulnerability.Criteria) {
require.NotNil(t, query.pkgSpec)
require.Equal(t, "mariadb", query.pkgSpec.Name)
require.Len(t, query.osSpecs, 1)
require.Equal(t, "almalinux", query.osSpecs[0].Name)
require.True(t, query.osSpecs[0].DisableAliasing, "ExactDistroCriteria should set DisableAliasing=true")
require.True(t, query.unaffectedOnly)
require.Empty(t, remaining, "ExactDistroCriteria should be handled, not left in remaining")
},
},
{
name: "regular distro criteria should not set DisableAliasing",
criteria: []vulnerability.Criteria{
search.ByDistro(*distro.New(distro.AlmaLinux, "8", "")),
},
validate: func(t *testing.T, query *searchQuery, remaining []vulnerability.Criteria) {
require.Len(t, query.osSpecs, 1)
require.Equal(t, "almalinux", query.osSpecs[0].Name)
require.Equal(t, "8", query.osSpecs[0].MajorVersion)
require.False(t, query.osSpecs[0].DisableAliasing, "Regular DistroCriteria should keep DisableAliasing=false")
require.Empty(t, remaining)
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
query, remaining, err := newSearchQuery(test.criteria)
require.NoError(t, err)
test.validate(t, query, remaining)
})
}
}

func TestQueryBuilder_IntegrationWithRealCriteria(t *testing.T) {
// test the full flow that mimics parseCriteria behavior
criteria := []vulnerability.Criteria{
Expand Down
43 changes: 43 additions & 0 deletions grype/matcher/internal/result/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,49 @@ func (s Set) Intersection(other Set) Set {
return out
}

// IdentitiesOverlap returns true if two results share any common identifiers, where identity
// includes both the primary ID and any aliases (from RelatedVulnerabilities). This can be used
// as a shouldUpdate predicate for Update when matching results by ID or alias relationships.
func IdentitiesOverlap(existing Result, incoming Result) bool {
existingIdentity := getIdentity(existing.ID, []Result{existing})
incomingIdentity := getIdentity(incoming.ID, []Result{incoming})
return !strset.Intersection(existingIdentity, incomingIdentity).IsEmpty()
}

// Update applies an update function to each result in the set where shouldUpdate returns true
// for the existing-incoming result pair. The updateFunc can modify fields of the existing result
// in-place while preserving other fields. Returns a new Set with updated results.
//
// Example with identity-based matching:
//
// updated := base.Update(incoming, IdentitiesOverlap, func(existing *Result, incoming Result) {
// existing.Vulnerabilities[0].Fix = incoming.Vulnerabilities[0].Fix
// })
func (s Set) Update(incoming Set, shouldUpdate func(existing Result, incoming Result) bool, updateFunc func(existing *Result, incoming Result)) Set {
out := make(Set)

// Copy everything from base set
for id, results := range s {
out[id] = append([]Result(nil), results...)
}

// For each entry in base, check all incoming entries with shouldUpdate
for id, existingResults := range out {
for i := range existingResults {
for _, incomingResults := range incoming {
for _, incomingResult := range incomingResults {
if shouldUpdate(existingResults[i], incomingResult) {
updateFunc(&existingResults[i], incomingResult)
}
}
}
}
out[id] = existingResults
}

return out
}

func (s Set) Filter(criteria ...vulnerability.Criteria) Set {
out := Set{}
for id, results := range s {
Expand Down
Loading
Loading