Skip to content

Conversation

willmurphyscode
Copy link
Contributor

@willmurphyscode willmurphyscode commented Sep 12, 2025

This is a better approach for #2745

Comment on lines 77 to 85
// Step 3: Also look for unaffected packages using source/binary RPM relationships
relatedUnaffectedResults := findRelatedUnaffectedPackages(provider, searchPkg)
// Merge the related results into the main unaffected results
for key, results := range relatedUnaffectedResults {
unaffectedResults[key] = append(unaffectedResults[key], results...)
}

// Step 4: Update RHEL disclosures with AlmaLinux-specific fix information
updatedDisclosures := updateDisclosuresWithAlmaLinuxFixes(disclosures, unaffectedResults, pkgVersion)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect (but have not proven this out) that steps 3 and 4 could leverage the result merge and filter functions.

return "does not match distro(s): " + strings.Join(distroStrs, ", ")
}

type ExactDistroCriteria struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I instead of adding a new type, would it make the change slightly simpler to add a bool to DistroCriteria { exact bool ... and add a bool to the handleDistro function?

}
}

func (b *searchQueryBuilder) handleExactDistro(c *search.ExactDistroCriteria) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essentially the same comment as below: would it be simpler to just add a bool to handleDistro?

pkgVersion := version.New(searchPkg.Version, pkg.VersionFormat(searchPkg))

// Step 1: Find RHEL disclosures for the package
disclosures, err := provider.FindResults(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should additionally check for disclosures on the src rpm.

Previously, grype treated almalinux as a direct clone of RHEL. Now, in
cases where almalinux has a more specific fix version than RHEL does,
use the AlmaLinux advisory to remove the match.

Signed-off-by: Will Murphy <[email protected]>
Signed-off-by: Will Murphy <[email protected]>
Just set a flag in the existing distro criteria struct instead of making
a whole new type.

Signed-off-by: Will Murphy <[email protected]>
Signed-off-by: Will Murphy <[email protected]>
Signed-off-by: Will Murphy <[email protected]>
Signed-off-by: Will Murphy <[email protected]>
The regular RPM matcher doesn't do this either, and there's a very long
comment in it about why.

Signed-off-by: Will Murphy <[email protected]>
@willmurphyscode willmurphyscode marked this pull request as ready for review October 8, 2025 20:03
)

// OnlyAffectedVulnerabilities returns a criteria object that filters out unaffected vulnerability records
func OnlyAffectedVulnerabilities() vulnerability.Criteria {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this overlaps (and might do the same thing)

Comment on lines 195 to 202
func shouldCompletelyFilter(vuln vulnerability.Vulnerability, pkgVersion *version.Version) bool {
if !isVersionUnaffected(pkgVersion, vuln.Constraint, vuln.ID) {
return false
}

fixVersion := extractFixVersionFromConstraint(vuln.Constraint)
return shouldFilterVulnerability(vuln.Constraint, fixVersion, pkgVersion)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having shouldFilterVulnerability vs shouldCompletelyFilter is confusing -- maybe merge them into a single function?

return ""
}

constraintStr := constraint.String()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you use constraint.Value() you can remove the cleanVersionString function (has no parentheticals)

// Handle common constraint patterns
// ">= version" → "version"
if strings.HasPrefix(constraintStr, ">= ") {
version := strings.TrimPrefix(constraintStr, ">= ")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: version clashes with import name

log.WithFields("almaLinuxFixesCount", len(almaLinuxFixes), "unaffectedResultsCount", len(unaffectedResults)).Debug("built AlmaLinux fixes map")

if len(almaLinuxFixes) == 0 {
log.Debug("no AlmaLinux fixes found, returning original disclosures")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this log line provides little value after initial development

Comment on lines 166 to 192
func identifyVulnerabilitiesToRemove(unaffectedResults result.Set, pkgVersion *version.Version) result.Set {
toRemove := make(result.Set)

for _, unaffectedResultList := range unaffectedResults {
for _, unaffectedResult := range unaffectedResultList {
for _, vuln := range unaffectedResult.Vulnerabilities {
if shouldCompletelyFilter(vuln, pkgVersion) {
// Create a result entry to mark this vulnerability for removal
toRemove[vuln.ID] = []result.Result{{
ID: vuln.ID,
Vulnerabilities: []vulnerability.Vulnerability{vuln},
}}

// Also mark related vulnerabilities (aliases) for removal
for _, related := range vuln.RelatedVulnerabilities {
toRemove[related.ID] = []result.Result{{
ID: related.ID,
Vulnerabilities: []vulnerability.Vulnerability{{Reference: related}},
}}
}
}
}
}
}

return toRemove
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like we are building a set to use later for removal from another set. Could this be better accomplished with the result.Set.Filter() which takes arbitrary criteria functions? That would prevent needing to craft an intermediate set.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I see, you're leveraging the alias processing baked into Remove(). I wonder if that could be extracted and used as a filter function? (consider this non-blocking; I'm trying to find ways to make this easier to read and reason about)

}

// updateRemainingWithAlmaLinuxFixes updates remaining vulnerabilities with AlmaLinux fix information
func updateRemainingWithAlmaLinuxFixes(disclosures result.Set, unaffectedResults result.Set) result.Set {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this really looks like it should be a result.Set.Merge() with a custom function to control behavior

Signed-off-by: Will Murphy <[email protected]>
Signed-off-by: Will Murphy <[email protected]>
Signed-off-by: Will Murphy <[email protected]>
There are fewer gaps in ALSAs than previously thought.

Signed-off-by: Will Murphy <[email protected]>
This data will instead be added to vunnel, so that cleaning it up
doesn't require a code release.

Signed-off-by: Will Murphy <[email protected]>
// For example, if the current set has CVE-2006-20001 and the incoming set has ALSA-2023:0852
// with alias CVE-2006-20001, the updateFunc will be called to update the CVE entry with data
// from the ALSA entry.
func (s Set) UpdateByIdentity(incoming Set, updateFunc func(existing *Result, incoming Result)) Set {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from our conversation, I feel like this (one day) could be a little more generic. This is a pretty specific functionality on a generic set implementation. We don't have to block on that now since this is in internal.

From what we chatted about the set intersectoin and getIdentity logic out and passed in as a "key function", thus this really changes from UpdateByIdentity to just Update. Odds are something like this can be done to Merge. TBD on removing of elements, but I feel that could be a separate call to Filter or Remove before hand.

Comment on lines +241 to +243
if strings.HasPrefix(constraintStr, ">= ") {
return strings.TrimPrefix(constraintStr, ">= ")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if strings.HasPrefix(constraintStr, ">= ") {
return strings.TrimPrefix(constraintStr, ">= ")
}
if strings.HasPrefix(constraintStr, ">=") {
return strings.TrimSpace(strings.TrimPrefix(constraintStr, ">="))
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

},
},
},
mergeFuncs: []func(existing, incoming []Result) []Result{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test could probably be deleted

Comment on lines 1069 to 1075
got := filtered.UpdateByIdentity(tt.unaffectedResults, func(existing *Result, incoming Result) {
for i := range existing.Vulnerabilities {
for _, incomingVuln := range incoming.Vulnerabilities {
existing.Vulnerabilities[i].Fix = incomingVuln.Fix
existing.Vulnerabilities[i].Advisories = incomingVuln.Advisories
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels kinda an odd/complicated way to test this

Comment on lines 1450 to 1456
got := tt.base.UpdateByIdentity(tt.incoming, func(existing *Result, incoming Result) {
// Update fix information from incoming
for i := range existing.Vulnerabilities {
for _, incomingVuln := range incoming.Vulnerabilities {
existing.Vulnerabilities[i].Fix = incomingVuln.Fix
existing.Vulnerabilities[i].Advisories = incomingVuln.Advisories
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicate test?


for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filtered := tt.disclosures.Remove(tt.unaffectedResults)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filtered := tt.disclosures.Remove(tt.unaffected)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably the r=wrong subject under test

Signed-off-by: Will Murphy <[email protected]>
This lets the caller pass in a predicate rather than fixing the
predicate used to determine what should be updated.

Signed-off-by: Will Murphy <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants