Skip to content

RPM OS release suffix (el7/el8/el9) causes incorrect cross-OS vulnerability matching #2240

@dejanb

Description

@dejanb

Problem Summary

RPM packages with OS release-specific suffixes (e.g., el7eap, el8eap, el9eap) are incorrectly matched across different OS release lines because the version comparison function treats them as a linear version progression rather than distinct product lines.

Current Behavior

When querying for vulnerabilities affecting a PURL like:

pkg:rpm/redhat/eap7-bouncycastle@1.76.0-4.redhat_00001.1.el8eap?arch=noarch

The system incorrectly matches it against purl_status records that have version ranges targeting different OS releases:

Version Range Status Expected Match Actual Match
[el8eap, el8eap] fixed Yes Yes
(null, el9eap) affected No Yes

The el8eap purl incorrectly matches the range < el9eap because rpmver_cmp() compares the release suffixes as 8 < 9.

Root Cause

The rpmver_cmp() function correctly implements RPM version comparison per the RPM spec:

-- Splits version into segments: 1.76.0-4.redhat_00001.1.el8eap
-- becomes: [1, 76, 0, 4, redhat, 00001, 1, el, 8, eap]
a_segments := array(select (regexp_matches(a, '(\d+|[a-zA-Z]+|[~^])', 'g'))[1]);

When comparing el8eap vs el9eap:

  1. Both split into segments: [el, 8, eap] vs [el, 9, eap]
  2. el = el (equal)
  3. 8 < 9 (numeric comparison)
  4. Result: el8eap < el9eap

This is technically correct per RPM ordering, but semantically wrong for vulnerability correlation:

  • el7, el8, el9 represent different RHEL major versions
  • They are parallel product lines, not version progressions
  • A vulnerability fixed in el9eap packages does NOT mean el8eap packages are affected

Affected Endpoints

Endpoint Method Impact
/api/v2/vulnerability/analyze POST Returns incorrect vulnerabilities for el8/el9 RPMs
/api/v3/vulnerability/analyze POST Same as above, plus incorrect remediations
/api/v3/purl/recommend POST May recommend wrong versions
/api/v2/purl/{key} GET Shows incorrect vulnerability status
/api/v2/sbom/{id}/advisory GET Lists wrong advisories for SBOM packages
/api/v2/vulnerability/{id} GET Shows incorrect affected packages

Reproduction Steps

  1. Ingest a Red Hat CSAF advisory that contains products for multiple RHEL versions:

    curl -X POST http://localhost:8080/api/v2/advisory \
      -H "Content-Type: application/json" \
      -d @etc/test-data/csaf/cve-2023-33201.json
  2. Query for an el8eap package:

    curl -X POST http://localhost:8080/api/v3/vulnerability/analyze \
      -H "Content-Type: application/json" \
      -d '{"purls": ["pkg:rpm/redhat/eap7-bouncycastle@1.76.0-4.redhat_00001.1.el8eap?arch=noarch"]}'
  3. Observe that the response includes vulnerability status and remediations from el9eap products.

Test Cases

Tests documenting this issue:

Branch: test/rpm-os-release-suffix-matching

Test file: modules/ingestor/tests/version/rpmver.rs

Test Name Purpose
test_rpmver_cmp_release_suffix_different_os Documents that el7 < el8 < el9 per RPM spec
test_rpmver_version_matches_cross_os_release_bug Documents the bug: el8eap matches range < el9eap
test_rpmver_same_version_different_os_releases Shows all OS suffixes are treated as linear progression
test_rpmver_version_matches_exact_with_os_suffix Verifies exact matching works correctly
test_rpmver_version_matches_same_os_release Verifies matching within same OS release works

Run the tests with:

cargo test -p trustify-module-ingestor --test ingestor test_rpmver

Potential Solutions

Option 1: Enhanced Version Range Matching

Modify rpmver_version_matches to detect and handle OS release suffixes:

CREATE FUNCTION extract_rpm_os_release(version text) RETURNS text AS $$
BEGIN
    RETURN (regexp_match(version, '\.el(\d+)'))[1];
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- In rpmver_version_matches, add check:
IF extract_rpm_os_release(version_p) != extract_rpm_os_release(range_p.high_version) THEN
    RETURN false;
END IF;

Pros: Targeted fix, minimal changes
Cons: RPM-specific, may miss edge cases

Option 2: Normalize OS Release in Base PURL

Extract OS release into PURL namespace during ingestion.

Option 3: Add OS Release Qualifier

Store OS release as a qualifier and include it in matching.

Option 4: Separate Version and Release Fields

Store RPM version and release separately in the database.

Related

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions