-
Notifications
You must be signed in to change notification settings - Fork 696
Feat alma unaffected #2939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feat alma unaffected #2939
Conversation
eb0cb13
to
c24673b
Compare
grype/matcher/rpm/almalinux.go
Outdated
// 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grype/search/distro.go
Outdated
return "does not match distro(s): " + strings.Join(distroStrs, ", ") | ||
} | ||
|
||
type ExactDistroCriteria struct { |
There was a problem hiding this comment.
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?
grype/db/v6/search_query.go
Outdated
} | ||
} | ||
|
||
func (b *searchQueryBuilder) handleExactDistro(c *search.ExactDistroCriteria) { |
There was a problem hiding this comment.
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
?
grype/matcher/rpm/almalinux.go
Outdated
pkgVersion := version.New(searchPkg.Version, pkg.VersionFormat(searchPkg)) | ||
|
||
// Step 1: Find RHEL disclosures for the package | ||
disclosures, err := provider.FindResults( |
There was a problem hiding this comment.
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.
Signed-off-by: Will Murphy <[email protected]>
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]>
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]>
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]>
Signed-off-by: Will Murphy <[email protected]>
cabf4b1
to
cb738cc
Compare
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]>
) | ||
|
||
// OnlyAffectedVulnerabilities returns a criteria object that filters out unaffected vulnerability records | ||
func OnlyAffectedVulnerabilities() vulnerability.Criteria { |
There was a problem hiding this comment.
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)
grype/matcher/rpm/almalinux.go
Outdated
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) | ||
} |
There was a problem hiding this comment.
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?
grype/matcher/rpm/almalinux.go
Outdated
return "" | ||
} | ||
|
||
constraintStr := constraint.String() |
There was a problem hiding this comment.
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)
grype/matcher/rpm/almalinux.go
Outdated
// Handle common constraint patterns | ||
// ">= version" → "version" | ||
if strings.HasPrefix(constraintStr, ">= ") { | ||
version := strings.TrimPrefix(constraintStr, ">= ") |
There was a problem hiding this comment.
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
grype/matcher/rpm/almalinux.go
Outdated
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") |
There was a problem hiding this comment.
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
grype/matcher/rpm/almalinux.go
Outdated
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 | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
grype/matcher/rpm/almalinux.go
Outdated
} | ||
|
||
// updateRemainingWithAlmaLinuxFixes updates remaining vulnerabilities with AlmaLinux fix information | ||
func updateRemainingWithAlmaLinuxFixes(disclosures result.Set, unaffectedResults result.Set) result.Set { |
There was a problem hiding this comment.
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]>
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]>
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 { |
There was a problem hiding this comment.
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.
if strings.HasPrefix(constraintStr, ">= ") { | ||
return strings.TrimPrefix(constraintStr, ">= ") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if strings.HasPrefix(constraintStr, ">= ") { | |
return strings.TrimPrefix(constraintStr, ">= ") | |
} | |
if strings.HasPrefix(constraintStr, ">=") { | |
return strings.TrimSpace(strings.TrimPrefix(constraintStr, ">=")) | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, we can use these https://github.com/anchore/grype/blob/main/grype/version/operator.go
}, | ||
}, | ||
}, | ||
mergeFuncs: []func(existing, incoming []Result) []Result{ |
There was a problem hiding this comment.
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
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 | ||
} | ||
} |
There was a problem hiding this comment.
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
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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplicate test?
grype/matcher/rpm/almalinux_test.go
Outdated
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
filtered := tt.disclosures.Remove(tt.unaffectedResults) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is already covered by https://github.com/anchore/grype/blob/v0.101.0/grype/matcher/internal/result/results_test.go#L118-L285 in the result package
grype/matcher/rpm/almalinux_test.go
Outdated
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
filtered := tt.disclosures.Remove(tt.unaffected) |
There was a problem hiding this comment.
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]>
Signed-off-by: Will Murphy <[email protected]>
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]>
This is a better approach for #2745