fix: match Red Hat OS distro in OSV vulnerability analysis#6325
fix: match Red Hat OS distro in OSV vulnerability analysis#6325mvanhorn wants to merge 1 commit into
Conversation
…yTrack#6156) Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 9 |
| Duplication | 1 |
🟢 Coverage 96.97% diff coverage · +0.09% coverage variation
Metric Results Coverage variation ✅ +0.09% coverage variation (-1.00%) Diff coverage ✅ 96.97% diff coverage (70.00%) Coverage variation details
Coverable lines Covered lines Coverage Common ancestor commit (1b32636) 41682 36066 86.53% Head commit (866b619) 41715 (+33) 36133 (+67) 86.62% (+0.09%) Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch:
<coverage of head commit> - <coverage of common ancestor commit>Diff coverage details
Coverable lines Covered lines Diff coverage Pull request (#6325) 33 32 96.97% Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified:
<covered lines added or modified>/<coverable lines added or modified> * 100%
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
nscuro
left a comment
There was a problem hiding this comment.
Thanks! A few changes to extend the coverage and accuracy.
| // authoritative OS scope and takes precedence over a product version that | ||
| // happens to differ from the RHEL major (OpenShift 4.18 runs on RHEL 8). | ||
| private static final Pattern EL_TARGET_PATTERN = | ||
| Pattern.compile(".*\\bel(\\d+)\\b.*", Pattern.CASE_INSENSITIVE); |
There was a problem hiding this comment.
To also handle minor versions:
.*\\bel(\\d+)(?:\\.(\\d+))?\\b.*| final String value = qualifierValue.toLowerCase().startsWith("redhat-") | ||
| ? qualifierValue.substring("redhat-".length()) | ||
| : qualifierValue; |
There was a problem hiding this comment.
Some SBOM generators like Syft use rhel- instead of redhat- for the distro qualifier. We should handle both here.
| * carry a RHEL major version, which is the smallest reliably comparable scope: | ||
| * an advisory for RHEL 8 must not be matched against a RHEL 9 component. | ||
| * | ||
| * @since 4.14.0 |
There was a problem hiding this comment.
| * @since 4.14.0 | |
| * @since 5.1.0 |
| private static final Pattern RHEL_PRODUCT_PATTERN = | ||
| Pattern.compile("^(?:rhel|rhel_aus|rhel_eus|rhel_els|rhel_tus|rhel_e4s|enterprise_linux):(\\d+)(?:[.:].*)?$", | ||
| Pattern.CASE_INSENSITIVE); | ||
|
|
There was a problem hiding this comment.
Based on the OSV data set, there are a few more product names we should capture:
| private static final Pattern RHEL_PRODUCT_PATTERN = | |
| Pattern.compile("^(?:rhel|rhel_aus|rhel_eus|rhel_els|rhel_tus|rhel_e4s|enterprise_linux):(\\d+)(?:[.:].*)?$", | |
| Pattern.CASE_INSENSITIVE); | |
| private static final Pattern RHEL_PRODUCT_PATTERN = Pattern.compile(""" | |
| ^(?:\ | |
| enterprise_linux\\w*\ | |
| |enterprise_ipa\ | |
| |rhel_application_server\ | |
| |rhel_aus\ | |
| |rhel_cluster\ | |
| |rhel_cluster_storage\ | |
| |rhel_developer_suite\ | |
| |rhel_e4s\ | |
| |rhel_els\ | |
| |rhel_eus\ | |
| |rhel_eus_long_life\ | |
| |rhel_extras\\w*\ | |
| |rhel_global_file_system\ | |
| |rhel_mission_critical\ | |
| |rhel_productivity\ | |
| |rhel_rhn_tools\ | |
| |rhel_sjis\ | |
| |rhel_stronghold\ | |
| |rhel_tus\ | |
| |rhel_virtualization\ | |
| ):(\\d+)(?:\\.(\\d+))?(?:[.:].*)?$\ | |
| """, Pattern.CASE_INSENSITIVE); |
That would also capture minor versions.
| * | ||
| * @since 4.14.0 | ||
| */ | ||
| public record RedhatDistribution(String majorVersion) implements OsDistribution { |
There was a problem hiding this comment.
We should also track the (optional) minor version. A large chunk of the OSV data provides Red Hat versions in the major.minor format. So if OSV claims a vuln to affect redhat-8.6, it shouldn't match against a component with redhat-8.7. But it should match a component with redhat-8 and vice-versa. matches would need to be updated accordingly.
| // The leading major version of a PURL "distro" qualifier value, e.g. "9" in | ||
| // "redhat-9.7" or "9.7". PURL qualifiers carry a bare RHEL version, not a CPE. | ||
| private static final Pattern PURL_VERSION_PATTERN = | ||
| Pattern.compile("^(\\d+)(?:\\..*)?$"); |
There was a problem hiding this comment.
To also handle minor versions:
| Pattern.compile("^(\\d+)(?:\\..*)?$"); | |
| Pattern.compile("^(\\d+)(?:\\.(\\d+))?(?:\\..*)?$"); |
Description
Adds Red Hat to the set of OS distributions consulted during OSV vulnerability matching, so Red Hat / UBI RPM components are only matched against advisories scoped to the same RHEL major version. This is the Red Hat equivalent of the Alpine/Debian/Ubuntu distro-aware matching introduced in #5783.
SBOMs from Red Hat / UBI9 images (e.g.
pkg:rpm/redhat/libsolv@0.7.24-3.el9?distro=redhat-9.7) currently produce false positives, because an OSV Red Hat advisory is compared against the component without respecting the Red Hat product-stream / RHEL scope. In #6156 the reporter's RHEL 9 component (0.7.24-3.el9) is matched against an unrelated RHEL 8 Satellite advisory (1:0.7.20-6.el8sat).OsDistributionalready gates version comparison inInternalVulnAnalyzer.matchesDistro, but only Alpine/Debian/Ubuntu were modeled, so Red Hat fell through and the false positives stood. As @nscuro noted on the issue, Red Hat needs the same treatment, and OSV encodes the Red Hat product stream in the ecosystem string via a:<CPE>suffix.Changes:
RedhatDistribution(new sealedOsDistributionrecord) modeling the RHEL major version, withmatchesthat only matches like RHEL majors.OsDistribution: addedRedhatDistributionto thepermitsclause and extendedof(PackageURL)to resolverpm/redhatPURLs carrying adistroqualifier.OsvEcosystems.toOsDistribution: added ared hat/redhatecosystem branch that parses the:<CPE>suffix.Addressed Issue
Fixes #6156
Additional Details
Red Hat OSV ecosystem CPEs do not all carry the RHEL major in the same field, so the parser handles them explicitly:
elNOS target is authoritative (e.g.Red Hat:openshift:4.18::el8-> RHEL 8, not OpenShift 4), since layered-product versions are not the RHEL major.rhel,rhel_aus,rhel_eus,rhel_els,rhel_tus,rhel_e4s,enterprise_linux).elNtarget (e.g.rhel_application_stack:2,rhel_atomic:7,satellite:6.16) return null rather than mis-scoping to a wrong RHEL major, which would create new false negatives.Verified against real OSV feed entries while iterating on the CPE parsing.
Testing:
mvn -pl support/os-distro-metadata test(115 tests, including 31 new inRedhatDistributionTest) andmvn -pl vuln-data-source/osv test -Dtest=OsvEcosystemsTest(40 tests) both pass. Tests cover the #6156 regression (RHEL 9 component not matched against a RHEL 8 stream advisory), same-major matches, base-RHEL and layered-product CPE forms, and PURL-qualifier resolution.AI was used for assistance.
Checklist
docs/adr/