Skip to content

Commit 0bea5a0

Browse files
jeremylenzclaude
andauthored
Add UI tests for Edit business risk, Edit status, and Filter by OS version (#20743)
* feat: Add UI tests for CVE business risk and status editing Add end-to-end tests for editing CVE business risk and status fields in the Insights Vulnerability UI. Tests added: - test_bulk_edit_business_risk_and_status - Bulk editing for multiple CVEs - test_edit_from_cve_details_page - Editing from CVE details page - test_filter_by_os_version - Filtering vulnerabilities by OS version Each test: - Provisions RHEL 10 host with IoP configured - Downgrades glibc to create CVE-2025-8058 - Verifies edit functionality via Airgun entity methods - Confirms changes persist in the vulnerabilities table Depends on: SatelliteQE/airgun#2303 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * refactor: Use vulnerable_rhel_host fixture and centralize constants Addresses code review feedback by: - Creating vulnerable_rhel_host fixture (matches pattern from PR #20733) - Centralizing CVE_ID as module-level constant - Removing duplicate client setup code from each test - Updating all test signatures to use the fixture Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * refactor: Address PR review comments for vulnerability edit tests - Move CVE and RPM constants to robottelo/constants/__init__.py for reusability - Change rhel_ver_list([10]) to rhel_ver_match('10') for exact version matching - Remove CaseImportance metadata (not used for test selection) - Convert double quotes to single quotes for consistency with project style Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * refactor: Replace glibc with mariadb for vulnerability testing glibc downgrade is not supported on RHEL10. Switch to mariadb package which creates 3 deterministic CVEs (CVE-2023-52969, CVE-2023-52970, CVE-2023-52971). This addresses PR review feedback to make bulk edit tests deterministic rather than relying on finding random CVEs. Also adds insights-client call to ensure vulnerabilities are reported to Satellite. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * refactor: Simplify to use single dnf install command dnf install with full NEVRA handles install/upgrade/downgrade automatically, so no need for separate install + downgrade commands. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: Add CaseImportance High marker to vulnerability tests This marker is required at the module level for CI checks to pass. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: Restore assert any pattern for vulnerability checks During the rebase, the assertion pattern was incorrectly changed from using `assert any()` to direct index access with `[0]`. This reverts to the more robust `assert any()` pattern while keeping the constant reference. The `assert any()` pattern is preferred because: - It's more resilient when the CVE isn't the first in the list - It handles empty lists gracefully - It better expresses the intent to find the CVE anywhere in the list Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent e395208 commit 0bea5a0

File tree

2 files changed

+278
-11
lines changed

2 files changed

+278
-11
lines changed

robottelo/constants/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,11 @@
976976
REAL_RHEL8_ERRATA_CVES = ['CVE-2021-27023', 'CVE-2021-27025']
977977
REAL_RHSCLIENT_ERRATA = 'RHSA-2023:5982' # for RH Satellite Client 8
978978
REAL_RHEL9_ERRATA_ID = 'RHBA-2022:7243'
979+
# Vulnerability testing constants for RHEL 10
980+
RHEL10_VULNERABLE_MARIADB_RPM = 'mariadb-3:10.11.11-1.el10.x86_64'
981+
RHEL10_VULNERABLE_MARIADB_CVES = ['CVE-2023-52969', 'CVE-2023-52970', 'CVE-2023-52971']
982+
# Use the first CVE as the primary one for single-CVE tests
983+
RHEL10_VULNERABILITY_CVE_ID = RHEL10_VULNERABLE_MARIADB_CVES[0]
979984
FAKE_1_YUM_REPOS_COUNT = 32
980985
FAKE_3_YUM_REPOS_COUNT = 78
981986
FAKE_9_YUM_SECURITY_ERRATUM = [

tests/foreman/ui/test_rhcloud_insights_vulnerability.py

Lines changed: 273 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,25 @@
1717
from robottelo import constants
1818

1919

20+
@pytest.fixture
21+
def vulnerable_rhel_host(rhel_insights_vm):
22+
"""Fixture to prepare a RHEL host with a vulnerable package"""
23+
client = rhel_insights_vm
24+
# Remove any static repos and update to the latest packages available from the Satellite
25+
assert (
26+
client.execute(
27+
'find /etc/yum.repos.d/ -type f | grep -vF redhat.repo | xargs -I \'{}\' rm \'{}\''
28+
).status
29+
== 0
30+
)
31+
assert client.execute('dnf -y update').status == 0
32+
# Install vulnerable mariadb version (dnf install with NEVRA handles install/downgrade)
33+
assert client.execute(f'dnf install -y {constants.RHEL10_VULNERABLE_MARIADB_RPM}').status == 0
34+
# Run insights-client to report the vulnerabilities to Satellite
35+
assert client.execute('insights-client').status == 0
36+
return client
37+
38+
2039
@pytest.fixture(scope='module')
2140
def setup_content_for_iop(module_target_sat_insights, rhcloud_manifest_org):
2241
"""Fixture to sync RHEL content repos"""
@@ -51,7 +70,7 @@ def setup_content_for_iop(module_target_sat_insights, rhcloud_manifest_org):
5170

5271
@pytest.mark.e2e
5372
@pytest.mark.no_containers
54-
@pytest.mark.rhel_ver_list([10])
73+
@pytest.mark.rhel_ver_match('10')
5574
@pytest.mark.parametrize('module_target_sat_insights', [False], ids=['local'], indirect=True)
5675
def test_rhcloud_insights_vulnerabilities_e2e(
5776
rhel_insights_vm,
@@ -78,24 +97,23 @@ def test_rhcloud_insights_vulnerabilities_e2e(
7897
7998
:verifies: SAT-30762
8099
"""
81-
CVE_ID = 'CVE-2025-8058'
82-
GLIBC_RPM = 'glibc-2.39-43.el10_0.x86_64'
83-
84100
satellite = module_target_sat_insights
85101
client = rhel_insights_vm
86102
hostname = client.hostname
87103

88104
# Remove any static repos and update to the latest packages available from the Satellite
89105
assert (
90106
client.execute(
91-
"find /etc/yum.repos.d/ -type f | grep -vF redhat.repo | xargs -I '{}' rm '{}'"
107+
'find /etc/yum.repos.d/ -type f | grep -vF redhat.repo | xargs -I \'{}\' rm \'{}\''
92108
).status
93109
== 0
94110
)
95111
assert client.execute('dnf -y update').status == 0
96112

97-
# Downgrade to vulnerable glibc version
98-
assert client.execute(f'dnf downgrade -y {GLIBC_RPM}').status == 0
113+
# Install vulnerable mariadb version (dnf install with NEVRA handles install/downgrade)
114+
assert client.execute(f'dnf install -y {constants.RHEL10_VULNERABLE_MARIADB_RPM}').status == 0
115+
# Run insights-client to report the vulnerabilities to Satellite
116+
assert client.execute('insights-client').status == 0
99117

100118
with satellite.ui_session() as session:
101119
session.organization.select(org_name=rhcloud_manifest_org.name)
@@ -104,14 +122,258 @@ def test_rhcloud_insights_vulnerabilities_e2e(
104122
assert status['Red Hat Lightspeed']['Status'] == 'Reporting'
105123

106124
vulnerabilities = session.host_new.get_vulnerabilities(hostname)
107-
assert any(vuln.get('CVE ID') == CVE_ID for vuln in vulnerabilities)
125+
assert any(
126+
vuln.get('CVE ID') == constants.RHEL10_VULNERABILITY_CVE_ID for vuln in vulnerabilities
127+
)
108128

109129
# Validate the affected Host from the Vulnerability details and list pages
110-
affected_host = session.cloudvulnerability.get_affected_hosts_by_cve(CVE_ID)
130+
affected_host = session.cloudvulnerability.get_affected_hosts_by_cve(
131+
constants.RHEL10_VULNERABILITY_CVE_ID
132+
)
111133
assert affected_host[0]['Name'] == hostname
112134

113135
# Validate the Host's Vulnerabilities tab
114136
vulnerabilities = session.cloudvulnerability.validate_cve_to_host_details_flow(
115-
CVE_ID, hostname
137+
constants.RHEL10_VULNERABILITY_CVE_ID, hostname
138+
)
139+
assert any(
140+
vuln.get('CVE ID') == constants.RHEL10_VULNERABILITY_CVE_ID for vuln in vulnerabilities
141+
)
142+
143+
144+
@pytest.mark.e2e
145+
@pytest.mark.no_containers
146+
@pytest.mark.rhel_ver_match('10')
147+
@pytest.mark.parametrize('module_target_sat_insights', [False], ids=['local'], indirect=True)
148+
def test_edit_business_risk_and_status_individual(
149+
vulnerable_rhel_host,
150+
rhcloud_manifest_org,
151+
module_target_sat_insights,
152+
setup_content_for_iop,
153+
):
154+
"""Test editing business risk and status for individual CVEs from the table
155+
156+
:id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
157+
158+
:steps:
159+
1. Create a CVE by downgrading a vulnerable package
160+
2. Navigate to Vulnerabilities page
161+
3. Edit business risk for the CVE via row kebab menu
162+
4. Verify business risk updated in table
163+
5. Edit status for the CVE via row kebab menu
164+
6. Verify status updated in table
165+
166+
:expectedresults:
167+
1. Business risk can be edited and persists in the table
168+
2. Status can be edited and persists in the table
169+
"""
170+
satellite = module_target_sat_insights
171+
172+
with satellite.ui_session() as session:
173+
session.organization.select(org_name=rhcloud_manifest_org.name)
174+
175+
# Edit business risk via row kebab menu
176+
session.cloudvulnerability.edit_business_risk(
177+
constants.RHEL10_VULNERABILITY_CVE_ID, 'High', 'Test justification'
178+
)
179+
180+
# Verify business risk in table
181+
vulns = session.cloudvulnerability.read()
182+
cve_row = [row for row in vulns if row['CVE ID'] == constants.RHEL10_VULNERABILITY_CVE_ID]
183+
assert len(cve_row) == 1, (
184+
f'Expected 1 CVE with ID {constants.RHEL10_VULNERABILITY_CVE_ID}, found {len(cve_row)}'
185+
)
186+
cve_row = cve_row[0]
187+
assert cve_row['Business risk'] == 'High'
188+
189+
# Edit status
190+
session.cloudvulnerability.edit_status(
191+
constants.RHEL10_VULNERABILITY_CVE_ID, 'In review', 'Testing status edit'
192+
)
193+
194+
# Verify status in table
195+
vulns = session.cloudvulnerability.read()
196+
cve_row = [row for row in vulns if row['CVE ID'] == constants.RHEL10_VULNERABILITY_CVE_ID]
197+
assert len(cve_row) == 1, (
198+
f'Expected 1 CVE with ID {constants.RHEL10_VULNERABILITY_CVE_ID}, found {len(cve_row)}'
199+
)
200+
cve_row = cve_row[0]
201+
assert cve_row['Status'] == 'In review'
202+
203+
204+
@pytest.mark.e2e
205+
@pytest.mark.no_containers
206+
@pytest.mark.rhel_ver_match('10')
207+
@pytest.mark.parametrize('module_target_sat_insights', [False], ids=['local'], indirect=True)
208+
def test_bulk_edit_business_risk_and_status(
209+
vulnerable_rhel_host,
210+
rhcloud_manifest_org,
211+
module_target_sat_insights,
212+
setup_content_for_iop,
213+
):
214+
"""Test bulk editing business risk and status for multiple CVEs
215+
216+
:id: 898904b5-fb95-43f9-8c33-cd107f00c206
217+
218+
:steps:
219+
1. Create multiple CVEs by downgrading vulnerable mariadb package
220+
2. Navigate to Vulnerabilities page
221+
3. Select multiple CVEs (2 of the 3 CVEs created by mariadb)
222+
4. Use bulk actions menu to edit business risk
223+
5. Verify business risk updated for all selected CVEs
224+
6. Use bulk actions menu to edit status
225+
7. Verify status updated for all selected CVEs
226+
227+
:expectedresults:
228+
1. Multiple CVEs can be selected via checkboxes
229+
2. Bulk business risk edit applies to all selected CVEs
230+
3. Bulk status edit applies to all selected CVEs
231+
"""
232+
satellite = module_target_sat_insights
233+
234+
with satellite.ui_session() as session:
235+
session.organization.select(org_name=rhcloud_manifest_org.name)
236+
237+
# Use the first 2 CVEs from the mariadb package for deterministic bulk edit testing
238+
cve_ids = constants.RHEL10_VULNERABLE_MARIADB_CVES[:2]
239+
240+
# Bulk edit business risk
241+
session.cloudvulnerability.bulk_edit_business_risk(cve_ids, 'Critical', 'Bulk test')
242+
243+
# Verify business risk updated for all selected CVEs
244+
vulns_after = session.cloudvulnerability.read()
245+
for cve_id in cve_ids:
246+
cve_row = [row for row in vulns_after if row['CVE ID'] == cve_id]
247+
assert len(cve_row) == 1, f'Expected 1 CVE with ID {cve_id}, found {len(cve_row)}'
248+
assert cve_row[0]['Business risk'] == 'Critical', (
249+
f'Business risk not updated for {cve_id}'
250+
)
251+
252+
# Bulk edit status
253+
session.cloudvulnerability.bulk_edit_status(cve_ids, 'Resolved', 'Testing bulk status')
254+
255+
# Verify status updated for all selected CVEs
256+
vulns_after = session.cloudvulnerability.read()
257+
for cve_id in cve_ids:
258+
cve_row = [row for row in vulns_after if row['CVE ID'] == cve_id]
259+
assert len(cve_row) == 1, f'Expected 1 CVE with ID {cve_id}, found {len(cve_row)}'
260+
assert cve_row[0]['Status'] == 'Resolved', f'Status not updated for {cve_id}'
261+
262+
263+
@pytest.mark.e2e
264+
@pytest.mark.no_containers
265+
@pytest.mark.rhel_ver_match('10')
266+
@pytest.mark.parametrize('module_target_sat_insights', [False], ids=['local'], indirect=True)
267+
def test_edit_from_cve_details_page(
268+
vulnerable_rhel_host,
269+
rhcloud_manifest_org,
270+
module_target_sat_insights,
271+
setup_content_for_iop,
272+
):
273+
"""Test editing business risk and status from the CVE details page
274+
275+
:id: 71e2f929-6536-439d-a734-10fc8a73967b
276+
277+
:steps:
278+
1. Create a CVE by downgrading a vulnerable package
279+
2. Navigate to CVE details page
280+
3. Edit business risk via Actions dropdown
281+
4. Verify business risk updated in main vulnerabilities table
282+
5. Navigate to CVE details page again
283+
6. Edit status via Actions dropdown
284+
7. Verify status updated in main vulnerabilities table
285+
286+
:expectedresults:
287+
1. Business risk can be edited from CVE details page Actions dropdown
288+
2. Status can be edited from CVE details page Actions dropdown
289+
3. Changes persist and are visible in main vulnerabilities table
290+
"""
291+
satellite = module_target_sat_insights
292+
293+
with satellite.ui_session() as session:
294+
session.organization.select(org_name=rhcloud_manifest_org.name)
295+
296+
# Edit business risk from CVE details page
297+
session.cloudvulnerability.edit_business_risk_from_details(
298+
constants.RHEL10_VULNERABILITY_CVE_ID, 'Low', 'Low priority via details page'
299+
)
300+
301+
# Verify business risk in main table
302+
vulns = session.cloudvulnerability.read()
303+
cve_row = [row for row in vulns if row['CVE ID'] == constants.RHEL10_VULNERABILITY_CVE_ID]
304+
assert len(cve_row) == 1, (
305+
f'Expected 1 CVE with ID {constants.RHEL10_VULNERABILITY_CVE_ID}, found {len(cve_row)}'
306+
)
307+
assert cve_row[0]['Business risk'] == 'Low'
308+
309+
# Edit status from CVE details page
310+
session.cloudvulnerability.edit_status_from_details(
311+
constants.RHEL10_VULNERABILITY_CVE_ID,
312+
'No action - risk accepted',
313+
'Risk accepted via details page',
314+
)
315+
316+
# Verify status in main table
317+
vulns = session.cloudvulnerability.read()
318+
cve_row = [row for row in vulns if row['CVE ID'] == constants.RHEL10_VULNERABILITY_CVE_ID]
319+
assert len(cve_row) == 1, (
320+
f'Expected 1 CVE with ID {constants.RHEL10_VULNERABILITY_CVE_ID}, found {len(cve_row)}'
321+
)
322+
assert cve_row[0]['Status'] == 'No action - risk accepted'
323+
324+
325+
@pytest.mark.e2e
326+
@pytest.mark.no_containers
327+
@pytest.mark.rhel_ver_match('10')
328+
@pytest.mark.parametrize('module_target_sat_insights', [False], ids=['local'], indirect=True)
329+
def test_filter_by_os_version(
330+
vulnerable_rhel_host,
331+
rhcloud_manifest_org,
332+
module_target_sat_insights,
333+
setup_content_for_iop,
334+
):
335+
"""Test filtering vulnerabilities by OS version
336+
337+
:id: 2790b179-2d98-4a7a-af59-f020bebd4413
338+
339+
:steps:
340+
1. Create a CVE by downgrading a vulnerable package
341+
2. Navigate to Vulnerabilities page
342+
3. Apply OS filter for RHEL 10
343+
4. Verify filtered results are displayed
344+
5. Verify CVE appears in filtered results
345+
346+
:expectedresults:
347+
1. OS filter can be applied successfully
348+
2. Table displays only CVEs that apply to the selected OS version
349+
3. The created CVE appears in the filtered results
350+
"""
351+
satellite = module_target_sat_insights
352+
353+
with satellite.ui_session() as session:
354+
session.organization.select(org_name=rhcloud_manifest_org.name)
355+
356+
# Read vulnerabilities before filtering
357+
vulns_before = session.cloudvulnerability.read()
358+
assert vulns_before, 'No vulnerabilities found before filtering'
359+
360+
# Apply OS filter for RHEL 10
361+
session.cloudvulnerability.filter_by_os(['RHEL 10'])
362+
363+
# Read vulnerabilities after filtering
364+
vulns_after = session.cloudvulnerability.read()
365+
assert vulns_after, 'No results after filtering - filter may have failed'
366+
367+
# Verify we got filtered results (should be same or fewer than before)
368+
assert len(vulns_after) <= len(vulns_before), (
369+
f'Filter should reduce or maintain results: '
370+
f'before={len(vulns_before)}, after={len(vulns_after)}'
371+
)
372+
373+
# Verify our CVE appears in the filtered results
374+
cve_found = any(
375+
row['CVE ID'] == constants.RHEL10_VULNERABILITY_CVE_ID for row in vulns_after
376+
)
377+
assert cve_found, (
378+
f'CVE {constants.RHEL10_VULNERABILITY_CVE_ID} should appear in RHEL 10 filtered results'
116379
)
117-
assert any(vuln.get('CVE ID') == CVE_ID for vuln in vulnerabilities)

0 commit comments

Comments
 (0)